Skip to content

Commit 167aa6c

Browse files
committed
inference: refine PartialStruct lattice tmerge
Be more aggressive about merging fields to greatly accelerate convergence, but also compute anyrefine more correctly as we do now elsewhere (since #42831, a121721) Move the tmeet algorithm, without changes, since it is a precise lattice operation, not a heuristic limit like tmerge. Close #43784
1 parent 4ca2759 commit 167aa6c

File tree

3 files changed

+95
-53
lines changed

3 files changed

+95
-53
lines changed

base/compiler/typelattice.jl

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,3 +469,45 @@ function stupdate1!(state::VarTable, change::StateUpdate)
469469
end
470470
return false
471471
end
472+
473+
# compute typeintersect over the extended inference lattice,
474+
# as precisely as we can,
475+
# where v is in the extended lattice, and t is a Type.
476+
function tmeet(@nospecialize(v), @nospecialize(t))
477+
if isa(v, Const)
478+
if !has_free_typevars(t) && !isa(v.val, t)
479+
return Bottom
480+
end
481+
return v
482+
elseif isa(v, PartialStruct)
483+
has_free_typevars(t) && return v
484+
widev = widenconst(v)
485+
if widev <: t
486+
return v
487+
end
488+
ti = typeintersect(widev, t)
489+
valid_as_lattice(ti) || return Bottom
490+
@assert widev <: Tuple
491+
new_fields = Vector{Any}(undef, length(v.fields))
492+
for i = 1:length(new_fields)
493+
vfi = v.fields[i]
494+
if isvarargtype(vfi)
495+
new_fields[i] = vfi
496+
else
497+
new_fields[i] = tmeet(vfi, widenconst(getfield_tfunc(t, Const(i))))
498+
if new_fields[i] === Bottom
499+
return Bottom
500+
end
501+
end
502+
end
503+
return tuple_tfunc(new_fields)
504+
elseif isa(v, Conditional)
505+
if !(Bool <: t)
506+
return Bottom
507+
end
508+
return v
509+
end
510+
ti = typeintersect(widenconst(v), t)
511+
valid_as_lattice(ti) || return Bottom
512+
return ti
513+
end

base/compiler/typelimits.jl

Lines changed: 30 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,7 @@ function tmerge(@nospecialize(typea), @nospecialize(typeb))
452452
aty = widenconst(typea)
453453
bty = widenconst(typeb)
454454
if aty === bty
455+
# must have egal here, since we do not create PartialStruct for non-concrete types
455456
typea_nfields = nfields_tfunc(typea)
456457
typeb_nfields = nfields_tfunc(typeb)
457458
isa(typea_nfields, Const) || return aty
@@ -460,18 +461,40 @@ function tmerge(@nospecialize(typea), @nospecialize(typeb))
460461
type_nfields === typeb_nfields.val::Int || return aty
461462
type_nfields == 0 && return aty
462463
fields = Vector{Any}(undef, type_nfields)
463-
anyconst = false
464+
anyrefine = false
464465
for i = 1:type_nfields
465466
ai = getfield_tfunc(typea, Const(i))
466467
bi = getfield_tfunc(typeb, Const(i))
467-
ity = tmerge(ai, bi)
468-
if ai === Union{} || bi === Union{}
469-
ity = widenconst(ity)
468+
ft = fieldtype(aty, i)
469+
if is_lattice_equal(ai, bi) || is_lattice_equal(ai, ft)
470+
# Since ai===bi, the given type has no restrictions on complexity.
471+
# and can be used to refine ft
472+
tyi = ai
473+
elseif is_lattice_equal(bi, ft)
474+
tyi = bi
475+
else
476+
# Otherwise choose between using the fieldtype or some other simple merged type.
477+
# The wrapper type never has restrictions on complexity,
478+
# so try to use that to refine the estimated type too.
479+
tni = _typename(widenconst(ai))
480+
if tni isa Const && tni === _typename(widenconst(bi))
481+
# A tmeet call may cause tyi to become complex, but since the inputs were
482+
# strictly limited to being egal, this has no restrictions on complexity.
483+
# (Otherwise, we would need to use <: and take the narrower one without
484+
# intersection. See the similar comment in abstract_call_method.)
485+
tyi = typeintersect(ft, (tni.val::Core.TypeName).wrapper)
486+
else
487+
# Since aty===bty, the fieldtype has no restrictions on complexity.
488+
tyi = ft
489+
end
490+
end
491+
fields[i] = tyi
492+
if !anyrefine
493+
anyrefine = has_nontrivial_const_info(tyi) || # constant information
494+
tyi ft # just a type-level information, but more precise than the declared type
470495
end
471-
fields[i] = ity
472-
anyconst |= has_nontrivial_const_info(ity)
473496
end
474-
return anyconst ? PartialStruct(aty, fields) : aty
497+
return anyrefine ? PartialStruct(aty, fields) : aty
475498
end
476499
end
477500
if isa(typea, PartialOpaque) && isa(typeb, PartialOpaque) && widenconst(typea) == widenconst(typeb)
@@ -658,44 +681,3 @@ function tuplemerge(a::DataType, b::DataType)
658681
end
659682
return Tuple{p...}
660683
end
661-
662-
# compute typeintersect over the extended inference lattice
663-
# where v is in the extended lattice, and t is a Type
664-
function tmeet(@nospecialize(v), @nospecialize(t))
665-
if isa(v, Const)
666-
if !has_free_typevars(t) && !isa(v.val, t)
667-
return Bottom
668-
end
669-
return v
670-
elseif isa(v, PartialStruct)
671-
has_free_typevars(t) && return v
672-
widev = widenconst(v)
673-
if widev <: t
674-
return v
675-
end
676-
ti = typeintersect(widev, t)
677-
valid_as_lattice(ti) || return Bottom
678-
@assert widev <: Tuple
679-
new_fields = Vector{Any}(undef, length(v.fields))
680-
for i = 1:length(new_fields)
681-
vfi = v.fields[i]
682-
if isvarargtype(vfi)
683-
new_fields[i] = vfi
684-
else
685-
new_fields[i] = tmeet(vfi, widenconst(getfield_tfunc(t, Const(i))))
686-
if new_fields[i] === Bottom
687-
return Bottom
688-
end
689-
end
690-
end
691-
return tuple_tfunc(new_fields)
692-
elseif isa(v, Conditional)
693-
if !(Bool <: t)
694-
return Bottom
695-
end
696-
return v
697-
end
698-
ti = typeintersect(widenconst(v), t)
699-
valid_as_lattice(ti) || return Bottom
700-
return ti
701-
end

test/compiler/inference.jl

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3999,15 +3999,33 @@ end
39993999
@test (a, c)
40004000
@test (b, c)
40014001

4002-
@test @eval Module() begin
4003-
const ginit = Base.ImmutableDict{Any,Any}()
4004-
Base.return_types() do
4005-
g = ginit
4002+
init = Base.ImmutableDict{Number,Number}()
4003+
a = Const(init)
4004+
b = Core.PartialStruct(typeof(init), Any[Const(init), Any, ComplexF64])
4005+
c = Core.Compiler.tmerge(a, b)
4006+
@test (a, c) && (b, c)
4007+
@test c === typeof(init)
4008+
4009+
a = Core.PartialStruct(typeof(init), Any[Const(init), ComplexF64, ComplexF64])
4010+
c = Core.Compiler.tmerge(a, b)
4011+
@test (a, c) && (b, c)
4012+
@test c.fields[2] === Any # or Number
4013+
@test c.fields[3] === ComplexF64
4014+
4015+
b = Core.PartialStruct(typeof(init), Any[Const(init), ComplexF32, Union{ComplexF32,ComplexF64}])
4016+
c = Core.Compiler.tmerge(a, b)
4017+
@test (a, c)
4018+
@test (b, c)
4019+
@test c.fields[2] === Complex
4020+
@test c.fields[3] === Complex
4021+
4022+
global const ginit43784 = Base.ImmutableDict{Any,Any}()
4023+
@test Base.return_types() do
4024+
g = ginit43784
40064025
while true
40074026
g = Base.ImmutableDict(g, 1=>2)
40084027
end
40094028
end |> only === Union{}
4010-
end
40114029
end
40124030

40134031
# Test that purity modeling doesn't accidentally introduce new world age issues

0 commit comments

Comments
 (0)