Skip to content

Commit 94b7154

Browse files
committed
inference: override InterConditional result with Const carefully (#44668)
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 inference (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 9b7990d commit 94b7154

File tree

2 files changed

+24
-14
lines changed

2 files changed

+24
-14
lines changed

base/compiler/abstractinterpretation.jl

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -110,19 +110,20 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
110110
splitsigs = switchtupleunion(sig)
111111
for sig_n in splitsigs
112112
result = abstract_call_method(interp, method, sig_n, svec(), multiple_matches, sv)
113-
rt, edge = result.rt, result.edge
114-
if edge !== nothing
115-
push!(edges, edge)
116-
end
113+
rt = result.rt
114+
edge = result.edge
115+
edge !== nothing && push!(edges, edge)
117116
this_argtypes = isa(matches, MethodMatches) ? argtypes : matches.applicable_argtypes[i]
118117
this_arginfo = ArgInfo(fargs, this_argtypes)
119118
const_call_result = abstract_call_method_with_const_args(interp, result,
120119
f, this_arginfo, match, sv)
121120
effects = result.edge_effects
122121
const_result = nothing
123122
if const_call_result !== nothing
124-
if const_call_result.rt rt
125-
(; rt, effects, const_result) = const_call_result
123+
const_rt = const_call_result.rt
124+
if const_rt rt
125+
rt = const_rt
126+
(; effects, const_result) = const_call_result
126127
end
127128
end
128129
tristate_merge!(sv, effects)
@@ -135,6 +136,8 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
135136
break
136137
end
137138
end
139+
this_conditional = ignorelimited(this_rt)
140+
this_rt = widenwrappedconditional(this_rt)
138141
else
139142
if infer_compilation_signature(interp)
140143
# Also infer the compilation signature for this method, so it's available
@@ -151,10 +154,10 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
151154
end
152155

153156
result = abstract_call_method(interp, method, sig, match.sparams, multiple_matches, sv)
154-
this_rt, edge = result.rt, result.edge
155-
if edge !== nothing
156-
push!(edges, edge)
157-
end
157+
this_conditional = ignorelimited(result.rt)
158+
this_rt = widenwrappedconditional(result.rt)
159+
edge = result.edge
160+
edge !== nothing && push!(edges, edge)
158161
# try constant propagation with argtypes for this match
159162
# this is in preparation for inlining, or improving the return result
160163
this_argtypes = isa(matches, MethodMatches) ? argtypes : matches.applicable_argtypes[i]
@@ -164,10 +167,12 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
164167
effects = result.edge_effects
165168
const_result = nothing
166169
if const_call_result !== nothing
167-
this_const_rt = const_call_result.rt
168-
# return type of const-prop' inference can be wider than that of non const-prop' inference
170+
this_const_conditional = ignorelimited(const_call_result.rt)
171+
this_const_rt = widenwrappedconditional(const_call_result.rt)
172+
# return type of const-prop' inference can be wider than that of non const-prop' inference
169173
# e.g. in cases when there are cycles but cached result is still accurate
170174
if this_const_rt this_rt
175+
this_conditional = this_const_conditional
171176
this_rt = this_const_rt
172177
(; effects, const_result) = const_call_result
173178
end
@@ -178,8 +183,6 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
178183
any_const_result = true
179184
end
180185
end
181-
this_conditional = ignorelimited(this_rt)
182-
this_rt = widenwrappedconditional(this_rt)
183186
@assert !(this_conditional isa Conditional) "invalid lattice element returned from inter-procedural context"
184187
seen += 1
185188
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 inference 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)