Skip to content

Commit

Permalink
inference: fix tmerge lattice over issimpleenoughtype
Browse files Browse the repository at this point in the history
Previously we assumed only union type could have complexity that
violated the tmerge lattice requirements, but other types can have that
too. This lets us fix an issue with the PartialStruct comparison failing
for undefined fields, mentioned in #43784.
  • Loading branch information
vtjnash committed Mar 10, 2022
1 parent 8076517 commit 4ca2759
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 4 deletions.
44 changes: 43 additions & 1 deletion base/compiler/typelattice.jl
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ The non-strict partial order over the type inference lattice.
end
for i in 1:nfields(a.val)
# XXX: let's handle varargs later
isdefined(a.val, i) || return false
isdefined(a.val, i) || continue # since ∀ T Union{} ⊑ T
(Const(getfield(a.val, i)), b.fields[i]) || return false
end
return true
Expand Down Expand Up @@ -289,6 +289,48 @@ function is_lattice_equal(@nospecialize(a), @nospecialize(b))
return a b && b a
end

# compute typeintersect over the extended inference lattice,
# as precisely as we can,
# where v is in the extended lattice, and t is a Type.
function tmeet(@nospecialize(v), @nospecialize(t))
if isa(v, Const)
if !has_free_typevars(t) && !isa(v.val, t)
return Bottom
end
return v
elseif isa(v, PartialStruct)
has_free_typevars(t) && return v
widev = widenconst(v)
if widev <: t
return v
end
ti = typeintersect(widev, t)
valid_as_lattice(ti) || return Bottom
@assert widev <: Tuple
new_fields = Vector{Any}(undef, length(v.fields))
for i = 1:length(new_fields)
vfi = v.fields[i]
if isvarargtype(vfi)
new_fields[i] = vfi
else
new_fields[i] = tmeet(vfi, widenconst(getfield_tfunc(t, Const(i))))
if new_fields[i] === Bottom
return Bottom
end
end
end
return tuple_tfunc(new_fields)
elseif isa(v, Conditional)
if !(Bool <: t)
return Bottom
end
return v
end
ti = typeintersect(widenconst(v), t)
valid_as_lattice(ti) || return Bottom
return ti
end

widenconst(c::AnyConditional) = Bool
widenconst((; val)::Const) = isa(val, Type) ? Type{val} : typeof(val)
widenconst(m::MaybeUndef) = widenconst(m.typ)
Expand Down
54 changes: 51 additions & 3 deletions base/compiler/typelimits.jl
Original file line number Diff line number Diff line change
Expand Up @@ -298,23 +298,71 @@ union_count_abstract(x::Union) = union_count_abstract(x.a) + union_count_abstrac
union_count_abstract(@nospecialize(x)) = !isdispatchelem(x)

function issimpleenoughtype(@nospecialize t)
t = ignorelimited(t)
return unionlen(t) + union_count_abstract(t) <= MAX_TYPEUNION_LENGTH &&
unioncomplexity(t) <= MAX_TYPEUNION_COMPLEXITY
end

# A simplified type_more_complex query over the extended lattice
# (assumes typeb ⊑ typea)
function issimplertype(@nospecialize(typea), @nospecialize(typeb))
typea = ignorelimited(typea)
typeb = ignorelimited(typeb)
typea isa MaybeUndef && (typea = typea.typ) # n.b. does not appear in inference
typeb isa MaybeUndef && (typeb = typeb.typ) # n.b. does not appear in inference
typea === typeb && return true
if typea isa PartialStruct
aty = widenconst(typea)
for i = 1:length(typea.fields)
ai = typea.fields[i]
bi = fieldtype(aty, i)
is_lattice_equal(ai, bi) && continue
tni = _typename(widenconst(ai))
if tni isa Const
bi = (tni.val::Core.TypeName).wrapper
is_lattice_equal(ai, bi) && continue
end
bi = getfield_tfunc(typeb, Const(i))
is_lattice_equal(ai, bi) && continue
# It is not enough for ai to be simpler than bi: it must exactly equal
# (for this, an invariant struct field, by contrast to
# type_more_complex above which handles covariant tuples).
return false
end
elseif typea isa Type
return issimpleenoughtype(typea)
# elseif typea isa Const # fall-through good
elseif typea isa Conditional # follow issubconditional query
typeb isa Const && return true
typeb isa Conditional || return false
is_same_conditionals(typea, typeb) || return false
issimplertype(typea.vtype, typeb.vtype) || return false
issimplertype(typea.elsetype, typeb.elsetype) || return false
elseif typea isa InterConditional # ibid
typeb isa Const && return true
typeb isa InterConditional || return false
is_same_conditionals(typea, typeb) || return false
issimplertype(typea.vtype, typeb.vtype) || return false
issimplertype(typea.elsetype, typeb.elsetype) || return false
elseif typea isa PartialOpaque
# TODO
end
return true
end

# pick a wider type that contains both typea and typeb,
# with some limits on how "large" it can get,
# but without losing too much precision in common cases
# and also trying to be mostly associative and commutative
function tmerge(@nospecialize(typea), @nospecialize(typeb))
typea === Union{} && return typeb
typeb === Union{} && return typea
typea === typeb && return typea

suba = typea typeb
suba && issimpleenoughtype(typeb) && return typeb
suba && issimplertype(typeb, typea) && return typeb
subb = typeb typea
suba && subb && return typea
subb && issimpleenoughtype(typea) && return typea
subb && issimplertype(typea, typeb) && return typea

# type-lattice for LimitedAccuracy wrapper
# the merge create a slightly narrower type than needed, but we can't
Expand Down

0 comments on commit 4ca2759

Please sign in to comment.