From 43235e9cc3fe4e790e4ad04f39b7d00b22fa71d9 Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Tue, 7 Feb 2023 20:57:00 +0000 Subject: [PATCH] [compiler] Teach type inference that `GotoIfNot` can throw Previously, the effects system would ignore certain cases where `GotoIfNot` nodes would be capable of throwing; this resulted in simple examples such as the following being marked as `nothrow`: ``` julia> foo(x) = x > 0 ? x : 0 Base.infer_effects(foo, (Missing,)) (+c,+e,+n,+t,+s,+m,+i) ``` With this change, we correctly notice when a `GotoIfNot` node is given a non-`Bool` condition, annotate the basic block as possibly throwing, and further end type inference if the condition is provably non-`Bool`. --- base/compiler/abstractinterpretation.jl | 9 +++++++++ test/compiler/effects.jl | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 58d4cbf10a82c..ef49d56895890 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -2893,6 +2893,7 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) empty!(frame.pclimitations) @goto find_next_bb end + orig_condt = condt if !(isa(condt, Const) || isa(condt, Conditional)) && isa(condx, SlotNumber) # if this non-`Conditional` object is a slot, we form and propagate # the conditional constraint on it @@ -2924,6 +2925,14 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) handle_control_backedge!(interp, frame, currpc, stmt.dest) @goto branch else + if !โŠ‘(๐•ƒแตข, orig_condt, Bool) + merge_effects!(interp, frame, EFFECTS_THROWS) + if !hasintersect(widenconst(orig_condt), Bool) + ssavaluetypes[currpc] = Bottom + @goto find_next_bb + end + end + # We continue with the true branch, but process the false # branch here. if isa(condt, Conditional) diff --git a/test/compiler/effects.jl b/test/compiler/effects.jl index 656ac9268dcb4..048e5146e2dcb 100644 --- a/test/compiler/effects.jl +++ b/test/compiler/effects.jl @@ -743,3 +743,11 @@ end @test Base.ismutationfree(Type{Union{}}) @test Core.Compiler.is_total(Base.infer_effects(typejoin, ())) + + +# GotoIfNot should properly mark itself as throwing when given a non-Bool +# https://github.com/JuliaLang/julia/pull/48583 +gotoifnot_throw_check_48583(x) = x ? x : 0 +@test !Core.Compiler.is_nothrow(Base.infer_effects(gotoifnot_throw_check_48583, (Missing,))) +@test !Core.Compiler.is_nothrow(Base.infer_effects(gotoifnot_throw_check_48583, (Any,))) +@test Core.Compiler.is_nothrow(Base.infer_effects(gotoifnot_throw_check_48583, (Bool,)))