Skip to content

Commit

Permalink
inference: refine exct information if :nothrow is proven
Browse files Browse the repository at this point in the history
  • Loading branch information
aviatesk authored and mkitti committed Dec 9, 2023
1 parent e9a853a commit 2297b83
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 8 deletions.
9 changes: 5 additions & 4 deletions base/compiler/abstractinterpretation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1196,13 +1196,14 @@ function semi_concrete_eval_call(interp::AbstractInterpreter,
# state = InliningState(interp)
# ir = ssa_inlining_pass!(irsv.ir, state, propagate_inbounds(irsv))
effects = result.effects
if !is_nothrow(effects)
effects = Effects(effects; nothrow)
if nothrow
effects = Effects(effects; nothrow=true)
end
if noub
effects = Effects(effects; noub = ALWAYS_TRUE)
effects = Effects(effects; noub=ALWAYS_TRUE)
end
return ConstCallResults(rt, result.exct, SemiConcreteResult(mi, ir, effects), effects, mi)
exct = refine_exception_type(result.exct, effects)
return ConstCallResults(rt, exct, SemiConcreteResult(mi, ir, effects), effects, mi)
end
end
end
Expand Down
16 changes: 12 additions & 4 deletions base/compiler/typeinfer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,11 @@ function adjust_effects(sv::InferenceState)
return ipo_effects
end

function refine_exception_type(@nospecialize(exc_bestguess), ipo_effects::Effects)
ipo_effects.nothrow && return Bottom
return exc_bestguess
end

# inference completed on `me`
# update the MethodInstance
function finish(me::InferenceState, interp::AbstractInterpreter)
Expand Down Expand Up @@ -539,8 +544,8 @@ function finish(me::InferenceState, interp::AbstractInterpreter)
end
me.result.valid_worlds = me.valid_worlds
me.result.result = bestguess
me.result.ipo_effects = me.ipo_effects = adjust_effects(me)
me.result.exc_result = exc_bestguess
ipo_effects = me.result.ipo_effects = me.ipo_effects = adjust_effects(me)
me.result.exc_result = me.exc_bestguess = refine_exception_type(me.exc_bestguess, ipo_effects)

if limited_ret
# a parent may be cached still, but not this intermediate work:
Expand Down Expand Up @@ -862,20 +867,23 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize
isinferred = is_inferred(frame)
edge = isinferred ? mi : nothing
effects = isinferred ? frame.result.ipo_effects : adjust_effects(Effects(), method) # effects are adjusted already within `finish` for ipo_effects
exc_bestguess = refine_exception_type(frame.exc_bestguess, effects)
# propagate newly inferred source to the inliner, allowing efficient inlining w/o deserialization:
# note that this result is cached globally exclusively, we can use this local result destructively
volatile_inf_result = isinferred && let inferred_src = result.src
isa(inferred_src, CodeInfo) && (is_inlineable(inferred_src) || force_inline)
end ? VolatileInferenceResult(result) : nothing
return EdgeCallResult(frame.bestguess, frame.exc_bestguess, edge, effects, volatile_inf_result)
return EdgeCallResult(frame.bestguess, exc_bestguess, edge, effects, volatile_inf_result)
elseif frame === true
# unresolvable cycle
return EdgeCallResult(Any, Any, nothing, Effects())
end
# return the current knowledge about this cycle
frame = frame::InferenceState
update_valid_age!(caller, frame.valid_worlds)
return EdgeCallResult(frame.bestguess, frame.exc_bestguess, nothing, adjust_effects(Effects(), method))
effects = adjust_effects(Effects(), method)
exc_bestguess = refine_exception_type(frame.exc_bestguess, effects)
return EdgeCallResult(frame.bestguess, exc_bestguess, nothing, effects)
end

function cached_return_type(code::CodeInstance)
Expand Down
29 changes: 29 additions & 0 deletions test/compiler/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5558,3 +5558,32 @@ function foo_typed_throw_metherr()
return 1
end
@test Base.return_types(foo_typed_throw_metherr) |> only === Float64

# using `exct` information if `:nothrow` is proven
Base.@assume_effects :nothrow function sin_nothrow(x::Float64)
x == Inf && return zero(x)
return sin(x)
end
@test Base.infer_exception_type(sin_nothrow, (Float64,)) == Union{}
@test Base.return_types((Float64,)) do x
try
return sin_nothrow(x)
catch err
return err
end
end |> only === Float64
# for semi-concrete interpretation result too
Base.@constprop :aggressive function sin_maythrow(x::Float64, maythrow::Bool)
if maythrow
return sin(x)
else
return @noinline sin_nothrow(x)
end
end
@test Base.return_types((Float64,)) do x
try
return sin_maythrow(x, false)
catch err
return err
end
end |> only === Float64

0 comments on commit 2297b83

Please sign in to comment.