diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 26bba7b51a2dd..7bc8267f04260 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -30,12 +30,12 @@ function propagate_conditional(rt::InterConditional, cond::Conditional) new_elsetype = rt.elsetype === Const(true) ? cond.thentype : cond.elsetype if rt.thentype == Bottom @assert rt.elsetype != Bottom - return Conditional(cond.slot, Bottom, new_elsetype) + return Conditional(cond.slot, Bottom, new_elsetype, cond.from_ssa) elseif rt.elsetype == Bottom @assert rt.thentype != Bottom - return Conditional(cond.slot, new_thentype, Bottom) + return Conditional(cond.slot, new_thentype, Bottom, cond.from_ssa) end - return Conditional(cond.slot, new_thentype, new_elsetype) + return Conditional(cond.slot, new_thentype, new_elsetype, cond.from_ssa) end function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), @@ -512,7 +512,7 @@ function from_interconditional(๐•ƒแตข::AbstractLattice, @nospecialize(rt), sv:: if alias !== nothing return form_mustalias_conditional(alias, thentype, elsetype) end - return Conditional(slot, thentype, elsetype) # record a Conditional improvement to this slot + return Conditional(slot, thentype, elsetype, #=from_ssa=#sv.currpc) # record a Conditional improvement to this slot end return widenconditional(rt) end @@ -1430,7 +1430,7 @@ function matching_cache_argtypes(๐•ƒ::AbstractLattice, mi::MethodInstance, # TODO bail out here immediately rather than just propagating Bottom ? given_argtypes[i] = Bottom else - given_argtypes[i] = Conditional(slotid, thentype, elsetype) + given_argtypes[i] = Conditional(slotid, thentype, elsetype, #=from_ssa=#0) end continue end @@ -1929,7 +1929,7 @@ function abstract_call_builtin(interp::AbstractInterpreter, f::Builtin, (; fargs if isa(a, SlotNumber) cndt = isa_condition(a2, a3, InferenceParams(interp).max_union_splitting, rt) if cndt !== nothing - return Conditional(a, cndt.thentype, cndt.elsetype) + return Conditional(a, cndt.thentype, cndt.elsetype, #=from_ssa=#sv.currpc) end end if isa(a2, MustAlias) @@ -1947,7 +1947,7 @@ function abstract_call_builtin(interp::AbstractInterpreter, f::Builtin, (; fargs # !(x isa T) implies !(Type{a2} <: T) # TODO: complete splitting, based on which portions of the Union a3 for which isa_tfunc returns Const(true) or Const(false) instead of Bool elsetype = typesubtract(a3, Type{widenconst(a2)}, InferenceParams(interp).max_union_splitting) - return Conditional(b, a3, elsetype) + return Conditional(b, a3, elsetype, #=from_ssa=#sv.currpc) end end elseif f === (===) @@ -1959,7 +1959,7 @@ function abstract_call_builtin(interp::AbstractInterpreter, f::Builtin, (; fargs if isa(aty, Const) if isa(b, SlotNumber) cndt = egal_condition(aty, bty, InferenceParams(interp).max_union_splitting, rt) - return Conditional(b, cndt.thentype, cndt.elsetype) + return Conditional(b, cndt.thentype, cndt.elsetype, #=from_ssa=#sv.currpc) elseif isa(bty, MustAlias) && !isa(rt, Const) # skip refinement when the field is known precisely (just optimization) cndt = egal_condition(aty, bty.fldtyp, InferenceParams(interp).max_union_splitting) return form_mustalias_conditional(bty, cndt.thentype, cndt.elsetype) @@ -1967,7 +1967,7 @@ function abstract_call_builtin(interp::AbstractInterpreter, f::Builtin, (; fargs elseif isa(bty, Const) if isa(a, SlotNumber) cndt = egal_condition(bty, aty, InferenceParams(interp).max_union_splitting, rt) - return Conditional(a, cndt.thentype, cndt.elsetype) + return Conditional(a, cndt.thentype, cndt.elsetype, #=from_ssa=#sv.currpc) elseif isa(aty, MustAlias) && !isa(rt, Const) # skip refinement when the field is known precisely (just optimization) cndt = egal_condition(bty, aty.fldtyp, InferenceParams(interp).max_union_splitting) return form_mustalias_conditional(aty, cndt.thentype, cndt.elsetype) @@ -1998,18 +1998,18 @@ function abstract_call_builtin(interp::AbstractInterpreter, f::Builtin, (; fargs if isa(b, SlotNumber) thentype = rt === Const(false) ? Bottom : widenslotwrapper(bty) elsetype = rt === Const(true) ? Bottom : widenslotwrapper(bty) - return Conditional(b, thentype, elsetype) + return Conditional(b, thentype, elsetype, #=from_ssa=#sv.currpc) elseif isa(a, SlotNumber) thentype = rt === Const(false) ? Bottom : widenslotwrapper(aty) elsetype = rt === Const(true) ? Bottom : widenslotwrapper(aty) - return Conditional(a, thentype, elsetype) + return Conditional(a, thentype, elsetype, #=from_ssa=#sv.currpc) end elseif f === Core.Compiler.not_int aty = argtypes[2] if isa(aty, Conditional) thentype = rt === Const(false) ? Bottom : aty.elsetype elsetype = rt === Const(true) ? Bottom : aty.thentype - return Conditional(aty.slot, thentype, elsetype) + return Conditional(aty.slot, thentype, elsetype, #=from_ssa=#sv.currpc) end elseif f === isdefined a = ssa_def_slot(fargs[2], sv) @@ -2032,7 +2032,7 @@ function abstract_call_builtin(interp::AbstractInterpreter, f::Builtin, (; fargs elsetype = elsetype โŠ” ty end end - return Conditional(a, thentype, elsetype) + return Conditional(a, thentype, elsetype, #=from_ssa=#sv.currpc) else thentype = form_partially_defined_struct(argtype2, argtypes[3]) if thentype !== nothing @@ -2042,7 +2042,7 @@ function abstract_call_builtin(interp::AbstractInterpreter, f::Builtin, (; fargs elseif rt === Const(true) elsetype = Bottom end - return Conditional(a, thentype, elsetype) + return Conditional(a, thentype, elsetype, #=from_ssa=#sv.currpc) end end end @@ -2307,7 +2307,8 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), call = abstract_call_gf_by_type(interp, f, ArgInfo(fargs, Any[Const(f), Any, Any]), si, Tuple{typeof(f), Any, Any}, sv, max_methods) rty = abstract_call_known(interp, (===), arginfo, si, sv, max_methods).rt if isa(rty, Conditional) - return CallMeta(Conditional(rty.slot, rty.elsetype, rty.thentype), Bottom, EFFECTS_TOTAL, NoCallInfo()) # swap if-else + newrty = Conditional(rty.slot, rty.elsetype, rty.thentype, #=from_ssa=#sv.currpc) + return CallMeta(newrty, Bottom, EFFECTS_TOTAL, NoCallInfo()) # swap if-else elseif isa(rty, Const) return CallMeta(Const(rty.val === false), Bottom, EFFECTS_TOTAL, MethodResultPure()) end @@ -2552,7 +2553,7 @@ struct RTEffects end function abstract_call(interp::AbstractInterpreter, arginfo::ArgInfo, sv::InferenceState) - unused = call_result_unused(sv, sv.currpc) + unused = call_result_unused(sv, #=from_ssa=#sv.currpc) if unused add_curr_ssaflag!(sv, IR_FLAG_UNUSED) end @@ -2695,7 +2696,7 @@ function abstract_eval_new_opaque_closure(interp::AbstractInterpreter, e::Expr, rt = widenconst(rt) # Propagation of PartialOpaque disabled end - if isa(rt, PartialOpaque) && isa(sv, InferenceState) && !call_result_unused(sv, sv.currpc) + if isa(rt, PartialOpaque) && isa(sv, InferenceState) && !call_result_unused(sv, #=from_ssa=#sv.currpc) # Infer this now so that the specialization is available to # optimization. argtypes = most_general_argtypes(rt) @@ -3221,6 +3222,7 @@ end lhs = stmt.args[1] if isa(lhs, SlotNumber) changes = StateUpdate(lhs, VarState(rt, false), false) + setassignment!(frame.slotassignments, slot_id(lhs), frame.currpc) elseif isa(lhs, GlobalRef) handle_global_assignment!(interp, frame, lhs, rt) elseif !isa(lhs, SSAValue) @@ -3390,7 +3392,7 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) if !(isa(condt, Const) || isa(condt, Conditional)) && isa(condslot, SlotNumber) # if this non-`Conditional` object is a slot, we form and propagate # the conditional constraint on it - condt = Conditional(condslot, Const(true), Const(false)) + condt = Conditional(condslot, Const(true), Const(false), #=from_ssa=#frame.currpc) end condval = maybe_extract_const_bool(condt) nothrow = (condval !== nothing) || โŠ‘(๐•ƒแตข, orig_condt, Bool) @@ -3435,7 +3437,7 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) # We continue with the true branch, but process the false # branch here. - if isa(condt, Conditional) + if isa(condt, Conditional) && is_valid_conditional(condt, currpc, frame) else_change = conditional_change(๐•ƒแตข, currstate, condt, #=then_or_else=#false) if else_change !== nothing elsestate = copy(currstate) @@ -3474,14 +3476,14 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) return caller.ssavaluetypes[caller_pc] !== Any end end - ssavaluetypes[frame.currpc] = Any + ssavaluetypes[currpc] = Any @goto find_next_bb elseif isa(stmt, EnterNode) ssavaluetypes[currpc] = Any add_curr_ssaflag!(frame, IR_FLAG_NOTHROW) if isdefined(stmt, :scope) scopet = abstract_eval_value(interp, stmt.scope, currstate, frame) - handler = gethandler(frame, frame.currpc+1)::TryCatchFrame + handler = gethandler(frame, currpc+1)::TryCatchFrame @assert handler.scopet !== nothing if !โŠ‘(๐•ƒแตข, scopet, handler.scopet) handler.scopet = tmerge(๐•ƒแตข, scopet, handler.scopet) @@ -3581,6 +3583,18 @@ function apply_refinement!(๐•ƒแตข::AbstractLattice, slot::SlotNumber, @nospecia end end +function is_valid_conditional(condt::Conditional, use_ssa::Int, sv::InferenceState) + domtree = get!(sv.lazydomtree) + dominates_ssa(sv.cfg, domtree, condt.from_ssa, use_ssa) || + condt.from_ssa == use_ssa || + return false + return all(getassignment(sv.slotassignments, condt.slot)) do aidx::Int + return (aidx == condt.from_ssa == 0 || + dominates_ssa(sv.cfg, domtree, aidx, condt.from_ssa) || + dominates_ssa(sv.cfg, domtree, use_ssa, aidx)) + end +end + function conditional_change(๐•ƒแตข::AbstractLattice, currstate::VarTable, condt::Conditional, then_or_else::Bool) vtype = currstate[condt.slot] oldtyp = vtype.typ @@ -3608,7 +3622,8 @@ function condition_object_change(currstate::VarTable, condt::Conditional, vtype = currstate[slot_id(condslot)] newcondt = Conditional(condt.slot, then_or_else ? condt.thentype : Union{}, - then_or_else ? Union{} : condt.elsetype) + then_or_else ? Union{} : condt.elsetype, + #=from_ssa=#condt.from_ssa) return StateUpdate(condslot, VarState(newcondt, vtype.undef), false) end diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index 6953dea5b9bd7..3285a9dce017f 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -182,15 +182,16 @@ function get!(x::LazyCFGReachability) end mutable struct LazyGenericDomtree{IsPostDom} - ir::IRCode + cfg::CFG domtree::GenericDomTree{IsPostDom} - LazyGenericDomtree{IsPostDom}(ir::IRCode) where {IsPostDom} = new{IsPostDom}(ir) + LazyGenericDomtree{IsPostDom}(cfg::CFG) where {IsPostDom} = new{IsPostDom}(cfg) end +LazyGenericDomtree{IsPostDom}(ir::IRCode) where {IsPostDom} = LazyGenericDomtree{IsPostDom}(ir.cfg) function get!(x::LazyGenericDomtree{IsPostDom}) where {IsPostDom} isdefined(x, :domtree) && return x.domtree return @timeit "domtree 2" x.domtree = IsPostDom ? - construct_postdomtree(x.ir) : - construct_domtree(x.ir) + construct_postdomtree(x.cfg) : + construct_domtree(x.cfg) end const LazyDomtree = LazyGenericDomtree{false} @@ -227,6 +228,16 @@ struct HandlerInfo handler_at::Vector{Tuple{Int,Int}} # tuple of current (handler, exception stack) value at the pc end +struct SlotAssignments + assignments::Vector{BitSet} + SlotAssignments(nslots::Int) = new(Vector{BitSet}(undef, nslots)) +end +const ARGUMENT_ASSIGNMENT = BitSet(0) +getassignment(sa::SlotAssignments, sidx::Int) = isassigned(sa.assignments, sidx) ? + sa.assignments[sidx] : ARGUMENT_ASSIGNMENT +setassignment!(sa::SlotAssignments, sidx::Int, pc::Int) = push!(isassigned(sa.assignments, sidx) ? + sa.assignments[sidx] : (sa.assignments[sidx] = BitSet()), pc) + mutable struct InferenceState #= information about this method instance =# linfo::MethodInstance @@ -249,6 +260,8 @@ mutable struct InferenceState ssavaluetypes::Vector{Any} stmt_edges::Vector{Vector{Any}} stmt_info::Vector{CallInfo} + slotassignments::SlotAssignments + lazydomtree::LazyDomtree #= intermediate states for interprocedural abstract interpretation =# pclimitations::IdSet{InferenceState} # causes of precision restrictions (LimitedAccuracy) on currpc ssavalue @@ -308,6 +321,8 @@ mutable struct InferenceState slottypes = Vector{Any}(undef, nslots) bb_vartables = Union{Nothing,VarTable}[ nothing for i = 1:length(cfg.blocks) ] bb_vartable1 = bb_vartables[1] = VarTable(undef, nslots) + slotassignments = SlotAssignments(nslots) + lazydomtree = LazyDomtree(cfg) argtypes = result.argtypes argtypes = va_process_argtypes(typeinf_lattice(interp), argtypes, src.nargs, src.isva) @@ -316,7 +331,7 @@ mutable struct InferenceState for i = 1:nslots argtyp = (i > nargtypes) ? Bottom : argtypes[i] if argtyp === Bool && has_conditional(typeinf_lattice(interp)) - argtyp = Conditional(i, Const(true), Const(false)) + argtyp = Conditional(i, Const(true), Const(false), #=from_ssa=#0) end slottypes[i] = argtyp bb_vartable1[i] = VarState(argtyp, i > nargtypes) @@ -350,7 +365,8 @@ mutable struct InferenceState this = new( mi, world, mod, sptypes, slottypes, src, cfg, method_info, - currbb, currpc, ip, handler_info, ssavalue_uses, bb_vartables, ssavaluetypes, stmt_edges, stmt_info, + currbb, currpc, ip, handler_info, ssavalue_uses, bb_vartables, ssavaluetypes, + stmt_edges, stmt_info, slotassignments, lazydomtree, pclimitations, limitations, cycle_backedges, callstack, 0, 0, 0, result, unreachable, valid_worlds, bestguess, exc_bestguess, ipo_effects, restrict_abstract_call_sites, cache_mode, insert_coverage, @@ -734,7 +750,8 @@ end _topmod(sv::InferenceState) = _topmod(frame_module(sv)) -function record_ssa_assign!(๐•ƒแตข::AbstractLattice, ssa_id::Int, @nospecialize(new), frame::InferenceState) +function record_ssa_assign!(๐•ƒแตข::AbstractLattice, ssa_id::Int, @nospecialize(new), + frame::InferenceState) ssavaluetypes = frame.ssavaluetypes old = ssavaluetypes[ssa_id] if old === NOT_FOUND || !is_lattice_equal(๐•ƒแตข, new, old) diff --git a/base/compiler/ssair/ir.jl b/base/compiler/ssair/ir.jl index 960da88ddffc8..43e7dcc03a498 100644 --- a/base/compiler/ssair/ir.jl +++ b/base/compiler/ssair/ir.jl @@ -914,6 +914,15 @@ function dominates_ssa(compact::IncrementalCompact, domtree::DomTree, x::AnySSAV return dominates(domtree, xb, yb) end +function dominates_ssa(cfg::CFG, domtree::DomTree, x::Int, y::Int) + xb = block_for_inst(cfg, x) + yb = block_for_inst(cfg, y) + if xb == yb + return x < y + end + return dominates(domtree, xb, yb) +end + function _count_added_node!(compact::IncrementalCompact, @nospecialize(val)) if isa(val, SSAValue) compact.used_ssas[val.id] += 1 diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 0c57c04a6ddea..faeb57fef72e1 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -229,7 +229,7 @@ end function not_tfunc(๐•ƒ::AbstractLattice, @nospecialize(b)) if isa(b, Conditional) - return Conditional(b.slot, b.elsetype, b.thentype) + return Conditional(b.slot, b.elsetype, b.thentype, b.from_ssa) elseif isa(b, Const) return Const(not_int(b.val)) end @@ -350,14 +350,14 @@ end if isa(x, Conditional) y = widenconditional(y) if isa(y, Const) - y.val === false && return Conditional(x.slot, x.elsetype, x.thentype) + y.val === false && return Conditional(x.slot, x.elsetype, x.thentype, x.from_ssa) y.val === true && return x return Const(false) end elseif isa(y, Conditional) x = widenconditional(x) if isa(x, Const) - x.val === false && return Conditional(y.slot, y.elsetype, y.thentype) + x.val === false && return Conditional(y.slot, y.elsetype, y.thentype, y.from_ssa) x.val === true && return y return Const(false) end diff --git a/base/compiler/typelattice.jl b/base/compiler/typelattice.jl index 14477c5dc2725..54d20993e8556 100644 --- a/base/compiler/typelattice.jl +++ b/base/compiler/typelattice.jl @@ -73,14 +73,15 @@ struct Conditional slot::Int thentype elsetype - function Conditional(slot::Int, @nospecialize(thentype), @nospecialize(elsetype)) + from_ssa::Int + function Conditional(slot::Int, @nospecialize(thentype), @nospecialize(elsetype), from_ssa::Int=1) assert_nested_slotwrapper(thentype) assert_nested_slotwrapper(elsetype) - return new(slot, thentype, elsetype) + return new(slot, thentype, elsetype, from_ssa) end end -Conditional(var::SlotNumber, @nospecialize(thentype), @nospecialize(elsetype)) = - Conditional(slot_id(var), thentype, elsetype) +Conditional(var::SlotNumber, @nospecialize(thentype), @nospecialize(elsetype), from_ssa::Int=1) = + Conditional(slot_id(var), thentype, elsetype, from_ssa) import Core: InterConditional """ @@ -100,7 +101,6 @@ InterConditional(var::SlotNumber, @nospecialize(thentype), @nospecialize(elsetyp InterConditional(slot_id(var), thentype, elsetype) const AnyConditional = Union{Conditional,InterConditional} -Conditional(cnd::InterConditional) = Conditional(cnd.slot, cnd.thentype, cnd.elsetype) InterConditional(cnd::Conditional) = InterConditional(cnd.slot, cnd.thentype, cnd.elsetype) """ @@ -316,7 +316,8 @@ end return false end -is_same_conditionals(a::C, b::C) where C<:AnyConditional = a.slot == b.slot +is_same_conditionals(a::Conditional, b::Conditional) = a.slot == b.slot && a.from_ssa == b.from_ssa +is_same_conditionals(a::InterConditional, b::InterConditional) = a.slot == b.slot @nospecializeinfer is_lattice_bool(lattice::AbstractLattice, @nospecialize(typ)) = typ !== Bottom && โŠ‘(lattice, typ, Bool) @@ -377,7 +378,8 @@ end end return Conditional(slot, thenfields === nothing ? Bottom : PartialStruct(vartyp.typ, thenfields), - elsefields === nothing ? Bottom : PartialStruct(vartyp.typ, elsefields)) + elsefields === nothing ? Bottom : PartialStruct(vartyp.typ, elsefields), + #=TODO from_ssa=#0) else vartyp_widened = widenconst(vartyp) thenfields = thentype === Bottom ? nothing : Any[] @@ -394,7 +396,8 @@ end end return Conditional(slot, thenfields === nothing ? Bottom : PartialStruct(vartyp_widened, thenfields), - elsefields === nothing ? Bottom : PartialStruct(vartyp_widened, elsefields)) + elsefields === nothing ? Bottom : PartialStruct(vartyp_widened, elsefields), + #=TODO from_ssa=#0) end end @@ -743,15 +746,23 @@ end @nospecializeinfer @inline schanged(lattice::AbstractLattice, @nospecialize(n), @nospecialize(o)) = (n !== o) && (o === NOT_FOUND || (n !== NOT_FOUND && !(n.undef <= o.undef && โŠ‘(lattice, n.typ, o.typ)))) -# remove any lattice elements that wrap the reassigned slot object from the vartable -function invalidate_slotwrapper(vt::VarState, changeid::Int, ignore_conditional::Bool) - newtyp = ignorelimited(vt.typ) - if (!ignore_conditional && isa(newtyp, Conditional) && newtyp.slot == changeid) || - (isa(newtyp, MustAlias) && newtyp.slot == changeid) - newtyp = @noinline widenwrappedslotwrapper(vt.typ) - return VarState(newtyp, vt.undef) +function should_invalidate(@nospecialize(typ), changeid::Int, ignore_conditional::Bool=false) + typ = ignorelimited(typ) + return ((!ignore_conditional && typ isa Conditional && typ.slot == changeid) || + (typ isa MustAlias && typ.slot == changeid)) +end + +# remove any lattice elements that wrap the reassigned slot object within `state` +function invalidate_slotwrapper!(state::VarTable, changeid::Int, ignore_conditional::Bool) + for idx = 1:length(state) + invalidate_slotwrapper!(state, idx, changeid, ignore_conditional) + end +end +function invalidate_slotwrapper!(state::VarTable, idx::Int, changeid::Int, ignore_conditional::Bool) + vt = state[idx] + if should_invalidate(vt.typ, changeid, ignore_conditional) + state[idx] = VarState(@noinline(widenwrappedslotwrapper(vt.typ)), vt.undef) end - return nothing end function stupdate!(lattice::AbstractLattice, state::VarTable, changes::VarTable) @@ -776,13 +787,10 @@ end function stoverwrite1!(state::VarTable, change::StateUpdate) changeid = slot_id(change.var) - for i = 1:length(state) - invalidated = invalidate_slotwrapper(state[i], changeid, change.conditional) - if invalidated !== nothing - state[i] = invalidated - end - end - # and update the type of it + # widen any slot wrapper types that should be invalidated by this change + # (unless if this change is made from `Conditional`) + invalidate_slotwrapper!(state, changeid, #=ignore_conditional=#change.conditional) + # and update the type of the slot newtype = change.vtype state[changeid] = newtype return state diff --git a/base/compiler/typelimits.jl b/base/compiler/typelimits.jl index 91a44d3b117ab..00afd274f63b6 100644 --- a/base/compiler/typelimits.jl +++ b/base/compiler/typelimits.jl @@ -494,16 +494,16 @@ end # 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{}) + typeb = Conditional(typea.slot, Any, Union{}, typea.from_ssa) elseif typeb.val === false - typeb = Conditional(typea.slot, Union{}, Any) + typeb = Conditional(typea.slot, Union{}, Any, typea.from_ssa) end end if isa(typeb, Conditional) && isa(typea, Const) if typea.val === true - typea = Conditional(typeb.slot, Any, Union{}) + typea = Conditional(typeb.slot, Any, Union{}, typeb.from_ssa) elseif typea.val === false - typea = Conditional(typeb.slot, Union{}, Any) + typea = Conditional(typeb.slot, Union{}, Any, typeb.from_ssa) end end if isa(typea, Conditional) && isa(typeb, Conditional) @@ -511,7 +511,7 @@ end 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) + return Conditional(typea.slot, thentype, elsetype, typea.from_ssa) end end val = maybe_extract_const_bool(typea) diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 6e2fa77eb15c8..2219d47b04e13 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -2035,7 +2035,7 @@ function foo25261() next = f25261(Core.getfield(next, 2)) end end -let opt25261 = code_typed(foo25261, Tuple{}, optimize=true)[1].first.code +let opt25261 = first(only(code_typed(foo25261, Tuple{}, optimize=true))).code i = 1 # Skip to after the branch while !isa(opt25261[i], GotoIfNot) @@ -6060,3 +6060,14 @@ end fcondvarargs(a, b, c, d) = isa(d, Int64) gcondvarargs(a, x...) = return fcondvarargs(a, x...) ? isa(a, Int64) : !isa(a, Int64) @test Core.Compiler.return_type(gcondvarargs, Tuple{Vararg{Any}}) === Bool + +# JuliaLang/julia#55548: invalidate stale slot wrapper types in `ssavaluetypes` +_issue55548_proj1(a, b) = a +function issue55548(a) + a = Base.inferencebarrier(a)::Union{Int64,Float64} + if _issue55548_proj1(isa(a, Int64), (a = Base.inferencebarrier(1.0)::Union{Int64,Float64}; true)) + return a + end + return 2 +end +@test Float64 <: Base.infer_return_type(issue55548, (Int,))