Skip to content

Commit

Permalink
inference: override InterConditional result with Const carefully (#…
Browse files Browse the repository at this point in the history
…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
```
  • Loading branch information
aviatesk committed Mar 24, 2022
1 parent 9b7990d commit 94b7154
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 14 deletions.
31 changes: 17 additions & 14 deletions base/compiler/abstractinterpretation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -110,19 +110,20 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
splitsigs = switchtupleunion(sig)
for sig_n in splitsigs
result = abstract_call_method(interp, method, sig_n, svec(), multiple_matches, sv)
rt, edge = result.rt, result.edge
if edge !== nothing
push!(edges, edge)
end
rt = result.rt
edge = result.edge
edge !== nothing && push!(edges, edge)
this_argtypes = isa(matches, MethodMatches) ? argtypes : matches.applicable_argtypes[i]
this_arginfo = ArgInfo(fargs, this_argtypes)
const_call_result = abstract_call_method_with_const_args(interp, result,
f, this_arginfo, match, sv)
effects = result.edge_effects
const_result = nothing
if const_call_result !== nothing
if const_call_result.rt rt
(; rt, effects, const_result) = const_call_result
const_rt = const_call_result.rt
if const_rt rt
rt = const_rt
(; effects, const_result) = const_call_result
end
end
tristate_merge!(sv, effects)
Expand All @@ -135,6 +136,8 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
break
end
end
this_conditional = ignorelimited(this_rt)
this_rt = widenwrappedconditional(this_rt)
else
if infer_compilation_signature(interp)
# Also infer the compilation signature for this method, so it's available
Expand All @@ -151,10 +154,10 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
end

result = abstract_call_method(interp, method, sig, match.sparams, multiple_matches, sv)
this_rt, edge = result.rt, result.edge
if edge !== nothing
push!(edges, edge)
end
this_conditional = ignorelimited(result.rt)
this_rt = widenwrappedconditional(result.rt)
edge = result.edge
edge !== nothing && push!(edges, edge)
# try constant propagation with argtypes for this match
# this is in preparation for inlining, or improving the return result
this_argtypes = isa(matches, MethodMatches) ? argtypes : matches.applicable_argtypes[i]
Expand All @@ -164,10 +167,12 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
effects = result.edge_effects
const_result = nothing
if const_call_result !== nothing
this_const_rt = const_call_result.rt
# return type of const-prop' inference can be wider than that of non const-prop' inference
this_const_conditional = ignorelimited(const_call_result.rt)
this_const_rt = widenwrappedconditional(const_call_result.rt)
# return type of const-prop' inference can be wider than that of non const-prop' inference
# e.g. in cases when there are cycles but cached result is still accurate
if this_const_rt this_rt
this_conditional = this_const_conditional
this_rt = this_const_rt
(; effects, const_result) = const_call_result
end
Expand All @@ -178,8 +183,6 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
any_const_result = true
end
end
this_conditional = ignorelimited(this_rt)
this_rt = widenwrappedconditional(this_rt)
@assert !(this_conditional isa Conditional) "invalid lattice element returned from inter-procedural context"
seen += 1
rettype = tmerge(rettype, this_rt)
Expand Down
7 changes: 7 additions & 0 deletions test/compiler/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2032,6 +2032,13 @@ end
end
@test ts == Any[Any]
end

# a tricky case: if constant inference derives `Const` while non-constant inference has
# derived `InterConditional`, we should not discard that constant information
iszero_simple(x) = x === 0
@test Base.return_types() do
iszero_simple(0) ? nothing : missing
end |> only === Nothing
end

@testset "branching on conditional object" begin
Expand Down

0 comments on commit 94b7154

Please sign in to comment.