Skip to content

Commit 0fcbc16

Browse files
committed
inference: refine slot undef info within then branch of @isdefined
By adding some information to `Conditional`, it is possible to improve the `undef` information of `slot` within the `then` branch of `@isdefined slot`. As a result, it's now possible to prove the `:nothrow`-ness in cases like: ```julia @test Base.infer_effects((Bool,Int)) do c, x local val if c val = x end if @isdefined val return val end return zero(Int) end |> Core.Compiler.is_nothrow ```
1 parent 3d20a92 commit 0fcbc16

File tree

3 files changed

+31
-10
lines changed

3 files changed

+31
-10
lines changed

base/compiler/abstractinterpretation.jl

+10-6
Original file line numberDiff line numberDiff line change
@@ -2733,6 +2733,8 @@ function abstract_eval_isdefined(interp::AbstractInterpreter, e::Expr, vtypes::U
27332733
rt = Const(false) # never assigned previously
27342734
elseif !vtyp.undef
27352735
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)
27362738
end
27372739
elseif isa(sym, GlobalRef)
27382740
if InferenceParams(interp).assume_bindings_static
@@ -3205,7 +3207,7 @@ end
32053207
@inline function abstract_eval_basic_statement(interp::AbstractInterpreter,
32063208
@nospecialize(stmt), pc_vartable::VarTable, frame::InferenceState)
32073209
if isa(stmt, NewvarNode)
3208-
changes = StateUpdate(stmt.slot, VarState(Bottom, true), false)
3210+
changes = StateUpdate(stmt.slot, VarState(Bottom, true))
32093211
return BasicStmtChange(changes, nothing, Union{})
32103212
elseif !isa(stmt, Expr)
32113213
(; rt, exct) = abstract_eval_statement(interp, stmt, pc_vartable, frame)
@@ -3220,7 +3222,7 @@ end
32203222
end
32213223
lhs = stmt.args[1]
32223224
if isa(lhs, SlotNumber)
3223-
changes = StateUpdate(lhs, VarState(rt, false), false)
3225+
changes = StateUpdate(lhs, VarState(rt, false))
32243226
elseif isa(lhs, GlobalRef)
32253227
handle_global_assignment!(interp, frame, lhs, rt)
32263228
elseif !isa(lhs, SSAValue)
@@ -3230,7 +3232,7 @@ end
32303232
elseif hd === :method
32313233
fname = stmt.args[1]
32323234
if isa(fname, SlotNumber)
3233-
changes = StateUpdate(fname, VarState(Any, false), false)
3235+
changes = StateUpdate(fname, VarState(Any, false))
32343236
end
32353237
return BasicStmtChange(changes, nothing, Union{})
32363238
elseif (hd === :code_coverage_effect || (
@@ -3576,7 +3578,7 @@ function apply_refinement!(𝕃ᵢ::AbstractLattice, slot::SlotNumber, @nospecia
35763578
oldtyp = vtype.typ
35773579
= strictpartialorder(𝕃ᵢ)
35783580
if newtyp oldtyp
3579-
stmtupdate = StateUpdate(slot, VarState(newtyp, vtype.undef), false)
3581+
stmtupdate = StateUpdate(slot, VarState(newtyp, vtype.undef))
35803582
stoverwrite1!(currstate, stmtupdate)
35813583
end
35823584
end
@@ -3600,7 +3602,9 @@ function conditional_change(𝕃ᵢ::AbstractLattice, currstate::VarTable, condt
36003602
# "causes" since we ignored those in the comparison
36013603
newtyp = tmerge(𝕃ᵢ, newtyp, LimitedAccuracy(Bottom, oldtyp.causes))
36023604
end
3603-
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)
36043608
end
36053609

36063610
function condition_object_change(currstate::VarTable, condt::Conditional,
@@ -3609,7 +3613,7 @@ function condition_object_change(currstate::VarTable, condt::Conditional,
36093613
newcondt = Conditional(condt.slot,
36103614
then_or_else ? condt.thentype : Union{},
36113615
then_or_else ? Union{} : condt.elsetype)
3612-
return StateUpdate(condslot, VarState(newcondt, vtype.undef), false)
3616+
return StateUpdate(condslot, VarState(newcondt, vtype.undef))
36133617
end
36143618

36153619
# make as much progress on `frame` as possible (by handling cycles)

base/compiler/typelattice.jl

+9-4
Original file line numberDiff line numberDiff line change
@@ -73,14 +73,18 @@ struct Conditional
7373
slot::Int
7474
thentype
7575
elsetype
76-
function Conditional(slot::Int, @nospecialize(thentype), @nospecialize(elsetype))
76+
# `isdefined` indicates this `Conditional` is from `@isdefined slot`, implying that
77+
# the `undef` information of `slot` can be improved in the then branch.
78+
# Since this is only beneficial for local inference, it is not translated into `InterConditional`.
79+
isdefined::Bool
80+
function Conditional(slot::Int, @nospecialize(thentype), @nospecialize(elsetype), isdefined::Bool=false)
7781
assert_nested_slotwrapper(thentype)
7882
assert_nested_slotwrapper(elsetype)
79-
return new(slot, thentype, elsetype)
83+
return new(slot, thentype, elsetype, isdefined)
8084
end
8185
end
82-
Conditional(var::SlotNumber, @nospecialize(thentype), @nospecialize(elsetype)) =
83-
Conditional(slot_id(var), thentype, elsetype)
86+
Conditional(var::SlotNumber, @nospecialize(thentype), @nospecialize(elsetype), isdefined::Bool=false) =
87+
Conditional(slot_id(var), thentype, elsetype, isdefined)
8488

8589
import Core: InterConditional
8690
"""
@@ -180,6 +184,7 @@ struct StateUpdate
180184
var::SlotNumber
181185
vtype::VarState
182186
conditional::Bool
187+
StateUpdate(var::SlotNumber, vtype::VarState, conditional::Bool=false) = new(var, vtype, conditional)
183188
end
184189

185190
"""

test/compiler/inference.jl

+12
Original file line numberDiff line numberDiff line change
@@ -6050,6 +6050,18 @@ end |> Core.Compiler.is_nothrow
60506050
return nothing
60516051
end |> Core.Compiler.is_nothrow
60526052

6053+
# refine `undef` information from `@isdefined` check
6054+
@test Base.infer_effects((Bool,Int)) do c, x
6055+
local val
6056+
if c
6057+
val = x
6058+
end
6059+
if @isdefined val
6060+
return val
6061+
end
6062+
return zero(Int)
6063+
end |> Core.Compiler.is_nothrow
6064+
60536065
# End to end test case for the partially initialized struct with `PartialStruct`
60546066
@noinline broadcast_noescape1(a) = (broadcast(identity, a); nothing)
60556067
@test fully_eliminated() do

0 commit comments

Comments
 (0)