Skip to content

Commit b682f49

Browse files
serenity4Cédric Belmant
and
Cédric Belmant
authored
Inference: propagate struct initialization info on setfield! (JuliaLang#57222)
When a variable has a field set with `setfield!(var, field, value)`, inference now assumes that this specific field is defined and may for example constant-propagate `isdefined(var, field)` as `true`. `PartialStruct`, the lattice element used to encode this information, still has a few limitations in terms of what it may represent (it cannot represent mutable structs with non-contiguously defined fields yet), further work on extending it would increase the impact of this change. Consider the following function: ```julia julia> function f() a = A(1) setfield!(a, :y, 2) invokelatest(identity, a) isdefined(a, :y) && return 1.0 a end f (generic function with 1 method) ``` Here is before on `master`: ```julia julia> @code_typed f() CodeInfo( 1 ─ %1 = %new(Main.A, 1)::A │ builtin Main.setfield!(%1, :y, 2)::Int64 │ dynamic builtin (Core._call_latest)(identity, %1)::Any │ %4 = builtin Main.isdefined(%1, :y)::Bool └── goto mmtk#3 if not %4 2 ─ return 1.0 3 ─ return %1 ) => Union{Float64, A} ``` And after this PR: ```julia julia> @code_typed f() CodeInfo( 1 ─ %1 = %new(Main.A, 1)::A │ builtin Main.setfield!(%1, :y, 2)::Int64 │ dynamic builtin (Core._call_latest)(identity, %1)::Any └── return 1.0 ) => Float64 ``` --------- Co-authored-by: Cédric Belmant <cedric.belmant@juliahub.com>
1 parent 4ebb50b commit b682f49

File tree

2 files changed

+27
-4
lines changed

2 files changed

+27
-4
lines changed

Compiler/src/abstractinterpretation.jl

+16-4
Original file line numberDiff line numberDiff line change
@@ -2665,12 +2665,24 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f),
26652665
end
26662666
pushfirst!(argtypes, ft)
26672667
refinements = nothing
2668-
if sv isa InferenceState && f === typeassert
2669-
# perform very limited back-propagation of invariants after this type assertion
2670-
if rt !== Bottom && isa(fargs, Vector{Any})
2668+
if sv isa InferenceState
2669+
if f === typeassert
2670+
# perform very limited back-propagation of invariants after this type assertion
2671+
if rt !== Bottom && isa(fargs, Vector{Any})
2672+
farg2 = ssa_def_slot(fargs[2], sv)
2673+
if farg2 isa SlotNumber
2674+
refinements = SlotRefinement(farg2, rt)
2675+
end
2676+
end
2677+
elseif f === setfield! && length(argtypes) == 4 && isa(argtypes[3], Const)
2678+
# from there on we know that the struct field will never be undefined,
2679+
# so we try to encode that information with a `PartialStruct`
26712680
farg2 = ssa_def_slot(fargs[2], sv)
26722681
if farg2 isa SlotNumber
2673-
refinements = SlotRefinement(farg2, rt)
2682+
refined = form_partially_defined_struct(argtypes[2], argtypes[3])
2683+
if refined !== nothing
2684+
refinements = SlotRefinement(farg2, refined)
2685+
end
26742686
end
26752687
end
26762688
end

Compiler/test/irpasses.jl

+11
Original file line numberDiff line numberDiff line change
@@ -2042,3 +2042,14 @@ let src = code_typed1(()) do
20422042
end
20432043
@test count(iscall((src, setfield!)), src.code) == 1
20442044
end
2045+
2046+
# optimize `isdefined` away in the presence of a dominating `setfield!`
2047+
let src = code_typed1(()) do
2048+
a = Ref{Any}()
2049+
setfield!(a, :x, 2)
2050+
invokelatest(identity, a)
2051+
isdefined(a, :x) && return 1.0
2052+
a[]
2053+
end
2054+
@test count(iscall((src, isdefined)), src.code) == 0
2055+
end

0 commit comments

Comments
 (0)