Skip to content

Commit ef23805

Browse files
committed
inference: override InterConditional result with Const carefully
I found that a tricky thing can happen when constant inference derives `Const`-result while non-constant inference has derived (non-constant) `InterConditional` result beforehand. In such a case, currently we discard the result with constant infernece (since `!(Const ⊑ InterConditional)`), but we can achieve more accuracy by not discarding that `Const`-information, e.g.: ```julia julia> iszero_simple(x) = x === 0 iszero_simple (generic function with 1 method) julia> @test Base.return_types() do iszero_simple(0) ? nothing : missing end |> only === Nothing Test Passed ```
1 parent 85eaf4e commit ef23805

File tree

2 files changed

+23
-13
lines changed

2 files changed

+23
-13
lines changed

base/compiler/abstractinterpretation.jl

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -118,19 +118,20 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
118118
splitsigs = switchtupleunion(sig)
119119
for sig_n in splitsigs
120120
result = abstract_call_method(interp, method, sig_n, svec(), multiple_matches, sv)
121-
rt, edge = result.rt, result.edge
122-
if edge !== nothing
123-
push!(edges, edge)
124-
end
121+
rt = result.rt
122+
edge = result.edge
123+
edge !== nothing && push!(edges, edge)
125124
this_argtypes = isa(matches, MethodMatches) ? argtypes : matches.applicable_argtypes[i]
126125
this_arginfo = ArgInfo(fargs, this_argtypes)
127126
const_call_result = abstract_call_method_with_const_args(interp, result,
128127
f, this_arginfo, match, sv)
129128
effects = result.edge_effects
130129
const_result = nothing
131130
if const_call_result !== nothing
132-
if const_call_result.rt rt
133-
(; rt, effects, const_result) = const_call_result
131+
const_rt = const_call_result.rt
132+
if const_rt rt
133+
rt = const_rt
134+
(; effects, const_result) = const_call_result
134135
end
135136
end
136137
tristate_merge!(sv, effects)
@@ -143,6 +144,8 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
143144
break
144145
end
145146
end
147+
this_conditional = ignorelimited(this_rt)
148+
this_rt = widenwrappedconditional(this_rt)
146149
else
147150
if infer_compilation_signature(interp)
148151
# Also infer the compilation signature for this method, so it's available
@@ -159,10 +162,10 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
159162
end
160163

161164
result = abstract_call_method(interp, method, sig, match.sparams, multiple_matches, sv)
162-
this_rt, edge = result.rt, result.edge
163-
if edge !== nothing
164-
push!(edges, edge)
165-
end
165+
this_conditional = ignorelimited(result.rt)
166+
this_rt = widenwrappedconditional(result.rt)
167+
edge = result.edge
168+
edge !== nothing && push!(edges, edge)
166169
# try constant propagation with argtypes for this match
167170
# this is in preparation for inlining, or improving the return result
168171
this_argtypes = isa(matches, MethodMatches) ? argtypes : matches.applicable_argtypes[i]
@@ -172,10 +175,12 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
172175
effects = result.edge_effects
173176
const_result = nothing
174177
if const_call_result !== nothing
175-
this_const_rt = const_call_result.rt
178+
this_const_conditional = ignorelimited(const_call_result.rt)
179+
this_const_rt = widenwrappedconditional(const_call_result.rt)
176180
# return type of const-prop' inference can be wider than that of non const-prop' inference
177181
# e.g. in cases when there are cycles but cached result is still accurate
178182
if this_const_rt this_rt
183+
this_conditional = this_const_conditional
179184
this_rt = this_const_rt
180185
(; effects, const_result) = const_call_result
181186
end
@@ -186,8 +191,6 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
186191
any_const_result = true
187192
end
188193
end
189-
this_conditional = ignorelimited(this_rt)
190-
this_rt = widenwrappedconditional(this_rt)
191194
@assert !(this_conditional isa Conditional) "invalid lattice element returned from inter-procedural context"
192195
seen += 1
193196
rettype = tmerge(rettype, this_rt)

test/compiler/inference.jl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2032,6 +2032,13 @@ end
20322032
end
20332033
@test ts == Any[Any]
20342034
end
2035+
2036+
# a tricky case: if constant inference derives `Const` while non-constant infernece has
2037+
# derived `InterConditional`, we should not discard that constant information
2038+
iszero_simple(x) = x === 0
2039+
@test Base.return_types() do
2040+
iszero_simple(0) ? nothing : missing
2041+
end |> only === Nothing
20352042
end
20362043

20372044
@testset "branching on conditional object" begin

0 commit comments

Comments
 (0)