diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index 1428d4b5ec34f..115085591667d 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -830,31 +830,38 @@ function ((; sv)::ScanStmt)(inst::Instruction, lstmt::Int, bb::Int) stmt_inconsistent = scan_inconsistency!(inst, sv) - if stmt_inconsistent && inst.idx == lstmt - if isa(stmt, ReturnNode) && isdefined(stmt, :val) + if stmt_inconsistent + if !has_flag(inst[:flag], IR_FLAG_NOTHROW) + # Taint :consistent if this statement may raise since :consistent requires + # consistent termination. TODO: Separate :consistent_return and :consistent_termination from :consistent. sv.all_retpaths_consistent = false - elseif isa(stmt, GotoIfNot) - # Conditional Branch with inconsistent condition. - # If we do not know this function terminates, taint consistency, now, - # :consistent requires consistent termination. TODO: Just look at the - # inconsistent region. - if !sv.result.ipo_effects.terminates - sv.all_retpaths_consistent = false - elseif visit_conditional_successors(sv.lazypostdomtree, sv.ir, bb) do succ::Int - return any_stmt_may_throw(sv.ir, succ) - end - # check if this `GotoIfNot` leads to conditional throws, which taints consistency + end + if inst.idx == lstmt + if isa(stmt, ReturnNode) && isdefined(stmt, :val) sv.all_retpaths_consistent = false - else - (; cfg, domtree) = get!(sv.lazyagdomtree) - for succ in iterated_dominance_frontier(cfg, BlockLiveness(sv.ir.cfg.blocks[bb].succs, nothing), domtree) - if succ == length(cfg.blocks) - # Phi node in the virtual exit -> We have a conditional - # return. TODO: Check if all the retvals are egal. - sv.all_retpaths_consistent = false - else - visit_bb_phis!(sv.ir, succ) do phiidx::Int - push!(sv.inconsistent, phiidx) + elseif isa(stmt, GotoIfNot) + # Conditional Branch with inconsistent condition. + # If we do not know this function terminates, taint consistency, now, + # :consistent requires consistent termination. TODO: Just look at the + # inconsistent region. + if !sv.result.ipo_effects.terminates + sv.all_retpaths_consistent = false + elseif visit_conditional_successors(sv.lazypostdomtree, sv.ir, bb) do succ::Int + return any_stmt_may_throw(sv.ir, succ) + end + # check if this `GotoIfNot` leads to conditional throws, which taints consistency + sv.all_retpaths_consistent = false + else + (; cfg, domtree) = get!(sv.lazyagdomtree) + for succ in iterated_dominance_frontier(cfg, BlockLiveness(sv.ir.cfg.blocks[bb].succs, nothing), domtree) + if succ == length(cfg.blocks) + # Phi node in the virtual exit -> We have a conditional + # return. TODO: Check if all the retvals are egal. + sv.all_retpaths_consistent = false + else + visit_bb_phis!(sv.ir, succ) do phiidx::Int + push!(sv.inconsistent, phiidx) + end end end end diff --git a/test/compiler/effects.jl b/test/compiler/effects.jl index d3884bf2c73e2..36c28b6c844bf 100644 --- a/test/compiler/effects.jl +++ b/test/compiler/effects.jl @@ -1102,7 +1102,7 @@ function f3_optrefine(x) @fastmath sqrt(x) return x end -@test Core.Compiler.is_consistent(Base.infer_effects(f3_optrefine)) +@test Core.Compiler.is_consistent(Base.infer_effects(f3_optrefine, (Float64,))) # Check that :consistent is properly modeled for throwing statements const GLOBAL_MUTABLE_SWITCH = Ref{Bool}(false) @@ -1390,10 +1390,13 @@ let; Base.Experimental.@force_compile; func52843(); end @noinline f53613() = @assert isdefined(@__MODULE__, :v53613) g53613() = f53613() +h53613() = g53613() @test !Core.Compiler.is_consistent(Base.infer_effects(f53613)) -@test_broken !Core.Compiler.is_consistent(Base.infer_effects(g53613)) +@test !Core.Compiler.is_consistent(Base.infer_effects(g53613)) @test_throws AssertionError f53613() @test_throws AssertionError g53613() +@test_throws AssertionError h53613() global v53613 = nothing @test f53613() === nothing @test g53613() === nothing +@test h53613() === nothing