Skip to content

Commit

Permalink
inference: followups for #51754 (#52241)
Browse files Browse the repository at this point in the history
Composed of:

- typeinf_local: factor into `update_cycle_worklists!` utility
(78f7b4e)
- inference: fix exception type of `typename` call
(fac36d8)
- add missing type annotations
(7ce140e)
- inference: refine exct information if `:nothrow` is proven
(76143d3)
- ~~improve exception type inference for core math functions
(525bd6c)~~ Separated into another PR
  • Loading branch information
aviatesk authored Nov 21, 2023
2 parents 527af66 + 8dd0cf5 commit 0402c78
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 21 deletions.
37 changes: 22 additions & 15 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 Expand Up @@ -2136,7 +2137,7 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f),
argtypes = Any[typeof(<:), argtypes[3], argtypes[2]]
return abstract_call_known(interp, <:, ArgInfo(fargs, argtypes), si, sv, max_methods)
elseif la == 2 && istopfunction(f, :typename)
return CallMeta(typename_static(argtypes[2]), Any, EFFECTS_TOTAL, MethodResultPure())
return CallMeta(typename_static(argtypes[2]), Bottom, EFFECTS_TOTAL, MethodResultPure())
elseif f === Core._hasmethod
return _hasmethod_tfunc(interp, argtypes, sv)
end
Expand Down Expand Up @@ -3086,6 +3087,14 @@ function propagate_to_error_handler!(frame::InferenceState, currpc::Int, W::BitS
end
end

function update_cycle_worklists!(callback, frame::InferenceState)
for (caller, caller_pc) in frame.cycle_backedges
if callback(caller, caller_pc)
push!(caller.ip, block_for_inst(caller.cfg, caller_pc))
end
end
end

# make as much progress on `frame` as possible (without handling cycles)
function typeinf_local(interp::AbstractInterpreter, frame::InferenceState)
@assert !is_inferred(frame)
Expand Down Expand Up @@ -3204,11 +3213,9 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState)
elseif isa(stmt, ReturnNode)
rt = abstract_eval_value(interp, stmt.val, currstate, frame)
if update_bestguess!(interp, frame, currstate, rt)
for (caller, caller_pc) in frame.cycle_backedges
if caller.ssavaluetypes[caller_pc] !== Any
# no reason to revisit if that call-site doesn't affect the final result
push!(caller.ip, block_for_inst(caller.cfg, caller_pc))
end
update_cycle_worklists!(frame) do caller::InferenceState, caller_pc::Int
# no reason to revisit if that call-site doesn't affect the final result
return caller.ssavaluetypes[caller_pc] !== Any
end
end
ssavaluetypes[frame.currpc] = Any
Expand All @@ -3231,11 +3238,11 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState)
if cur_hand == 0
if !(𝕃ₚ, exct, frame.exc_bestguess)
frame.exc_bestguess = tmerge(𝕃ₚ, frame.exc_bestguess, exct)
for (caller, caller_pc) in frame.cycle_backedges
handler = caller.handler_at[caller_pc][1]
if (handler == 0 ? caller.exc_bestguess : caller.handlers[handler].exct) !== Any
push!(caller.ip, block_for_inst(caller.cfg, caller_pc))
end
update_cycle_worklists!(frame) do caller::InferenceState, caller_pc::Int
caller_handler = caller.handler_at[caller_pc][1]
caller_exct = caller_handler == 0 ?
caller.exc_bestguess : caller.handlers[caller_handler].exct
return caller_exct !== Any
end
end
else
Expand Down
3 changes: 1 addition & 2 deletions base/compiler/ssair/irinterp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ function abstract_call(interp::AbstractInterpreter, arginfo::ArgInfo, irsv::IRIn
return RTEffects(rt, exct, effects)
end

function kill_block!(ir, bb)
function kill_block!(ir::IRCode, bb::Int)
# Kill the entire block
stmts = ir.cfg.blocks[bb].stmts
for bidx = stmts
Expand All @@ -64,7 +64,6 @@ function kill_block!(ir, bb)
return
end


function update_phi!(irsv::IRInterpretationState, from::Int, to::Int)
ir = irsv.ir
if length(ir.cfg.blocks[to].preds) == 0
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

2 comments on commit 0402c78

@nanosoldier
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Executing the daily package evaluation, I will reply here when finished:

@nanosoldier runtests(isdaily = true)

@nanosoldier
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The package evaluation job you requested has completed - possible new issues were detected.
The full report is available.

Please sign in to comment.