@@ -793,10 +793,10 @@ function edge_matches_sv(interp::AbstractInterpreter, frame::AbsIntState,
793793 # otherwise, we don't
794794
795795 # check in the cycle list first
796- # all items in here are mutual parents of all others
796+ # all items in here are considered mutual parents of all others
797797 if ! any (p:: AbsIntState -> matches_sv (p, sv), callers_in_cycle (frame))
798798 let parent = frame_parent (frame)
799- parent != = nothing || return false
799+ parent === nothing && return false
800800 (is_cached (parent) || frame_parent (parent) != = nothing ) || return false
801801 matches_sv (parent, sv) || return false
802802 end
@@ -1027,7 +1027,7 @@ collect_const_args(arginfo::ArgInfo, start::Int) = collect_const_args(arginfo.ar
10271027function collect_const_args (argtypes:: Vector{Any} , start:: Int )
10281028 return Any[ let a = widenslotwrapper (argtypes[i])
10291029 isa (a, Const) ? a. val :
1030- isconstType (a) ? (a :: DataType ) . parameters[1 ] :
1030+ isconstType (a) ? a . parameters[1 ] :
10311031 (a:: DataType ). instance
10321032 end for i = start: length (argtypes) ]
10331033end
@@ -1298,7 +1298,7 @@ function semi_concrete_eval_call(interp::AbstractInterpreter,
12981298 if code != = nothing
12991299 irsv = IRInterpretationState (interp, code, mi, arginfo. argtypes, world)
13001300 if irsv != = nothing
1301- irsv. parent = sv
1301+ assign_parentchild! ( irsv, sv)
13021302 rt, (nothrow, noub) = ir_abstract_constant_propagation (interp, irsv)
13031303 @assert ! (rt isa Conditional || rt isa MustAlias) " invalid lattice element returned from irinterp"
13041304 if ! (isa (rt, Type) && hasintersect (rt, Bool))
@@ -1376,11 +1376,17 @@ function const_prop_call(interp::AbstractInterpreter,
13761376 add_remark! (interp, sv, " [constprop] Could not retrieve the source" )
13771377 return nothing # this is probably a bad generated function (unsound), but just ignore it
13781378 end
1379- frame. parent = sv
1379+ assign_parentchild! ( frame, sv)
13801380 if ! typeinf (interp, frame)
13811381 add_remark! (interp, sv, " [constprop] Fresh constant inference hit a cycle" )
1382+ @assert frame. frameid != 0 && frame. cycleid == frame. frameid
1383+ callstack = frame. callstack:: Vector{AbsIntState}
1384+ @assert callstack[end ] === frame && length (callstack) == frame. frameid
1385+ pop! (callstack)
13821386 return nothing
13831387 end
1388+ @assert frame. frameid != 0 && frame. cycleid == frame. frameid
1389+ @assert frame. parentid == sv. frameid
13841390 @assert inf_result. result != = nothing
13851391 # ConditionalSimpleArgtypes is allowed, because the only case in which it modifies
13861392 # the argtypes is when one of the argtypes is a `Conditional`, which case
@@ -2006,33 +2012,74 @@ function abstract_call_builtin(interp::AbstractInterpreter, f::Builtin, (; fargs
20062012 return Conditional (aty. slot, thentype, elsetype)
20072013 end
20082014 elseif f === isdefined
2009- uty = argtypes[2 ]
20102015 a = ssa_def_slot (fargs[2 ], sv)
2011- if isa (uty, Union) && isa (a, SlotNumber)
2012- fld = argtypes[3 ]
2013- thentype = Bottom
2014- elsetype = Bottom
2015- for ty in uniontypes (uty)
2016- cnd = isdefined_tfunc (𝕃ᵢ, ty, fld)
2017- if isa (cnd, Const)
2018- if cnd. val:: Bool
2019- thentype = thentype ⊔ ty
2016+ if isa (a, SlotNumber)
2017+ argtype2 = argtypes[2 ]
2018+ if isa (argtype2, Union)
2019+ fld = argtypes[3 ]
2020+ thentype = Bottom
2021+ elsetype = Bottom
2022+ for ty in uniontypes (argtype2)
2023+ cnd = isdefined_tfunc (𝕃ᵢ, ty, fld)
2024+ if isa (cnd, Const)
2025+ if cnd. val:: Bool
2026+ thentype = thentype ⊔ ty
2027+ else
2028+ elsetype = elsetype ⊔ ty
2029+ end
20202030 else
2031+ thentype = thentype ⊔ ty
20212032 elsetype = elsetype ⊔ ty
20222033 end
2023- else
2024- thentype = thentype ⊔ ty
2025- elsetype = elsetype ⊔ ty
2034+ end
2035+ return Conditional (a, thentype, elsetype)
2036+ else
2037+ thentype = form_partially_defined_struct (argtype2, argtypes[3 ])
2038+ if thentype != = nothing
2039+ elsetype = argtype2
2040+ if rt === Const (false )
2041+ thentype = Bottom
2042+ elseif rt === Const (true )
2043+ elsetype = Bottom
2044+ end
2045+ return Conditional (a, thentype, elsetype)
20262046 end
20272047 end
2028- return Conditional (a, thentype, elsetype)
20292048 end
20302049 end
20312050 end
20322051 @assert ! isa (rt, TypeVar) " unhandled TypeVar"
20332052 return rt
20342053end
20352054
2055+ function form_partially_defined_struct (@nospecialize (obj), @nospecialize (name))
2056+ obj isa Const && return nothing # nothing to refine
2057+ name isa Const || return nothing
2058+ objt0 = widenconst (obj)
2059+ objt = unwrap_unionall (objt0)
2060+ objt isa DataType || return nothing
2061+ isabstracttype (objt) && return nothing
2062+ fldidx = try_compute_fieldidx (objt, name. val)
2063+ fldidx === nothing && return nothing
2064+ nminfld = datatype_min_ninitialized (objt)
2065+ if ismutabletype (objt)
2066+ # A mutable struct can have non-contiguous undefined fields, but `PartialStruct` cannot
2067+ # model such a state. So here `PartialStruct` can be used to represent only the
2068+ # objects where the field following the minimum initialized fields is also defined.
2069+ if fldidx ≠ nminfld+ 1
2070+ # if it is already represented as a `PartialStruct`, we can add one more
2071+ # `isdefined`-field information on top of those implied by its `fields`
2072+ if ! (obj isa PartialStruct && fldidx == length (obj. fields)+ 1 )
2073+ return nothing
2074+ end
2075+ end
2076+ else
2077+ fldidx > nminfld || return nothing
2078+ end
2079+ return PartialStruct (objt0, Any[obj isa PartialStruct && i≤ length (obj. fields) ?
2080+ obj. fields[i] : fieldtype (objt0,i) for i = 1 : fldidx])
2081+ end
2082+
20362083function abstract_call_unionall (interp:: AbstractInterpreter , argtypes:: Vector{Any} , call:: CallMeta )
20372084 na = length (argtypes)
20382085 if isvarargtype (argtypes[end ])
@@ -2573,20 +2620,18 @@ function abstract_eval_new(interp::AbstractInterpreter, e::Expr, vtypes::Union{V
25732620 end
25742621 ats[i] = at
25752622 end
2576- # For now, don't allow:
2577- # - Const/PartialStruct of mutables (but still allow PartialStruct of mutables
2578- # with `const` fields if anything refined)
2579- # - partially initialized Const/PartialStruct
2580- if fcount == nargs
2581- if consistent === ALWAYS_TRUE && allconst
2582- argvals = Vector {Any} (undef, nargs)
2583- for j in 1 : nargs
2584- argvals[j] = (ats[j]:: Const ). val
2585- end
2586- rt = Const (ccall (:jl_new_structv , Any, (Any, Ptr{Cvoid}, UInt32), rt, argvals, nargs))
2587- elseif anyrefine
2588- rt = PartialStruct (rt, ats)
2623+ if fcount == nargs && consistent === ALWAYS_TRUE && allconst
2624+ argvals = Vector {Any} (undef, nargs)
2625+ for j in 1 : nargs
2626+ argvals[j] = (ats[j]:: Const ). val
25892627 end
2628+ rt = Const (ccall (:jl_new_structv , Any, (Any, Ptr{Cvoid}, UInt32), rt, argvals, nargs))
2629+ elseif anyrefine || nargs > datatype_min_ninitialized (rt)
2630+ # propagate partially initialized struct as `PartialStruct` when:
2631+ # - any refinement information is available (`anyrefine`), or when
2632+ # - `nargs` is greater than `n_initialized` derived from the struct type
2633+ # information alone
2634+ rt = PartialStruct (rt, ats)
25902635 end
25912636 else
25922637 rt = refine_partial_type (rt)
@@ -2688,6 +2733,8 @@ function abstract_eval_isdefined(interp::AbstractInterpreter, e::Expr, vtypes::U
26882733 rt = Const (false ) # never assigned previously
26892734 elseif ! vtyp. undef
26902735 rt = Const (true ) # definitely assigned previously
2736+ else # form `Conditional` to refine `vtyp.undef` in the then branch
2737+ rt = Conditional (sym, vtyp. typ, vtyp. typ; isdefined= true )
26912738 end
26922739 elseif isa (sym, GlobalRef)
26932740 if InferenceParams (interp). assume_bindings_static
@@ -2794,7 +2841,7 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp
27942841 elseif ehead === :globaldecl
27952842 return RTEffects (Nothing, Any, EFFECTS_UNKNOWN)
27962843 elseif ehead === :thunk
2797- return RTEffects (Any, Any, EFFECTS_UNKNOWN )
2844+ return RTEffects (Any, Any, Effects () )
27982845 end
27992846 # N.B.: abstract_eval_value_expr can modify the global effects, but
28002847 # we move out any arguments with effects during SSA construction later
@@ -3094,7 +3141,8 @@ end
30943141@nospecializeinfer function widenreturn_partials (𝕃ᵢ:: PartialsLattice , @nospecialize (rt), info:: BestguessInfo )
30953142 if isa (rt, PartialStruct)
30963143 fields = copy (rt. fields)
3097- local anyrefine = false
3144+ anyrefine = ! isvarargtype (rt. fields[end ]) &&
3145+ length (rt. fields) > datatype_min_ninitialized (unwrap_unionall (rt. typ))
30983146 𝕃 = typeinf_lattice (info. interp)
30993147 ⊏ = strictpartialorder (𝕃)
31003148 for i in 1 : length (fields)
@@ -3159,7 +3207,7 @@ end
31593207@inline function abstract_eval_basic_statement (interp:: AbstractInterpreter ,
31603208 @nospecialize (stmt), pc_vartable:: VarTable , frame:: InferenceState )
31613209 if isa (stmt, NewvarNode)
3162- changes = StateUpdate (stmt. slot, VarState (Bottom, true ), false )
3210+ changes = StateUpdate (stmt. slot, VarState (Bottom, true ))
31633211 return BasicStmtChange (changes, nothing , Union{})
31643212 elseif ! isa (stmt, Expr)
31653213 (; rt, exct) = abstract_eval_statement (interp, stmt, pc_vartable, frame)
@@ -3174,7 +3222,7 @@ end
31743222 end
31753223 lhs = stmt. args[1 ]
31763224 if isa (lhs, SlotNumber)
3177- changes = StateUpdate (lhs, VarState (rt, false ), false )
3225+ changes = StateUpdate (lhs, VarState (rt, false ))
31783226 elseif isa (lhs, GlobalRef)
31793227 handle_global_assignment! (interp, frame, lhs, rt)
31803228 elseif ! isa (lhs, SSAValue)
@@ -3184,7 +3232,7 @@ end
31843232 elseif hd === :method
31853233 fname = stmt. args[1 ]
31863234 if isa (fname, SlotNumber)
3187- changes = StateUpdate (fname, VarState (Any, false ), false )
3235+ changes = StateUpdate (fname, VarState (Any, false ))
31883236 end
31893237 return BasicStmtChange (changes, nothing , Union{})
31903238 elseif (hd === :code_coverage_effect || (
@@ -3298,7 +3346,6 @@ end
32983346# make as much progress on `frame` as possible (without handling cycles)
32993347function typeinf_local (interp:: AbstractInterpreter , frame:: InferenceState )
33003348 @assert ! is_inferred (frame)
3301- frame. dont_work_on_me = true # mark that this function is currently on the stack
33023349 W = frame. ip
33033350 ssavaluetypes = frame. ssavaluetypes
33043351 bbs = frame. cfg. blocks
@@ -3519,7 +3566,6 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState)
35193566 end
35203567 end # while currbb <= nbbs
35213568
3522- frame. dont_work_on_me = false
35233569 nothing
35243570end
35253571
@@ -3532,7 +3578,7 @@ function apply_refinement!(𝕃ᵢ::AbstractLattice, slot::SlotNumber, @nospecia
35323578 oldtyp = vtype. typ
35333579 ⊏ = strictpartialorder (𝕃ᵢ)
35343580 if newtyp ⊏ oldtyp
3535- stmtupdate = StateUpdate (slot, VarState (newtyp, vtype. undef), false )
3581+ stmtupdate = StateUpdate (slot, VarState (newtyp, vtype. undef))
35363582 stoverwrite1! (currstate, stmtupdate)
35373583 end
35383584end
@@ -3556,7 +3602,9 @@ function conditional_change(𝕃ᵢ::AbstractLattice, currstate::VarTable, condt
35563602 # "causes" since we ignored those in the comparison
35573603 newtyp = tmerge (𝕃ᵢ, newtyp, LimitedAccuracy (Bottom, oldtyp. causes))
35583604 end
3559- return StateUpdate (SlotNumber (condt. slot), VarState (newtyp, vtype. undef), true )
3605+ # if this `Conditional` is from from `@isdefined condt.slot`, refine its `undef` information
3606+ newundef = condt. isdefined ? ! then_or_else : vtype. undef
3607+ return StateUpdate (SlotNumber (condt. slot), VarState (newtyp, newundef), #= conditional=# true )
35603608end
35613609
35623610function condition_object_change (currstate:: VarTable , condt:: Conditional ,
@@ -3565,22 +3613,29 @@ function condition_object_change(currstate::VarTable, condt::Conditional,
35653613 newcondt = Conditional (condt. slot,
35663614 then_or_else ? condt. thentype : Union{},
35673615 then_or_else ? Union{} : condt. elsetype)
3568- return StateUpdate (condslot, VarState (newcondt, vtype. undef), false )
3616+ return StateUpdate (condslot, VarState (newcondt, vtype. undef))
35693617end
35703618
35713619# make as much progress on `frame` as possible (by handling cycles)
35723620function typeinf_nocycle (interp:: AbstractInterpreter , frame:: InferenceState )
35733621 typeinf_local (interp, frame)
3622+ @assert isempty (frame. ip)
3623+ callstack = frame. callstack:: Vector{AbsIntState}
3624+ frame. cycleid == length (callstack) && return true
35743625
3575- # If the current frame is part of a cycle, solve the cycle before finishing
35763626 no_active_ips_in_callers = false
3577- while ! no_active_ips_in_callers
3627+ while true
3628+ # If the current frame is not the top part of a cycle, continue to the top of the cycle before resuming work
3629+ frame. cycleid == frame. frameid || return false
3630+ # If done, return and finalize this cycle
3631+ no_active_ips_in_callers && return true
3632+ # Otherwise, do at least one iteration over the entire current cycle
35783633 no_active_ips_in_callers = true
3579- for caller in frame. callers_in_cycle
3580- caller. dont_work_on_me && return false # cycle is above us on the stack
3634+ for i = reverse ( frame. cycleid : length (callstack))
3635+ caller = callstack[i] :: InferenceState
35813636 if ! isempty (caller. ip)
35823637 # Note that `typeinf_local(interp, caller)` can potentially modify the other frames
3583- # `frame.callers_in_cycle `, which is why making incremental progress requires the
3638+ # `frame.cycleid `, which is why making incremental progress requires the
35843639 # outer while loop.
35853640 typeinf_local (interp, caller)
35863641 no_active_ips_in_callers = false
0 commit comments