diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 397c185e36d67..bfddc2d6927b3 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -485,7 +485,10 @@ function abstract_call_method(interp::AbstractInterpreter, return MethodCallResult(Any, false, false, nothing, Effects()) end sigtuple = unwrap_unionall(sig) - sigtuple isa DataType || return MethodCallResult(Any, false, false, nothing, Effects()) + sigtuple isa DataType || + return MethodCallResult(Any, false, false, nothing, Effects()) + all(@nospecialize(x) -> valid_as_lattice(unwrapva(x), true), sigtuple.parameters) || + return MethodCallResult(Union{}, false, false, nothing, EFFECTS_THROWS) # catch bad type intersections early if is_nospecializeinfer(method) sig = get_nospecializeinfer_sig(method, sig, sparams) @@ -1365,25 +1368,35 @@ function precise_container_type(interp::AbstractInterpreter, @nospecialize(itft) end if isa(tti, Union) utis = uniontypes(tti) - if any(@nospecialize(t) -> !isa(t, DataType) || !(t <: Tuple) || !isknownlength(t), utis) - return AbstractIterationResult(Any[Vararg{Any}], nothing, Effects()) - end - ltp = length((utis[1]::DataType).parameters) - for t in utis - if length((t::DataType).parameters) != ltp - return AbstractIterationResult(Any[Vararg{Any}], nothing) + # refine the Union to remove elements that are not valid tags for objects + filter!(@nospecialize(x) -> valid_as_lattice(x, true), utis) + if length(utis) == 0 + return AbstractIterationResult(Any[], nothing) # oops, this statement was actually unreachable + elseif length(utis) == 1 + tti = utis[1] + tti0 = rewrap_unionall(tti, tti0) + else + if any(@nospecialize(t) -> !isa(t, DataType) || !(t <: Tuple) || !isknownlength(t), utis) + return AbstractIterationResult(Any[Vararg{Any}], nothing, Effects()) end - end - result = Any[ Union{} for _ in 1:ltp ] - for t in utis - tps = (t::DataType).parameters - _all(valid_as_lattice, tps) || continue - for j in 1:ltp - result[j] = tmerge(result[j], rewrap_unionall(tps[j], tti0)) + ltp = length((utis[1]::DataType).parameters) + for t in utis + if length((t::DataType).parameters) != ltp + return AbstractIterationResult(Any[Vararg{Any}], nothing) + end + end + result = Any[ Union{} for _ in 1:ltp ] + for t in utis + tps = (t::DataType).parameters + for j in 1:ltp + @assert valid_as_lattice(tps[j], true) + result[j] = tmerge(result[j], rewrap_unionall(tps[j], tti0)) + end end + return AbstractIterationResult(result, nothing) end - return AbstractIterationResult(result, nothing) - elseif tti0 <: Tuple + end + if tti0 <: Tuple if isa(tti0, DataType) return AbstractIterationResult(Any[ p for p in tti0.parameters ], nothing) elseif !isa(tti, DataType) @@ -1647,7 +1660,7 @@ end return isa_condition(xt, ty, max_union_splitting) end @inline function isa_condition(@nospecialize(xt), @nospecialize(ty), max_union_splitting::Int) - tty_ub, isexact_tty = instanceof_tfunc(ty) + tty_ub, isexact_tty = instanceof_tfunc(ty, true) tty = widenconst(xt) if isexact_tty && !isa(tty_ub, TypeVar) tty_lb = tty_ub # TODO: this would be wrong if !isexact_tty, but instanceof_tfunc doesn't preserve this info @@ -1657,7 +1670,7 @@ end # `typeintersect` may be unable narrow down `Type`-type thentype = tty_ub end - valid_as_lattice(thentype) || (thentype = Bottom) + valid_as_lattice(thentype, true) || (thentype = Bottom) elsetype = typesubtract(tty, tty_lb, max_union_splitting) return ConditionalTypes(thentype, elsetype) end @@ -1903,7 +1916,7 @@ function abstract_invoke(interp::AbstractInterpreter, (; fargs, argtypes)::ArgIn ft′ = argtype_by_index(argtypes, 2) ft = widenconst(ft′) ft === Bottom && return CallMeta(Bottom, EFFECTS_THROWS, NoCallInfo()) - (types, isexact, isconcrete, istype) = instanceof_tfunc(argtype_by_index(argtypes, 3)) + (types, isexact, isconcrete, istype) = instanceof_tfunc(argtype_by_index(argtypes, 3), false) isexact || return CallMeta(Any, Effects(), NoCallInfo()) unwrapped = unwrap_unionall(types) if types === Bottom || !(unwrapped isa DataType) || unwrapped.name !== Tuple.name @@ -2322,7 +2335,7 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp (; rt, effects) = abstract_eval_call(interp, e, vtypes, sv) t = rt elseif ehead === :new - t, isexact = instanceof_tfunc(abstract_eval_value(interp, e.args[1], vtypes, sv)) + t, isexact = instanceof_tfunc(abstract_eval_value(interp, e.args[1], vtypes, sv), true) ut = unwrap_unionall(t) consistent = noub = ALWAYS_FALSE nothrow = false @@ -2387,7 +2400,7 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp end effects = Effects(EFFECTS_TOTAL; consistent, nothrow, noub) elseif ehead === :splatnew - t, isexact = instanceof_tfunc(abstract_eval_value(interp, e.args[1], vtypes, sv)) + t, isexact = instanceof_tfunc(abstract_eval_value(interp, e.args[1], vtypes, sv), true) nothrow = false # TODO: More precision if length(e.args) == 2 && isconcretedispatch(t) && !ismutabletype(t) at = abstract_eval_value(interp, e.args[2], vtypes, sv) diff --git a/base/compiler/abstractlattice.jl b/base/compiler/abstractlattice.jl index 98a53e7283cbc..c1229124d1cec 100644 --- a/base/compiler/abstractlattice.jl +++ b/base/compiler/abstractlattice.jl @@ -98,8 +98,10 @@ is_valid_lattice_norec(::InferenceLattice, @nospecialize(elem)) = isa(elem, Limi """ tmeet(𝕃::AbstractLattice, a, b::Type) -Compute the lattice meet of lattice elements `a` and `b` over the lattice `𝕃`. -If `𝕃` is `JLTypeLattice`, this is equivalent to type intersection. +Compute the lattice meet of lattice elements `a` and `b` over the lattice `𝕃`, +dropping any results that will not be inhabited at runtime. +If `𝕃` is `JLTypeLattice`, this is equivalent to type intersection plus the +elimination of results that have no concrete subtypes. Note that currently `b` is restricted to being a type (interpreted as a lattice element in the `JLTypeLattice` sub-lattice of `𝕃`). """ @@ -107,7 +109,7 @@ function tmeet end function tmeet(::JLTypeLattice, @nospecialize(a::Type), @nospecialize(b::Type)) ti = typeintersect(a, b) - valid_as_lattice(ti) || return Bottom + valid_as_lattice(ti, true) || return Bottom return ti end diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index 790e1b4e022d5..fb57b06d75dbc 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -243,7 +243,7 @@ function new_expr_effect_flags(𝕃ₒ::AbstractLattice, args::Vector{Any}, src: Targ = args[1] atyp = argextype(Targ, src) # `Expr(:new)` of unknown type could raise arbitrary TypeError. - typ, isexact = instanceof_tfunc(atyp) + typ, isexact = instanceof_tfunc(atyp, true) if !isexact atyp = unwrap_unionall(widenconst(atyp)) if isType(atyp) && isTypeDataType(atyp.parameters[1]) @@ -335,7 +335,7 @@ function stmt_effect_flags(𝕃ₒ::AbstractLattice, @nospecialize(stmt), @nospe elseif head === :new_opaque_closure length(args) < 4 && return (false, false, false) typ = argextype(args[1], src) - typ, isexact = instanceof_tfunc(typ) + typ, isexact = instanceof_tfunc(typ, true) isexact || return (false, false, false) ⊑(𝕃ₒ, typ, Tuple) || return (false, false, false) rt_lb = argextype(args[2], src) diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index 657937c9168ac..0a5b5c6580595 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -1200,7 +1200,7 @@ function handle_invoke_call!(todo::Vector{Pair{Int,Any}}, end function invoke_signature(argtypes::Vector{Any}) - ft, argtyps = widenconst(argtypes[2]), instanceof_tfunc(widenconst(argtypes[3]))[1] + ft, argtyps = widenconst(argtypes[2]), instanceof_tfunc(widenconst(argtypes[3]), false)[1] return rewrap_unionall(Tuple{ft, unwrap_unionall(argtyps).parameters...}, argtyps) end diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index 428909e2792ea..af3d91d8b69f4 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -1756,7 +1756,7 @@ function adce_pass!(ir::IRCode, inlining::Union{Nothing,InliningState}=nothing) else if is_known_call(stmt, typeassert, compact) && length(stmt.args) == 3 # nullify safe `typeassert` calls - ty, isexact = instanceof_tfunc(argextype(stmt.args[3], compact)) + ty, isexact = instanceof_tfunc(argextype(stmt.args[3], compact), true) if isexact && ⊑(𝕃ₒ, argextype(stmt.args[2], compact), ty) compact[idx] = nothing continue diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 2f0b6711cf995..2104686dca566 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -95,9 +95,9 @@ add_tfunc(throw, 1, 1, @nospecs((𝕃::AbstractLattice, x)->Bottom), 0) # if isexact is false, the actual runtime type may (will) be a subtype of t # if isconcrete is true, the actual runtime type is definitely concrete (unreachable if not valid as a typeof) # if istype is true, the actual runtime value will definitely be a type (e.g. this is false for Union{Type{Int}, Int}) -function instanceof_tfunc(@nospecialize(t)) +function instanceof_tfunc(@nospecialize(t), astag::Bool=false) if isa(t, Const) - if isa(t.val, Type) && valid_as_lattice(t.val) + if isa(t.val, Type) && valid_as_lattice(t.val, astag) return t.val, true, isconcretetype(t.val), true end return Bottom, true, false, false # runtime throws on non-Type @@ -109,11 +109,11 @@ function instanceof_tfunc(@nospecialize(t)) return Bottom, true, false, false # literal Bottom or non-Type elseif isType(t) tp = t.parameters[1] - valid_as_lattice(tp) || return Bottom, true, false, false # runtime unreachable / throws on non-Type + valid_as_lattice(tp, astag) || return Bottom, true, false, false # runtime unreachable / throws on non-Type return tp, !has_free_typevars(tp), isconcretetype(tp), true elseif isa(t, UnionAll) t′ = unwrap_unionall(t) - t′′, isexact, isconcrete, istype = instanceof_tfunc(t′) + t′′, isexact, isconcrete, istype = instanceof_tfunc(t′, astag) tr = rewrap_unionall(t′′, t) if t′′ isa DataType && t′′.name !== Tuple.name && !has_free_typevars(tr) # a real instance must be within the declared bounds of the type, @@ -128,8 +128,8 @@ function instanceof_tfunc(@nospecialize(t)) end return tr, isexact, isconcrete, istype elseif isa(t, Union) - ta, isexact_a, isconcrete_a, istype_a = instanceof_tfunc(t.a) - tb, isexact_b, isconcrete_b, istype_b = instanceof_tfunc(t.b) + ta, isexact_a, isconcrete_a, istype_a = instanceof_tfunc(t.a, astag) + tb, isexact_b, isconcrete_b, istype_b = instanceof_tfunc(t.b, astag) isconcrete = isconcrete_a && isconcrete_b istype = istype_a && istype_b # most users already handle the Union case, so here we assume that @@ -149,9 +149,9 @@ end # ---------- @nospecs bitcast_tfunc(𝕃::AbstractLattice, t, x) = bitcast_tfunc(widenlattice(𝕃), t, x) -@nospecs bitcast_tfunc(::JLTypeLattice, t, x) = instanceof_tfunc(t)[1] +@nospecs bitcast_tfunc(::JLTypeLattice, t, x) = instanceof_tfunc(t, true)[1] @nospecs conversion_tfunc(𝕃::AbstractLattice, t, x) = conversion_tfunc(widenlattice(𝕃), t, x) -@nospecs conversion_tfunc(::JLTypeLattice, t, x) = instanceof_tfunc(t)[1] +@nospecs conversion_tfunc(::JLTypeLattice, t, x) = instanceof_tfunc(t, true)[1] add_tfunc(bitcast, 2, 2, bitcast_tfunc, 1) add_tfunc(sext_int, 2, 2, conversion_tfunc, 1) @@ -291,7 +291,7 @@ add_tfunc(checked_umul_int, 2, 2, chk_tfunc, 10) # ----------- @nospecs function llvmcall_tfunc(𝕃::AbstractLattice, fptr, rt, at, a...) - return instanceof_tfunc(rt)[1] + return instanceof_tfunc(rt, true)[1] end add_tfunc(Core.Intrinsics.llvmcall, 3, INT_INF, llvmcall_tfunc, 10) @@ -461,7 +461,7 @@ function sizeof_nothrow(@nospecialize(x)) return sizeof_nothrow(rewrap_unionall(xu.a, x)) && sizeof_nothrow(rewrap_unionall(xu.b, x)) end - t, exact, isconcrete = instanceof_tfunc(x) + t, exact, isconcrete = instanceof_tfunc(x, false) if t === Bottom # x must be an instance (not a Type) or is the Bottom type object x = widenconst(x) @@ -513,7 +513,7 @@ end end # Core.sizeof operates on either a type or a value. First check which # case we're in. - t, exact = instanceof_tfunc(x) + t, exact = instanceof_tfunc(x, false) if t !== Bottom # The value corresponding to `x` at runtime could be a type. # Normalize the query to ask about that type. @@ -665,7 +665,7 @@ function pointer_eltype(@nospecialize(ptr)) unw = unwrap_unionall(a) if isa(unw, DataType) && unw.name === Ptr.body.name T = unw.parameters[1] - valid_as_lattice(T) || return Bottom + valid_as_lattice(T, true) || return Bottom return rewrap_unionall(T, a) end end @@ -697,7 +697,7 @@ end if isa(unw, DataType) && unw.name === Ptr.body.name T = unw.parameters[1] # note: we could sometimes refine this to a PartialStruct if we analyzed `op(T, T)::T` - valid_as_lattice(T) || return Bottom + valid_as_lattice(T, true) || return Bottom return rewrap_unionall(Pair{T, T}, a) end end @@ -709,7 +709,7 @@ end unw = unwrap_unionall(a) if isa(unw, DataType) && unw.name === Ptr.body.name T = unw.parameters[1] - valid_as_lattice(T) || return Bottom + valid_as_lattice(T, true) || return Bottom return rewrap_unionall(ccall(:jl_apply_cmpswap_type, Any, (Any,), T), a) end end @@ -817,7 +817,7 @@ end add_tfunc(typeof, 1, 1, typeof_tfunc, 1) @nospecs function typeassert_tfunc(𝕃::AbstractLattice, v, t) - t = instanceof_tfunc(t)[1] + t = instanceof_tfunc(t, true)[1] t === Any && return v return tmeet(𝕃, v, t) end @@ -825,7 +825,7 @@ add_tfunc(typeassert, 2, 2, typeassert_tfunc, 4) @nospecs function typeassert_nothrow(𝕃::AbstractLattice, v, t) ⊑ = Core.Compiler.:⊑(𝕃) - # ty, exact = instanceof_tfunc(t) + # ty, exact = instanceof_tfunc(t, true) # return exact && v ⊑ ty if (isType(t) && !has_free_typevars(t) && v ⊑ t.parameters[1]) || (isa(t, Const) && isa(t.val, Type) && v ⊑ t.val) @@ -835,7 +835,7 @@ add_tfunc(typeassert, 2, 2, typeassert_tfunc, 4) end @nospecs function isa_tfunc(𝕃::AbstractLattice, v, tt) - t, isexact = instanceof_tfunc(tt) + t, isexact = instanceof_tfunc(tt, true) if t === Bottom # check if t could be equivalent to typeof(Bottom), since that's valid in `isa`, but the set of `v` is empty # if `t` cannot have instances, it's also invalid on the RHS of isa @@ -875,8 +875,8 @@ add_tfunc(isa, 2, 2, isa_tfunc, 1) end @nospecs function subtype_tfunc(𝕃::AbstractLattice, a, b) - a, isexact_a = instanceof_tfunc(a) - b, isexact_b = instanceof_tfunc(b) + a, isexact_a = instanceof_tfunc(a, false) + b, isexact_b = instanceof_tfunc(b, false) if !has_free_typevars(a) && !has_free_typevars(b) if a <: b if isexact_b || a === Bottom @@ -1223,31 +1223,36 @@ end return Bottom end if nf == 1 - return rewrap_unionall(unwrapva(ftypes[1]), s00) - end - # union together types of all fields - t = Bottom - for i in 1:nf - _ft = ftypes[i] - setfield && isconst(s, i) && continue - t = tmerge(t, rewrap_unionall(unwrapva(_ft), s00)) - t === Any && break + fld = 1 + else + # union together types of all fields + t = Bottom + for i in 1:nf + _ft = unwrapva(ftypes[i]) + valid_as_lattice(_ft, true) || continue + setfield && isconst(s, i) && continue + t = tmerge(t, rewrap_unionall(_ft, s00)) + t === Any && break + end + return t end - return t + else + fld = _getfield_fieldindex(s, name) + fld === nothing && return Bottom end - fld = _getfield_fieldindex(s, name) - fld === nothing && return Bottom if s <: Tuple && fld >= nf && isvarargtype(ftypes[nf]) - return rewrap_unionall(unwrapva(ftypes[nf]), s00) - end - if fld < 1 || fld > nf - return Bottom - elseif setfield && isconst(s, fld) - return Bottom - end - R = ftypes[fld] - if isempty(s.parameters) - return R + R = unwrapva(ftypes[nf]) + else + if fld < 1 || fld > nf + return Bottom + elseif setfield && isconst(s, fld) + return Bottom + end + R = ftypes[fld] + valid_as_lattice(R, true) || return Bottom + if isempty(s.parameters) + return R + end end return rewrap_unionall(R, s00) end @@ -1382,7 +1387,7 @@ end T = _fieldtype_tfunc(𝕃, o, f, isconcretetype(o)) T === Bottom && return Bottom PT = Const(Pair) - return instanceof_tfunc(apply_type_tfunc(𝕃, PT, T, T))[1] + return instanceof_tfunc(apply_type_tfunc(𝕃, PT, T, T), true)[1] end function abstract_modifyfield!(interp::AbstractInterpreter, argtypes::Vector{Any}, si::StmtInfo, sv::AbsIntState) nargs = length(argtypes) @@ -1424,7 +1429,7 @@ end T = _fieldtype_tfunc(𝕃, o, f, isconcretetype(o)) T === Bottom && return Bottom PT = Const(ccall(:jl_apply_cmpswap_type, Any, (Any,), T) where T) - return instanceof_tfunc(apply_type_tfunc(𝕃, PT, T))[1] + return instanceof_tfunc(apply_type_tfunc(𝕃, PT, T), true)[1] end # we could use tuple_tfunc instead of widenconst, but `o` is mutable, so that is unlikely to be beneficial @@ -1456,7 +1461,7 @@ add_tfunc(replacefield!, 4, 6, replacefield!_tfunc, 3) fieldtype_nothrow(𝕃, rewrap_unionall(su.b, s0), name) end - s, exact = instanceof_tfunc(s0) + s, exact = instanceof_tfunc(s0, false) s === Bottom && return false # always return _fieldtype_nothrow(s, exact, name) end @@ -1521,7 +1526,7 @@ end fieldtype_tfunc(𝕃, rewrap_unionall(su.b, s0), name)) end - s, exact = instanceof_tfunc(s0) + s, exact = instanceof_tfunc(s0, false) s === Bottom && return Bottom return _fieldtype_tfunc(𝕃, s, name, exact) end @@ -1534,8 +1539,8 @@ end tb0 = _fieldtype_tfunc(𝕃, rewrap_unionall(u.b, s), name, exact) ta0 ⊑ tb0 && return tb0 tb0 ⊑ ta0 && return ta0 - ta, exacta, _, istypea = instanceof_tfunc(ta0) - tb, exactb, _, istypeb = instanceof_tfunc(tb0) + ta, exacta, _, istypea = instanceof_tfunc(ta0, false) + tb, exactb, _, istypeb = instanceof_tfunc(tb0, false) if exact && exacta && exactb return Const(Union{ta, tb}) end @@ -1669,7 +1674,7 @@ function apply_type_nothrow(𝕃::AbstractLattice, argtypes::Vector{Any}, @nospe return false end else - T, exact, _, istype = instanceof_tfunc(ai) + T, exact, _, istype = instanceof_tfunc(ai, false) if T === Bottom if !(u.var.lb === Union{} && u.var.ub === Any) return false @@ -1733,9 +1738,7 @@ const _tvarnames = Symbol[:_A, :_B, :_C, :_D, :_E, :_F, :_G, :_H, :_I, :_J, :_K, end end if largs == 1 # Union{T} --> T - u1 = typeintersect(widenconst(args[1]), Union{Type,TypeVar}) - valid_as_lattice(u1) || return Bottom - return u1 + return tmeet(widenconst(args[1]), Union{Type,TypeVar}) end hasnonType && return Type ty = Union{} @@ -1820,7 +1823,7 @@ const _tvarnames = Symbol[:_A, :_B, :_C, :_D, :_E, :_F, :_G, :_H, :_I, :_J, :_K, elseif !isT # if we didn't have isType to compute ub directly, try to use instanceof_tfunc to refine this guess ai_w = widenconst(ai) - ub = ai_w isa Type && ai_w <: Type ? instanceof_tfunc(ai)[1] : Any + ub = ai_w isa Type && ai_w <: Type ? instanceof_tfunc(ai, false)[1] : Any end if istuple # in the last parameter of a Tuple type, if the upper bound is Any @@ -2019,7 +2022,7 @@ function array_elmtype(@nospecialize ary) end if isa(a, DataType) T = a.parameters[1] - valid_as_lattice(T) || return Bottom + valid_as_lattice(T, true) || return Bottom return rewrap_unionall(T, a0) end end @@ -2571,7 +2574,7 @@ function intrinsic_nothrow(f::IntrinsicFunction, argtypes::Vector{Any}) return argtypes[1] ⊑ Array end if f === Intrinsics.bitcast - ty, isexact, isconcrete = instanceof_tfunc(argtypes[1]) + ty, isexact, isconcrete = instanceof_tfunc(argtypes[1], true) xty = widenconst(argtypes[2]) return isconcrete && isprimitivetype(ty) && isprimitivetype(xty) && Core.sizeof(ty) === Core.sizeof(xty) end @@ -2580,12 +2583,12 @@ function intrinsic_nothrow(f::IntrinsicFunction, argtypes::Vector{Any}) Intrinsics.sitofp, Intrinsics.fptrunc, Intrinsics.fpext) # If !isconcrete, `ty` may be Union{} at runtime even if we have # isprimitivetype(ty). - ty, isexact, isconcrete = instanceof_tfunc(argtypes[1]) + ty, isexact, isconcrete = instanceof_tfunc(argtypes[1], true) xty = widenconst(argtypes[2]) return isconcrete && isprimitivetype(ty) && isprimitivetype(xty) end if f === Intrinsics.have_fma - ty, isexact, isconcrete = instanceof_tfunc(argtypes[1]) + ty, isexact, isconcrete = instanceof_tfunc(argtypes[1], true) return isconcrete && isprimitivetype(ty) end # The remaining intrinsics are math/bits/comparison intrinsics. They work on all @@ -2775,7 +2778,7 @@ function _hasmethod_tfunc(interp::AbstractInterpreter, argtypes::Vector{Any}, sv else return CallMeta(Any, Effects(), NoCallInfo()) end - (types, isexact, isconcrete, istype) = instanceof_tfunc(argtype_by_index(argtypes, typeidx)) + (types, isexact, isconcrete, istype) = instanceof_tfunc(argtype_by_index(argtypes, typeidx), false) isexact || return CallMeta(Bool, Effects(), NoCallInfo()) unwrapped = unwrap_unionall(types) if types === Bottom || !(unwrapped isa DataType) || unwrapped.name !== Tuple.name diff --git a/base/compiler/typelattice.jl b/base/compiler/typelattice.jl index 324f2b600cc44..df6022609d612 100644 --- a/base/compiler/typelattice.jl +++ b/base/compiler/typelattice.jl @@ -607,7 +607,7 @@ end if ti === widev return v end - valid_as_lattice(ti) || return Bottom + valid_as_lattice(ti, true) || return Bottom if widev <: Tuple new_fields = Vector{Any}(undef, length(v.fields)) for i = 1:length(new_fields) @@ -631,7 +631,7 @@ end return v end ti = typeintersect(widev, t) - valid_as_lattice(ti) || return Bottom + valid_as_lattice(ti, true) || return Bottom return PartialOpaque(ti, v.env, v.parent, v.source) end return tmeet(widenlattice(lattice), v, t) diff --git a/base/compiler/typeutils.jl b/base/compiler/typeutils.jl index 22952961b2484..a4499e003cf2c 100644 --- a/base/compiler/typeutils.jl +++ b/base/compiler/typeutils.jl @@ -95,12 +95,13 @@ end has_concrete_subtype(d::DataType) = d.flags & 0x0020 == 0x0020 # n.b. often computed only after setting the type and layout fields -# determine whether x is a valid lattice element tag +# determine whether x is a valid lattice element # For example, Type{v} is not valid if v is a value -# Accepts TypeVars also, since it assumes the user will rewrap it correctly -function valid_as_lattice(@nospecialize(x)) +# Accepts TypeVars and has_free_typevar also, since it assumes the user will rewrap it correctly +# If astag is true, then also requires that it be a possible type tag for a valid object +function valid_as_lattice(@nospecialize(x), astag::Bool=false) x === Bottom && false - x isa TypeVar && return valid_as_lattice(x.ub) + x isa TypeVar && return valid_as_lattice(x.ub, astag) x isa UnionAll && (x = unwrap_unionall(x)) if x isa Union # the Union constructor ensures this (and we'll recheck after @@ -111,6 +112,9 @@ function valid_as_lattice(@nospecialize(x)) if isType(x) p = x.parameters[1] p isa Type || p isa TypeVar || return false + elseif astag && isstructtype(x) + datatype_fieldtypes(x) # force computation of has_concrete_subtype to be updated now + return has_concrete_subtype(x) end return true end @@ -149,6 +153,7 @@ function compatible_vatuple(a::DataType, b::DataType) end # return an upper-bound on type `a` with type `b` removed +# and also any contents that are not valid type tags on any objects # such that `return <: a` && `Union{return, b} == Union{a, b}` function typesubtract(@nospecialize(a), @nospecialize(b), max_union_splitting::Int) if a <: b && isnotbrokensubtype(a, b) @@ -158,8 +163,8 @@ function typesubtract(@nospecialize(a), @nospecialize(b), max_union_splitting::I if isa(ua, Union) uua = typesubtract(rewrap_unionall(ua.a, a), b, max_union_splitting) uub = typesubtract(rewrap_unionall(ua.b, a), b, max_union_splitting) - return Union{valid_as_lattice(uua) ? uua : Union{}, - valid_as_lattice(uub) ? uub : Union{}} + return Union{valid_as_lattice(uua, true) ? uua : Union{}, + valid_as_lattice(uub, true) ? uub : Union{}} elseif a isa DataType ub = unwrap_unionall(b) if ub isa DataType diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index e464ed7994f04..52309744b4a05 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -5256,3 +5256,24 @@ use_vararg_constrant3(args::NTuple{N,T}) where {T,N} = Val(T), Val(N) @test only(Base.return_types(use_vararg_constrant3, Tuple{Tuple{Int,Int}})) == Tuple{Val{Int},Val{2}} use_vararg_constrant4(args::NTuple{N,T}) where {T,N} = Val(T), N @test only(Base.return_types(use_vararg_constrant4, Tuple{NTuple{N,Int}} where N)) == Tuple{Val{Int},Int} + +# issue 51228 +global whatever_unknown_value51228 +f51228() = f51228(whatever_unknown_value51228) +f51228(x) = 1 +f51228(::Vararg{T,T}) where {T} = "2" +@test only(Base.return_types(f51228, ())) == Int + +struct A51317 + b::Tuple{1} + A1() = new() +end +struct An51317 + a::Int + b::Tuple{1} + An51317() = new() +end +@test only(Base.return_types((x,f) -> getfield(x, f), (A51317, Symbol))) === Union{} +@test only(Base.return_types((x,f) -> getfield(x, f), (An51317, Symbol))) === Int +@test only(Base.return_types(x -> getfield(x, :b), (A51317,))) === Union{} +@test only(Base.return_types(x -> getfield(x, :b), (An51317,))) === Union{}