Skip to content

Commit f68793b

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 3a5dc09 commit f68793b

File tree

3 files changed

+93
-53
lines changed

3 files changed

+93
-53
lines changed

base/compiler/typelattice.jl

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,3 +425,45 @@ function stupdate1!(state::VarTable, change::StateUpdate)
425425
end
426426
return false
427427
end
428+
429+
# compute typeintersect over the extended inference lattice,
430+
# as precisely as we can,
431+
# where v is in the extended lattice, and t is a Type.
432+
function tmeet(@nospecialize(v), @nospecialize(t))
433+
if isa(v, Const)
434+
if !has_free_typevars(t) && !isa(v.val, t)
435+
return Bottom
436+
end
437+
return v
438+
elseif isa(v, PartialStruct)
439+
has_free_typevars(t) && return v
440+
widev = widenconst(v)
441+
if widev <: t
442+
return v
443+
end
444+
ti = typeintersect(widev, t)
445+
valid_as_lattice(ti) || return Bottom
446+
@assert widev <: Tuple
447+
new_fields = Vector{Any}(undef, length(v.fields))
448+
for i = 1:length(new_fields)
449+
vfi = v.fields[i]
450+
if isvarargtype(vfi)
451+
new_fields[i] = vfi
452+
else
453+
new_fields[i] = tmeet(vfi, widenconst(getfield_tfunc(t, Const(i))))
454+
if new_fields[i] === Bottom
455+
return Bottom
456+
end
457+
end
458+
end
459+
return tuple_tfunc(new_fields)
460+
elseif isa(v, Conditional)
461+
if !(Bool <: t)
462+
return Bottom
463+
end
464+
return v
465+
end
466+
ti = typeintersect(widenconst(v), t)
467+
valid_as_lattice(ti) || return Bottom
468+
return ti
469+
end

base/compiler/typelimits.jl

Lines changed: 28 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,7 @@ function tmerge(@nospecialize(typea), @nospecialize(typeb))
404404
aty = widenconst(typea)
405405
bty = widenconst(typeb)
406406
if aty === bty
407+
# must have egal here, since we do not create PartialStruct for non-concrete types
407408
typea_nfields = nfields_tfunc(typea)
408409
typeb_nfields = nfields_tfunc(typeb)
409410
isa(typea_nfields, Const) || return aty
@@ -412,18 +413,38 @@ function tmerge(@nospecialize(typea), @nospecialize(typeb))
412413
type_nfields === typeb_nfields.val::Int || return aty
413414
type_nfields == 0 && return aty
414415
fields = Vector{Any}(undef, type_nfields)
415-
anyconst = false
416+
anyrefine = false
416417
for i = 1:type_nfields
417418
ai = getfield_tfunc(typea, Const(i))
418419
bi = getfield_tfunc(typeb, Const(i))
419-
ity = tmerge(ai, bi)
420-
if ai === Union{} || bi === Union{}
421-
ity = widenconst(ity)
420+
ft = fieldtype(aty, i)
421+
if is_lattice_equal(ai, bi)
422+
# Since ai===bi, the given type has no restrictions on complexity.
423+
# and can be used to refine ft
424+
tyi = ai
425+
else
426+
# Otherwise choose between using the fieldtype or some other simple merged type.
427+
# The wrapper type never has restrictions on complexity,
428+
# so try to use that to refine the estimated type too.
429+
tni = _typename(widenconst(ai))
430+
if tni isa Const && tni === _typename(widenconst(bi))
431+
# A tmeet call may cause tyi to become complex, but since the inputs were
432+
# strictly limited to being egal, this has no restrictions on complexity.
433+
# (Otherwise, we would need to use <: and take the narrower one without
434+
# intersection. See the similar comment in abstract_call_method.)
435+
tyi = typeintersect(ft, (tni.val::Core.TypeName).wrapper)
436+
else
437+
# Since aty===bty, the fieldtype has no restrictions on complexity.
438+
tyi = ft
439+
end
440+
end
441+
fields[i] = tyi
442+
if !anyrefine
443+
anyrefine = has_nontrivial_const_info(tyi) || # constant information
444+
tyi ft # just a type-level information, but more precise than the declared type
422445
end
423-
fields[i] = ity
424-
anyconst |= has_nontrivial_const_info(ity)
425446
end
426-
return anyconst ? PartialStruct(aty, fields) : aty
447+
return anyrefine ? PartialStruct(aty, fields) : aty
427448
end
428449
end
429450
if isa(typea, PartialOpaque) && isa(typeb, PartialOpaque) && widenconst(typea) == widenconst(typeb)
@@ -610,44 +631,3 @@ function tuplemerge(a::DataType, b::DataType)
610631
end
611632
return Tuple{p...}
612633
end
613-
614-
# compute typeintersect over the extended inference lattice
615-
# where v is in the extended lattice, and t is a Type
616-
function tmeet(@nospecialize(v), @nospecialize(t))
617-
if isa(v, Const)
618-
if !has_free_typevars(t) && !isa(v.val, t)
619-
return Bottom
620-
end
621-
return v
622-
elseif isa(v, PartialStruct)
623-
has_free_typevars(t) && return v
624-
widev = widenconst(v)
625-
if widev <: t
626-
return v
627-
end
628-
ti = typeintersect(widev, t)
629-
valid_as_lattice(ti) || return Bottom
630-
@assert widev <: Tuple
631-
new_fields = Vector{Any}(undef, length(v.fields))
632-
for i = 1:length(new_fields)
633-
vfi = v.fields[i]
634-
if isvarargtype(vfi)
635-
new_fields[i] = vfi
636-
else
637-
new_fields[i] = tmeet(vfi, widenconst(getfield_tfunc(t, Const(i))))
638-
if new_fields[i] === Bottom
639-
return Bottom
640-
end
641-
end
642-
end
643-
return tuple_tfunc(new_fields)
644-
elseif isa(v, Conditional)
645-
if !(Bool <: t)
646-
return Bottom
647-
end
648-
return v
649-
end
650-
ti = typeintersect(widenconst(v), t)
651-
valid_as_lattice(ti) || return Bottom
652-
return ti
653-
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)