typelimits.jl
# This file is a part of Julia. License is MIT: https://julialang.org/license
#########################
# limitation parameters #
#########################
const MAX_TYPEUNION_COMPLEXITY = 3
const MAX_TYPEUNION_LENGTH = 3
#########################
# limitation heuristics #
#########################
# limit the complexity of type `t` to be simpler than the comparison type `compare`
# no new values may be introduced, so the parameter `source` encodes the set of all values already present
# the outermost tuple type is permitted to have up to `allowed_tuplelen` parameters
function limit_type_size(@nospecialize(t), @nospecialize(compare), @nospecialize(source), allowed_tupledepth::Int, allowed_tuplelen::Int)
source = svec(unwrap_unionall(compare), unwrap_unionall(source))
source[1] === source[2] && (source = svec(source[1]))
type_more_complex(t, compare, source, 1, allowed_tupledepth, allowed_tuplelen) || return t
r = _limit_type_size(t, compare, source, 1, allowed_tuplelen)
#@assert t <: r # this may fail if t contains a typevar in invariant and multiple times
# in covariant position and r looses the occurrence in invariant position (see #36407)
if !(t <: r) # ideally, this should never happen
# widen to minimum complexity to obtain a valid result
r = _limit_type_size(t, Any, source, 1, allowed_tuplelen)
t <: r || (r = Any) # final escape hatch
end
#@assert r === _limit_type_size(r, t, source) # this monotonicity constraint is slightly stronger than actually required,
# since we only actually need to demonstrate that repeated application would reaches a fixed point,
#not that it is already at the fixed point
return r
end
# try to find `type` somewhere in `comparison` type
# at a minimum nesting depth of `mindepth`
function is_derived_type(@nospecialize(t), @nospecialize(c), mindepth::Int)
if has_free_typevars(t) || has_free_typevars(c)
# Don't allow finding types with free typevars. These strongly depend
# on identity and we do not make any effort to make sure this returns
# sensible results in that case.
return false
end
if t === c
return mindepth <= 1
end
isvarargtype(t) && (t = unwrapva(t))
isvarargtype(c) && (c = unwrapva(c))
if isa(c, Union)
# see if it is one of the elements of the union
return is_derived_type(t, c.a, mindepth) || is_derived_type(t, c.b, mindepth)
elseif isa(c, UnionAll)
# see if it is derived from the body
# also handle the var here, since this construct bounds the mindepth to the smallest possible value
return is_derived_type(t, c.var.ub, mindepth) || is_derived_type(t, c.body, mindepth)
elseif isa(c, DataType)
if mindepth > 0
mindepth -= 1
end
if isa(t, DataType)
# see if it is one of the supertypes of a parameter
super = supertype(c)
while super !== Any
t === super && return true
super = supertype(super)
end
end
# see if it was extracted from a type parameter
cP = c.parameters
for p in cP
is_derived_type(t, p, mindepth) && return true
end
end
return false
end
function is_derived_type_from_any(@nospecialize(t), sources::SimpleVector, mindepth::Int)
for s in sources
is_derived_type(t, s, mindepth) && return true
end
return false
end
# The goal of this function is to return a type of greater "size" and less "complexity" than
# both `t` or `c` over the lattice defined by `sources`, `depth`, and `allowed_tuplelen`.
function _limit_type_size(@nospecialize(t), @nospecialize(c), sources::SimpleVector, depth::Int, allowed_tuplelen::Int)
@assert isa(t, Type) && isa(c, Type) "unhandled TypeVar / Vararg"
if t === c
return t # quick egal test
elseif t === Union{}
return t # easy case
elseif isa(t, DataType) && isempty(t.parameters)
return t # fast path: unparameterized are always simple
else
ut = unwrap_unionall(t)
if is_derived_type_from_any(ut, sources, depth)
return t # t isn't something new
end
end
# peel off (and ignore) wrappers - they contribute no useful information, so we don't need to consider their size
# first attempt to turn `c` into a type that contributes meaningful information
# by peeling off meaningless non-matching wrappers of comparison one at a time
# then unwrap `t`
# NOTE that `TypeVar` / `Vararg` are handled separately to catch the logic errors
if isa(c, UnionAll)
return __limit_type_size(t, c.body, sources, depth, allowed_tuplelen)::Type
end
if isa(t, UnionAll)
tbody = __limit_type_size(t.body, c, sources, depth, allowed_tuplelen)
tbody === t.body && return t
return UnionAll(t.var, tbody)::Type
elseif isa(t, Union)
if isa(c, Union)
a = __limit_type_size(t.a, c.a, sources, depth, allowed_tuplelen)
b = __limit_type_size(t.b, c.b, sources, depth, allowed_tuplelen)
return Union{a, b}
end
elseif isa(t, DataType)
if isType(t)
# Type is fairly important, so do not widen it as fast as other types if avoidable
tt = t.parameters[1]
ttu = unwrap_unionall(tt) # TODO: use argument_datatype(tt) after #50692 fixed
# must forbid nesting through this if we detect that potentially occurring
# we already know !is_derived_type_from_any so refuse to recurse here
if !isa(ttu, DataType)
return Type
elseif isType(ttu)
return Type{<:Type}
end
# try to peek into c to get a comparison object, but if we can't perhaps t is already simple enough on its own
if isType(c)
ct = c.parameters[1]
else
ct = Union{}
end
Qt = __limit_type_size(tt, ct, sources, depth + 1, 0)
Qt === tt && return t
Qt === Any && return Type
# Can't form Type{<:Qt} just yet, without first make sure we limited the depth
# enough, since this moves Qt outside of Type for is_derived_type_from_any
Qt = __limit_type_size(tt, ct, sources, depth + 2, 0)
Qt === Any && return Type
return Type{<:Qt}
elseif isa(c, DataType)
tP = t.parameters
cP = c.parameters
if t.name === c.name && !isempty(cP)
if t.name === Tuple.name
# for covariant datatypes (Tuple),
# apply type-size limit element-wise
ltP = length(tP)
lcP = length(cP)
np = min(ltP, max(lcP, allowed_tuplelen))
Q = Any[ tP[i] for i in 1:np ]
if ltP > np
# combine tp[np:end] into tP[np] using Vararg
Q[np] = tuple_tail_elem(fallback_lattice, Bottom, Any[ tP[i] for i in np:ltP ])
end
for i = 1:np
# now apply limit element-wise to Q
# padding out the comparison as needed to allowed_tuplelen elements
if i <= lcP
cPi = cP[i]
elseif isvarargtype(cP[lcP])
cPi = cP[lcP]
else
cPi = Any
end
Q[i] = __limit_type_size(Q[i], cPi, sources, depth + 1, 0)
end
return Tuple{Q...}
end
end
end
if allowed_tuplelen < 1 && t.name === Tuple.name
# forbid nesting Tuple{Tuple{Tuple...}} through this
return Any
end
widert = t.name.wrapper
if !(t <: widert) # XXX: we should call has_free_typevars(t) here, but usually t does not have those wrappers by the time it got here
# This can happen when a typevar has bounds too wide for its context, e.g.
# `Complex{T} where T` is not a subtype of `Complex`. In that case widen even
# faster to something safe to ensure the result is a supertype of the input.
return Any
end
return widert
end
return Any
end
# helper function of `_limit_type_size`, which has the right to take and return `TypeVar` / `Vararg`
function __limit_type_size(@nospecialize(t), @nospecialize(c), sources::SimpleVector, depth::Int, allowed_tuplelen::Int)
cN = 0
if isvarargtype(c) # Tuple{Vararg{T}} --> Tuple{T} is OK
isdefined(c, :N) && (cN = c.N)
c = unwrapva(c)
end
if isa(c, TypeVar)
if isa(t, TypeVar) && t.ub === c.ub && (t.lb === Union{} || t.lb === c.lb)
return t # it's ok to change the name, or widen `lb` to Union{}, so we can handle this immediately here
end
return __limit_type_size(t, c.ub, sources, depth, allowed_tuplelen)
elseif isa(t, TypeVar)
# don't have a matching TypeVar in comparison, so we keep just the upper bound
return __limit_type_size(t.ub, c, sources, depth, allowed_tuplelen)
elseif isvarargtype(t)
# Tuple{Vararg{T,N}} --> Tuple{Vararg{S,M}} is OK
# Tuple{T} --> Tuple{Vararg{T}} is OK
# but S must be more limited than T, and must not introduce a new number for M
VaT = __limit_type_size(unwrapva(t), c, sources, depth + 1, 0)
if isdefined(t, :N)
tN = t.N
if isa(tN, TypeVar) || tN === cN
return Vararg{VaT, tN}
end
end
return Vararg{VaT}
else
return _limit_type_size(t, c, sources, depth, allowed_tuplelen)
end
end
function type_more_complex(@nospecialize(t), @nospecialize(c), sources::SimpleVector, depth::Int, tupledepth::Int, allowed_tuplelen::Int)
# detect cases where the comparison is trivial
if t === c
return false
elseif t === Union{}
return false # Bottom is as simple as they come
elseif isa(t, DataType) && isempty(t.parameters)
return false # fastpath: unparameterized types are always finite
elseif is_derived_type_from_any(unwrap_unionall(t), sources, depth)
return false # t isn't something new
end
# peel off wrappers
isvarargtype(t) && (t = unwrapva(t))
isvarargtype(c) && (c = unwrapva(c))
if isa(c, UnionAll)
# allow wrapping type with fewer UnionAlls than comparison only if in a covariant context
if !isa(t, UnionAll) && tupledepth == 0
return true
end
c = unwrap_unionall(c)
end
if isa(t, UnionAll)
t = unwrap_unionall(t)
end
# rules for various comparison types
if isa(c, TypeVar)
tupledepth = 1
if isa(t, TypeVar)
return !(t.lb === Union{} || t.lb === c.lb) || # simplify lb towards Union{}
type_more_complex(t.ub, c.ub, sources, depth + 1, tupledepth, 0)
end
c.lb === Union{} || return true
return type_more_complex(t, c.ub, sources, depth, tupledepth, 0)
elseif isa(c, Union)
if isa(t, Union)
return type_more_complex(t.a, c.a, sources, depth, tupledepth, allowed_tuplelen) ||
type_more_complex(t.b, c.b, sources, depth, tupledepth, allowed_tuplelen)
end
return type_more_complex(t, c.a, sources, depth, tupledepth, allowed_tuplelen) &&
type_more_complex(t, c.b, sources, depth, tupledepth, allowed_tuplelen)
elseif isa(t, Int) && isa(c, Int)
return t !== 1 && !(0 <= t < c) # alternatively, could use !(abs(t) <= abs(c) || abs(t) < n) for some n
end
# base case for data types
if isa(t, DataType)
tP = t.parameters
if isType(t)
# Type is fairly important, so do not widen it as fast as other types if avoidable
tt = tP[1]
ttu = unwrap_unionall(tt) # TODO: use argument_datatype(tt) after #50692 fixed
if isType(c)
ct = c.parameters[1]
else
ct = Union{}
tupledepth == 0 && return true # cannot allow nesting
end
# allow creating variation within a nested Type, but not very deep
if tupledepth > 1
tupledepth = 1
else
tupledepth = 0
end
return type_more_complex(tt, ct, sources, depth + 1, tupledepth, 0)
elseif isa(c, DataType) && t.name === c.name
cP = c.parameters
length(cP) < length(tP) && return true
isempty(tP) && return false
length(cP) > length(tP) && !isvarargtype(tP[end]) && depth == 1 && return false # is this line necessary?
ntail = length(cP) - length(tP) # assume parameters were dropped from the tuple head
# allow creating variation within a nested tuple, but only so deep
if t.name === Tuple.name && tupledepth > 0
tupledepth -= 1
else
tupledepth = 0
end
for i = 1:length(tP)
tPi = tP[i]
cPi = cP[i + ntail]
type_more_complex(tPi, cPi, sources, depth + 1, tupledepth, 0) && return true
end
return false
end
end
return true
end
union_count_abstract(x::Union) = union_count_abstract(x.a) + union_count_abstract(x.b)
union_count_abstract(@nospecialize(x)) = !isdispatchelem(x)
function issimpleenoughtype(@nospecialize t)
ut = unwrap_unionall(t)
ut isa DataType && ut.name.wrapper == t && return true
return max(unionlen(t), union_count_abstract(t) + 1) <= MAX_TYPEUNION_LENGTH &&
unioncomplexity(t) <= MAX_TYPEUNION_COMPLEXITY
end
# We may want to apply a stricter limit than issimpleenoughtype to
# tupleelements individually, to try to keep the whole tuple under the limit,
# even after complicated recursion and other operations on it elsewhere
const issimpleenoughtupleelem = issimpleenoughtype
# A simplified type_more_complex query over the extended lattice
# (assumes typeb ⊑ typea)
@nospecializeinfer function issimplertype(𝕃::AbstractLattice, @nospecialize(typea), @nospecialize(typeb))
@assert !isa(typea, LimitedAccuracy) && !isa(typeb, LimitedAccuracy) "LimitedAccuracy not supported by simplertype lattice" # n.b. the caller was supposed to handle these
typea === typeb && return true
if typea isa PartialStruct
aty = widenconst(typea)
for i = 1:length(typea.fields)
ai = unwrapva(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 to true is 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.thentype, typeb.thentype) || 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.thentype, typeb.thentype) || return false
issimplertype(𝕃, typea.elsetype, typeb.elsetype) || return false
elseif typea isa MustAlias
typeb isa MustAlias || return false
issubalias(typeb, typea) || return false
issimplertype(𝕃, typea.vartyp, typeb.vartyp) || return false
issimplertype(𝕃, typea.fldtyp, typeb.fldtyp) || return false
elseif typea isa InterMustAlias
typeb isa InterMustAlias || return false
issubalias(typeb, typea) || return false
issimplertype(𝕃, typea.vartyp, typeb.vartyp) || return false
issimplertype(𝕃, typea.fldtyp, typeb.fldtyp) || return false
elseif typea isa PartialOpaque
# TODO
typeb isa PartialOpaque || return false
aty = widenconst(typea)
bty = widenconst(typeb)
if typea.source === typeb.source && typea.parent === typeb.parent && aty == bty && typea.env == typeb.env
return false
end
return false
end
return true
end
@inline function tmerge_fast_path(lattice::AbstractLattice, @nospecialize(typea), @nospecialize(typeb))
# Fast paths
typea === Union{} && return typeb
typeb === Union{} && return typea
typea === typeb && return typea
suba = ⊑(lattice, typea, typeb)
suba && issimplertype(lattice, typeb, typea) && return typeb
subb = ⊑(lattice, typeb, typea)
suba && subb && return typea
subb && issimplertype(lattice, typea, typeb) && return typea
return nothing
end
function union_causes(causesa::IdSet{InferenceState}, causesb::IdSet{InferenceState})
if causesa ⊆ causesb
return causesb
elseif causesb ⊆ causesa
return causesa
else
return union!(copy(causesa), causesb)
end
end
function merge_causes(causesa::IdSet{InferenceState}, causesb::IdSet{InferenceState})
# TODO: When lattice elements are equal, we're allowed to discard one or the
# other set, but we'll need to come up with a consistent rule. For now, we
# just check the length, but other heuristics may be applicable.
if length(causesa) < length(causesb)
return causesa
elseif length(causesb) < length(causesa)
return causesb
else
return union!(copy(causesa), causesb)
end
end
@nospecializeinfer @noinline function tmerge_limited(lattice::InferenceLattice, @nospecialize(typea), @nospecialize(typeb))
typea === Union{} && return typeb
typeb === Union{} && return typea
# Like tmerge_fast_path, but tracking which causes need to be preserved at
# the same time.
if isa(typea, LimitedAccuracy) && isa(typeb, LimitedAccuracy)
causesa = typea.causes
causesb = typeb.causes
typea = typea.typ
typeb = typeb.typ
suba = ⊑(lattice, typea, typeb)
subb = ⊑(lattice, typeb, typea)
# Approximated types are lattice equal. Merge causes.
if suba && subb
return LimitedAccuracy(typeb, merge_causes(causesa, causesb))
elseif suba
issimplertype(lattice, typeb, typea) && return LimitedAccuracy(typeb, causesb)
causes = causesb
# `a`'s causes may be discarded
elseif subb
causes = causesa
else
causes = union_causes(causesa, causesb)
end
else
if isa(typeb, LimitedAccuracy)
(typea, typeb) = (typeb, typea)
end
typea = typea::LimitedAccuracy
causes = typea.causes
typea = typea.typ
suba = ⊑(lattice, typea, typeb)
if suba
issimplertype(lattice, typeb, typea) && return typeb
# `typea` was narrower than `typeb`. Whatever tmerge produces,
# we know it must be wider than `typeb`, so we may drop the
# causes.
causes = nothing
end
subb = ⊑(lattice, typeb, typea)
end
suba && subb && return LimitedAccuracy(typea, causes)
subb && issimplertype(lattice, typea, typeb) && return LimitedAccuracy(typea, causes)
return LimitedAccuracy(tmerge(widenlattice(lattice), typea, typeb), causes)
end
@nospecializeinfer function tmerge(lattice::InferenceLattice, @nospecialize(typea), @nospecialize(typeb))
if isa(typea, LimitedAccuracy) || isa(typeb, LimitedAccuracy)
return tmerge_limited(lattice, typea, typeb)
end
return tmerge(widenlattice(lattice), typea, typeb)
end
@nospecializeinfer function tmerge(lattice::ConditionalsLattice, @nospecialize(typea), @nospecialize(typeb))
# type-lattice for Conditional wrapper (NOTE never be merged with InterConditional)
if isa(typea, Conditional) && isa(typeb, Const)
if typeb.val === true
typeb = Conditional(typea.slot, Any, Union{})
elseif typeb.val === false
typeb = Conditional(typea.slot, Union{}, Any)
end
end
if isa(typeb, Conditional) && isa(typea, Const)
if typea.val === true
typea = Conditional(typeb.slot, Any, Union{})
elseif typea.val === false
typea = Conditional(typeb.slot, Union{}, Any)
end
end
if isa(typea, Conditional) && isa(typeb, Conditional)
if is_same_conditionals(typea, typeb)
thentype = tmerge(widenlattice(lattice), typea.thentype, typeb.thentype)
elsetype = tmerge(widenlattice(lattice), typea.elsetype, typeb.elsetype)
if thentype !== elsetype
return Conditional(typea.slot, thentype, elsetype)
end
end
val = maybe_extract_const_bool(typea)
if val isa Bool && val === maybe_extract_const_bool(typeb)
return Const(val)
end
return Bool
end
if isa(typea, Conditional)
typeb === Union{} && return typea
typea = widenconditional(typea)
elseif isa(typeb, Conditional)
typea === Union{} && return typeb
typeb = widenconditional(typeb)
end
return tmerge(widenlattice(lattice), typea, typeb)
end
@nospecializeinfer function tmerge(lattice::InterConditionalsLattice, @nospecialize(typea), @nospecialize(typeb))
# type-lattice for InterConditional wrapper (NOTE never be merged with Conditional)
if isa(typea, InterConditional) && isa(typeb, Const)
if typeb.val === true
typeb = InterConditional(typea.slot, Any, Union{})
elseif typeb.val === false
typeb = InterConditional(typea.slot, Union{}, Any)
end
end
if isa(typeb, InterConditional) && isa(typea, Const)
if typea.val === true
typea = InterConditional(typeb.slot, Any, Union{})
elseif typea.val === false
typea = InterConditional(typeb.slot, Union{}, Any)
end
end
if isa(typea, InterConditional) && isa(typeb, InterConditional)
if is_same_conditionals(typea, typeb)
thentype = tmerge(widenlattice(lattice), typea.thentype, typeb.thentype)
elsetype = tmerge(widenlattice(lattice), typea.elsetype, typeb.elsetype)
if thentype !== elsetype
return InterConditional(typea.slot, thentype, elsetype)
end
end
val = maybe_extract_const_bool(typea)
if val isa Bool && val === maybe_extract_const_bool(typeb)
return Const(val)
end
return Bool
end
if isa(typea, InterConditional)
typeb === Union{} && return typea
typea = widenconditional(typea)
elseif isa(typeb, InterConditional)
typea === Union{} && return typeb
typeb = widenconditional(typeb)
end
return tmerge(widenlattice(lattice), typea, typeb)
end
@nospecializeinfer function tmerge(𝕃::AnyMustAliasesLattice, @nospecialize(typea), @nospecialize(typeb))
if is_valid_lattice_norec(𝕃, typea)
typeb === Union{} && return typea
typea = widenmustalias(typea)
end
if is_valid_lattice_norec(𝕃, typeb)
typea === Union{} && return typeb
typeb = widenmustalias(typeb)
end
return tmerge(widenlattice(𝕃), typea, typeb)
end
# N.B. This can also be called with both typea::Const and typeb::Const to
# to recover PartialStruct from `Const`s with overlapping fields.
@nospecializeinfer function tmerge_partial_struct(lattice::PartialsLattice, @nospecialize(typea), @nospecialize(typeb))
aty = widenconst(typea)
bty = widenconst(typeb)
if aty === bty
# must have egal here, since we do not create PartialStruct for non-concrete types
typea_nfields = nfields_tfunc(lattice, typea)
typeb_nfields = nfields_tfunc(lattice, typeb)
isa(typea_nfields, Const) || return nothing
isa(typeb_nfields, Const) || return nothing
type_nfields = typea_nfields.val::Int
type_nfields === typeb_nfields.val::Int || return nothing
type_nfields == 0 && return nothing
fields = Vector{Any}(undef, type_nfields)
anyrefine = false
for i = 1:type_nfields
ai = getfield_tfunc(lattice, typea, Const(i))
bi = getfield_tfunc(lattice, typeb, Const(i))
# N.B.: We're assuming here that !isType(aty), because that case
# only arises when typea === typeb, which should have been caught
# before calling this.
ft = fieldtype(aty, i)
if is_lattice_equal(lattice, ai, bi) || is_lattice_equal(lattice, ai, ft)
# Since ai===bi, the given type has no restrictions on complexity.
# and can be used to refine ft
tyi = ai
elseif is_lattice_equal(lattice, bi, ft)
tyi = bi
elseif (tyi′ = tmerge_field(lattice, ai, bi); tyi′ !== nothing)
# allow external lattice implementation to provide a custom field-merge strategy
tyi = tyi′
else
# Otherwise use the default aggressive field-merge implementation, and
# choose between using the fieldtype or some other simple merged type.
# The wrapper type never has restrictions on complexity,
# so try to use that to refine the estimated type too.
tni = _typename(widenconst(ai))
if tni isa Const && tni === _typename(widenconst(bi))
# A tmeet call may cause tyi to become complex, but since the inputs were
# strictly limited to being egal, this has no restrictions on complexity.
# (Otherwise, we would need to use <: and take the narrower one without
# intersection. See the similar comment in abstract_call_method.)
tyi = typeintersect(ft, (tni.val::Core.TypeName).wrapper)
else
# Since aty===bty, the fieldtype has no restrictions on complexity.
tyi = ft
end
end
fields[i] = tyi
if !anyrefine
anyrefine = has_nontrivial_extended_info(lattice, tyi) || # extended information
⋤(lattice, tyi, ft) # just a type-level information, but more precise than the declared type
end
end
anyrefine && return PartialStruct(aty, fields)
end
return nothing
end
@nospecializeinfer function tmerge(lattice::PartialsLattice, @nospecialize(typea), @nospecialize(typeb))
r = tmerge_fast_path(lattice, typea, typeb)
r !== nothing && return r
# type-lattice for Const and PartialStruct wrappers
aps = isa(typea, PartialStruct)
bps = isa(typeb, PartialStruct)
acp = aps || isa(typea, Const)
bcp = bps || isa(typeb, Const)
if acp && bcp
typea === typeb && return typea
psrt = tmerge_partial_struct(lattice, typea, typeb)
psrt !== nothing && return psrt
end
# Don't widen const here - external AbstractInterpreter might insert lattice
# layers between us and `ConstsLattice`.
wl = widenlattice(lattice)
aps && (typea = widenlattice(wl, typea))
bps && (typeb = widenlattice(wl, typeb))
# type-lattice for PartialOpaque wrapper
apo = isa(typea, PartialOpaque)
bpo = isa(typeb, PartialOpaque)
if apo && bpo
aty = widenconst(typea)
bty = widenconst(typeb)
if aty == bty
if !(typea.source === typeb.source &&
typea.parent === typeb.parent)
return widenconst(typea)
end
return PartialOpaque(typea.typ, tmerge(lattice, typea.env, typeb.env),
typea.parent, typea.source)
end
typea = aty
typeb = bty
elseif apo
typea = widenlattice(wl, typea)
elseif bpo
typeb = widenlattice(wl, typeb)
end
return tmerge(wl, typea, typeb)
end
@nospecializeinfer function tmerge(lattice::ConstsLattice, @nospecialize(typea), @nospecialize(typeb))
acp = isa(typea, Const) || isa(typea, PartialTypeVar)
bcp = isa(typeb, Const) || isa(typeb, PartialTypeVar)
if acp && bcp
typea === typeb && return typea
end
wl = widenlattice(lattice)
acp && (typea = widenlattice(wl, typea))
bcp && (typeb = widenlattice(wl, typeb))
return tmerge(wl, typea, typeb)
end
@nospecializeinfer function tmerge(lattice::JLTypeLattice, @nospecialize(typea::Type), @nospecialize(typeb::Type))
# it's always ok to form a Union of two concrete types
act = isconcretetype(typea)
bct = isconcretetype(typeb)
if act && bct
# Extra fast path for pointer-egal concrete types
(pointer_from_objref(typea) === pointer_from_objref(typeb)) && return typea
end
if (act || isType(typea)) && (bct || isType(typeb))
return Union{typea, typeb}
end
u = tmerge_fast_path(lattice, typea, typeb)
u === nothing || return u
return tmerge_types_slow(typea, typeb)
end
@nospecializeinfer @noinline function tname_intersect(aname::Core.TypeName, bname::Core.TypeName)
aname === bname && return aname
if !isabstracttype(aname.wrapper) && !isabstracttype(bname.wrapper)
return nothing # fast path
end
Any.name === aname && return aname
a = unwrap_unionall(aname.wrapper)
heighta = 0
while a !== Any
heighta += 1
a = a.super
end
b = unwrap_unionall(bname.wrapper)
heightb = 0
while b !== Any
b.name === aname && return aname
heightb += 1
b = b.super
end
a = unwrap_unionall(aname.wrapper)
while heighta > heightb
a = a.super
heighta -= 1
end
return a.name === bname ? bname : nothing
end
@nospecializeinfer @noinline function tmerge_types_slow(@nospecialize(typea::Type), @nospecialize(typeb::Type))
# collect the list of types from past tmerge calls returning Union
# and then reduce over that list
types = Any[]
_uniontypes(typea, types)
_uniontypes(typeb, types)
typenames = Vector{Core.TypeName}(undef, length(types))
for i in 1:length(types)
# check that we will be able to analyze (and simplify) everything
# bail if everything isn't a well-formed DataType
ti = types[i]
uw = unwrap_unionall(ti)
uw isa DataType || return Any
ti <: uw.name.wrapper || return Any
typenames[i] = uw.name
end
u = Union{types...}
if issimpleenoughtype(u)
return u
end
# see if any of the union elements have the same TypeName
# in which case, simplify this tmerge by replacing it with
# the widest possible version of itself (the wrapper)
simplify = falses(length(types))
for i in 1:length(types)
typenames[i] === Any.name && continue
ti = types[i]
for j in (i + 1):length(types)
typenames[j] === Any.name && continue
ijname = tname_intersect(typenames[i], typenames[j])
if !(ijname === nothing)
tj = types[j]
if ti <: tj
types[i] = Union{}
typenames[i] = Any.name
simplify[i] = false
simplify[j] = true
break
elseif tj <: ti
types[j] = Union{}
typenames[j] = Any.name
simplify[j] = false
simplify[i] = true
else
if ijname === Tuple.name
# try to widen Tuple slower: make a single non-concrete Tuple containing both
# converge the Tuple element-wise if they are the same length
# see 4ee2b41552a6bc95465c12ca66146d69b354317b, be59686f7613a2ccfd63491c7b354d0b16a95c05,
widen = tuplemerge(unwrap_unionall(ti)::DataType, unwrap_unionall(tj)::DataType)
widen = rewrap_unionall(rewrap_unionall(widen, ti), tj)
simplify[j] = false
else
wr = ijname.wrapper
uw = unwrap_unionall(wr)::DataType
ui = unwrap_unionall(ti)::DataType
while ui.name !== ijname
ui = ui.super
end
uj = unwrap_unionall(tj)::DataType
while uj.name !== ijname
uj = uj.super
end
p = Vector{Any}(undef, length(uw.parameters))
usep = true
widen = wr
for k = 1:length(uw.parameters)
ui_k = ui.parameters[k]
if ui_k === uj.parameters[k] && !has_free_typevars(ui_k)
p[k] = ui_k
usep = true
else
p[k] = uw.parameters[k]
end
end
if usep
widen = rewrap_unionall(wr{p...}, wr)
widen <: wr || (widen = wr) # sometimes there are cross-constraints on wr that we may lose in this process, but that would cause future calls to this to need to return Any, which is undesirable
end
simplify[j] = !usep
end
types[i] = Union{}
typenames[i] = Any.name
simplify[i] = false
types[j] = widen
break
end
end
end
end
# don't let elements of the union get too big, if the above didn't reduce something enough
# Specifically widen Tuple{..., Union{lots of stuff}...} to Tuple{..., Any, ...}
# Don't let Val{<:Val{<:Val}} keep nesting abstract levels either
for i in 1:length(types)
simplify[i] || continue
ti = types[i]
issimpleenoughtype(ti) && continue
if typenames[i] === Tuple.name
# otherwise we need to do a simple version of tuplemerge for one element now
tip = (unwrap_unionall(ti)::DataType).parameters
lt = length(tip)
p = Vector{Any}(undef, lt)
for j = 1:lt
ui = tip[j]
p[j] = issimpleenoughtupleelem(unwrapva(ui)) ? ui : isvarargtype(ui) ? Vararg : Any
end
types[i] = rewrap_unionall(Tuple{p...}, ti)
else
# this element is not simple enough yet, make it so now
types[i] = typenames[i].wrapper
end
end
u = Union{types...}
return u
end
# the inverse of switchtupleunion, with limits on max element union size
function tuplemerge(a::DataType, b::DataType)
@assert a.name === b.name === Tuple.name "assertion failure"
ap, bp = a.parameters, b.parameters
lar = length(ap)::Int
lbr = length(bp)::Int
va = lar > 0 && isvarargtype(ap[lar])
vb = lbr > 0 && isvarargtype(bp[lbr])
if lar == lbr && !va && !vb
lt = lar
vt = false
else
lt = 0 # or min(lar - va, lbr - vb)
vt = true
end
# combine the common elements
p = Vector{Any}(undef, lt + vt)
for i = 1:lt
ui = Union{ap[i], bp[i]}
p[i] = issimpleenoughtupleelem(ui) ? ui : Any
end
# merge the remaining tail into a single, simple Tuple{Vararg{T}} (#22120)
if vt
tail = Union{}
for loop_b = (false, true)
for i = (lt + 1):(loop_b ? lbr : lar)
ti = unwrapva(loop_b ? bp[i] : ap[i])
while ti isa TypeVar
ti = ti.ub
end
# compare (ti <-> tail), (wrapper ti <-> tail), (ti <-> wrapper tail), then (wrapper ti <-> wrapper tail)
# until we find the first element that contains the other in the pair
# TODO: this result would be more stable (and more associative and more commutative)
# if we either joined all of the element wrappers first into a wide-tail, then picked between that or an exact tail,
# or (equivalently?) iteratively took super-types until reaching a common wrapper
# e.g. consider the results of `tuplemerge(Tuple{Complex}, Tuple{Number, Int})` and of
# `tuplemerge(Tuple{Int}, Tuple{String}, Tuple{Int, String})`
# c.f. tname_intersect in the algorithm above
hasfree = has_free_typevars(ti)
if hasfree || !(ti <: tail)
if !hasfree && tail <: ti
tail = ti # widen to ti
else
uw = unwrap_unionall(tail)
if uw isa DataType && tail <: uw.name.wrapper
# widen tail to wrapper(tail)
tail = uw.name.wrapper
if !(ti <: tail)
#assert !(tail <: ti)
uw = unwrap_unionall(ti)
if uw isa DataType && ti <: uw.name.wrapper
# widen ti to wrapper(ti)
ti = uw.name.wrapper
#assert !(ti <: tail)
if tail <: ti
tail = ti
else
tail = Any # couldn't find common super-type
end
else
tail = Any # couldn't analyze type
end
end
else
tail = Any # couldn't analyze type
end
end
end
tail === Any && return Tuple # short-circuit loops
end
end
@assert !(tail === Union{})
if !issimpleenoughtupleelem(tail) || tail === Any
p[lt + 1] = Vararg
lt == 0 && return Tuple
else
p[lt + 1] = Vararg{tail}
end
end
return Tuple{p...}
end