diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 5abc92ee19eb79..2783d05b8fa2bf 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -231,11 +231,46 @@ function finish!(interp::AbstractInterpreter, caller::InferenceState) return nothing end +# override statement flags for a frame within the cycle so that they are consistent with +# the new effects that are valid for the cycle +function cycle_ssaflags!(sv::InferenceState, callers_in_cycle::Vector{InferenceState}, + cycle_effects::Effects) + new_flags = flags_for_effects(cycle_effects) + old_currpc = sv.currpc + for i = 1:length(sv.src.ssaflags) + edges = sv.stmt_edges[i] + edges === nothing && continue + if any(callers_in_cycle) do caller::InferenceState + mi = caller.linfo + for edge in edges + edge isa MethodInstance || continue + if edge === mi + return true + end + end + return false + end + sv.currpc = i + set_curr_ssaflag!(sv, new_flags) + end + end + sv.currpc = old_currpc + return nothing +end + function _typeinf(interp::AbstractInterpreter, frame::InferenceState) typeinf_nocycle(interp, frame) || return false # frame is now part of a higher cycle # with no active ip's, frame is done frames = frame.callers_in_cycle - isempty(frames) && push!(frames, frame) + if isempty(frames) + push!(frames, frame) + has_cycle = false + elseif length(frames) == 1 + @assert frames[1] === frame "invalid callers_in_cycle" + has_cycle = false + else + has_cycle = true + end cycle_valid_worlds = WorldRange() cycle_effects = EFFECTS_TOTAL for caller in frames @@ -251,6 +286,7 @@ function _typeinf(interp::AbstractInterpreter, frame::InferenceState) for caller in frames caller.valid_worlds = cycle_valid_worlds caller.ipo_effects = cycle_effects + has_cycle && cycle_ssaflags!(caller, frames, cycle_effects) finish(caller, caller.interp) end for caller in frames diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index c45f5f11bf62e9..eca937dddc5aba 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -5291,6 +5291,15 @@ end foo51090(b) = return bar51090(b) @test !fully_eliminated(foo51090, (Int,)) +Base.@assume_effects :terminates_globally @noinline function bar51090_terminates(b) + b == 0 && return + r = foo51090_terminates(b - 1) + Base.donotdelete(b) + return r +end +foo51090_terminates(b) = return bar51090_terminates(b) +@test !fully_eliminated(foo51090_terminates, (Int,)) + # exploit throwness from concrete eval for intrinsics @test Base.return_types() do Base.or_int(true, 1)