Skip to content

Commit

Permalink
inference: fix bad effects for recursion
Browse files Browse the repository at this point in the history
Effects are not converged, so they need to always be correct, and not a
bestguess, even during recursion.
  • Loading branch information
vtjnash committed Aug 30, 2023
1 parent af68fc3 commit 39911bc
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 31 deletions.
59 changes: 34 additions & 25 deletions base/compiler/typeinfer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,34 @@ function cycle_fix_limited(@nospecialize(typ), sv::InferenceState)
return typ
end

function adjust_effects(ipo_effects::Effects, def::Method)
# override the analyzed effects using manually annotated effect settings
override = decode_effects_override(def.purity)
if is_effect_overridden(override, :consistent)
ipo_effects = Effects(ipo_effects; consistent=ALWAYS_TRUE)
end
if is_effect_overridden(override, :effect_free)
ipo_effects = Effects(ipo_effects; effect_free=ALWAYS_TRUE)
end
if is_effect_overridden(override, :nothrow)
ipo_effects = Effects(ipo_effects; nothrow=true)
end
if is_effect_overridden(override, :terminates_globally)
ipo_effects = Effects(ipo_effects; terminates=true)
end
if is_effect_overridden(override, :notaskstate)
ipo_effects = Effects(ipo_effects; notaskstate=true)
end
if is_effect_overridden(override, :inaccessiblememonly)
ipo_effects = Effects(ipo_effects; inaccessiblememonly=ALWAYS_TRUE)
end
if is_effect_overridden(override, :noub)
ipo_effects = Effects(ipo_effects; noub=true)
end
return ipo_effects
end


function adjust_effects(sv::InferenceState)
ipo_effects = sv.ipo_effects

Expand Down Expand Up @@ -478,28 +506,7 @@ function adjust_effects(sv::InferenceState)
# override the analyzed effects using manually annotated effect settings
def = sv.linfo.def
if isa(def, Method)
override = decode_effects_override(def.purity)
if is_effect_overridden(override, :consistent)
ipo_effects = Effects(ipo_effects; consistent=ALWAYS_TRUE)
end
if is_effect_overridden(override, :effect_free)
ipo_effects = Effects(ipo_effects; effect_free=ALWAYS_TRUE)
end
if is_effect_overridden(override, :nothrow)
ipo_effects = Effects(ipo_effects; nothrow=true)
end
if is_effect_overridden(override, :terminates_globally)
ipo_effects = Effects(ipo_effects; terminates=true)
end
if is_effect_overridden(override, :notaskstate)
ipo_effects = Effects(ipo_effects; notaskstate=true)
end
if is_effect_overridden(override, :inaccessiblememonly)
ipo_effects = Effects(ipo_effects; inaccessiblememonly=ALWAYS_TRUE)
end
if is_effect_overridden(override, :noub)
ipo_effects = Effects(ipo_effects; noub=true)
end
ipo_effects = adjust_effects(ipo_effects, def)
end

return ipo_effects
Expand Down Expand Up @@ -850,16 +857,18 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize
end
typeinf(interp, frame)
update_valid_age!(caller, frame.valid_worlds)
edge = is_inferred(frame) ? mi : nothing
return EdgeCallResult(frame.bestguess, edge, frame.ipo_effects) # effects are adjusted already within `finish`
isinferred = is_inferred(frame)
edge = isinferred ? mi : nothing
effects = isinferred ? frame.ipo_effects : adjust_effects(Effects(), method) # effects are adjusted already within `finish` for ipo_effects
return EdgeCallResult(frame.bestguess, edge, effects)
elseif frame === true
# unresolvable cycle
return EdgeCallResult(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, nothing, adjust_effects(frame))
return EdgeCallResult(frame.bestguess, nothing, adjust_effects(Effects(), method))
end

function cached_return_type(code::CodeInstance)
Expand Down
2 changes: 1 addition & 1 deletion test/compiler/AbstractInterpreter.jl
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ function CC.concrete_eval_eligible(interp::Issue48097Interp,
end
@overlay Issue48097MT @noinline Core.throw_inexacterror(f::Symbol, ::Type{T}, val) where {T} = return
issue48097(; kwargs...) = return 42
@test fully_eliminated(; interp=Issue48097Interp(), retval=42) do
@test_broken fully_eliminated(; interp=Issue48097Interp(), retval=42) do
issue48097(; a=1f0, b=1.0)
end

Expand Down
19 changes: 14 additions & 5 deletions test/compiler/effects.jl
Original file line number Diff line number Diff line change
Expand Up @@ -89,22 +89,31 @@ Base.@assume_effects :terminates_globally function recur_termination1(x)
0 x < 20 || error("bad fact")
return x * recur_termination1(x-1)
end
@test Core.Compiler.is_foldable(Base.infer_effects(recur_termination1, (Int,)))
@test fully_eliminated() do
@test_broken Core.Compiler.is_foldable(Base.infer_effects(recur_termination1, (Int,)))
@test Core.Compiler.is_terminates(Base.infer_effects(recur_termination1, (Int,)))
function recur_termination2()
Base.@assume_effects :total !:terminates_globally
recur_termination1(12)
end
@test_broken fully_eliminated(recur_termination2)
@test fully_eliminated() do; recur_termination2(); end

Base.@assume_effects :terminates_globally function recur_termination21(x)
x == 0 && return 1
0 x < 20 || error("bad fact")
return recur_termination22(x)
end
recur_termination22(x) = x * recur_termination21(x-1)
@test Core.Compiler.is_foldable(Base.infer_effects(recur_termination21, (Int,)))
@test Core.Compiler.is_foldable(Base.infer_effects(recur_termination22, (Int,)))
@test fully_eliminated() do
@test_broken Core.Compiler.is_foldable(Base.infer_effects(recur_termination21, (Int,)))
@test_broken Core.Compiler.is_foldable(Base.infer_effects(recur_termination22, (Int,)))
@test Core.Compiler.is_terminates(Base.infer_effects(recur_termination21, (Int,)))
@test Core.Compiler.is_terminates(Base.infer_effects(recur_termination22, (Int,)))
function recur_termination2x()
Base.@assume_effects :total !:terminates_globally
recur_termination21(12) + recur_termination22(12)
end
@test_broken fully_eliminated(recur_termination2x)
@test fully_eliminated() do; recur_termination2x(); end

# anonymous function support for `@assume_effects`
@test fully_eliminated() do
Expand Down
10 changes: 10 additions & 0 deletions test/compiler/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5172,3 +5172,13 @@ end |> only === Val{5}
@test fully_eliminated() do
length(continue_const_prop(1, 5))
end

# issue #51090
@noinline function bar51090(b)
b == 0 && return
r = foo51090(b - 1)
Base.donotdelete(b)
return r
end
foo51090(b) = return bar51090(b)
@test !fully_eliminated(foo51090, (Int,))

0 comments on commit 39911bc

Please sign in to comment.