Skip to content

Commit 15f7db8

Browse files
committed
inference: fix bad effects for recursion
Effects are not converged, so they need to always be correct, and not a bestguess, even during recursion.
1 parent 00c6ea7 commit 15f7db8

File tree

4 files changed

+59
-31
lines changed

4 files changed

+59
-31
lines changed

base/compiler/typeinfer.jl

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,34 @@ function cycle_fix_limited(@nospecialize(typ), sv::InferenceState)
432432
return typ
433433
end
434434

435+
function adjust_effects(ipo_effects::Effects, def::Method)
436+
# override the analyzed effects using manually annotated effect settings
437+
override = decode_effects_override(def.purity)
438+
if is_effect_overridden(override, :consistent)
439+
ipo_effects = Effects(ipo_effects; consistent=ALWAYS_TRUE)
440+
end
441+
if is_effect_overridden(override, :effect_free)
442+
ipo_effects = Effects(ipo_effects; effect_free=ALWAYS_TRUE)
443+
end
444+
if is_effect_overridden(override, :nothrow)
445+
ipo_effects = Effects(ipo_effects; nothrow=true)
446+
end
447+
if is_effect_overridden(override, :terminates_globally)
448+
ipo_effects = Effects(ipo_effects; terminates=true)
449+
end
450+
if is_effect_overridden(override, :notaskstate)
451+
ipo_effects = Effects(ipo_effects; notaskstate=true)
452+
end
453+
if is_effect_overridden(override, :inaccessiblememonly)
454+
ipo_effects = Effects(ipo_effects; inaccessiblememonly=ALWAYS_TRUE)
455+
end
456+
if is_effect_overridden(override, :noub)
457+
ipo_effects = Effects(ipo_effects; noub=ALWAYS_TRUE)
458+
end
459+
return ipo_effects
460+
end
461+
462+
435463
function adjust_effects(sv::InferenceState)
436464
ipo_effects = sv.ipo_effects
437465

@@ -478,28 +506,7 @@ function adjust_effects(sv::InferenceState)
478506
# override the analyzed effects using manually annotated effect settings
479507
def = sv.linfo.def
480508
if isa(def, Method)
481-
override = decode_effects_override(def.purity)
482-
if is_effect_overridden(override, :consistent)
483-
ipo_effects = Effects(ipo_effects; consistent=ALWAYS_TRUE)
484-
end
485-
if is_effect_overridden(override, :effect_free)
486-
ipo_effects = Effects(ipo_effects; effect_free=ALWAYS_TRUE)
487-
end
488-
if is_effect_overridden(override, :nothrow)
489-
ipo_effects = Effects(ipo_effects; nothrow=true)
490-
end
491-
if is_effect_overridden(override, :terminates_globally)
492-
ipo_effects = Effects(ipo_effects; terminates=true)
493-
end
494-
if is_effect_overridden(override, :notaskstate)
495-
ipo_effects = Effects(ipo_effects; notaskstate=true)
496-
end
497-
if is_effect_overridden(override, :inaccessiblememonly)
498-
ipo_effects = Effects(ipo_effects; inaccessiblememonly=ALWAYS_TRUE)
499-
end
500-
if is_effect_overridden(override, :noub)
501-
ipo_effects = Effects(ipo_effects; noub=ALWAYS_TRUE)
502-
end
509+
ipo_effects = adjust_effects(ipo_effects, def)
503510
end
504511

505512
return ipo_effects
@@ -850,16 +857,18 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize
850857
end
851858
typeinf(interp, frame)
852859
update_valid_age!(caller, frame.valid_worlds)
853-
edge = is_inferred(frame) ? mi : nothing
854-
return EdgeCallResult(frame.bestguess, edge, frame.ipo_effects) # effects are adjusted already within `finish`
860+
isinferred = is_inferred(frame)
861+
edge = isinferred ? mi : nothing
862+
effects = isinferred ? frame.ipo_effects : adjust_effects(Effects(), method) # effects are adjusted already within `finish` for ipo_effects
863+
return EdgeCallResult(frame.bestguess, edge, effects)
855864
elseif frame === true
856865
# unresolvable cycle
857866
return EdgeCallResult(Any, nothing, Effects())
858867
end
859868
# return the current knowledge about this cycle
860869
frame = frame::InferenceState
861870
update_valid_age!(caller, frame.valid_worlds)
862-
return EdgeCallResult(frame.bestguess, nothing, adjust_effects(frame))
871+
return EdgeCallResult(frame.bestguess, nothing, adjust_effects(Effects(), method))
863872
end
864873

865874
function cached_return_type(code::CodeInstance)

test/compiler/AbstractInterpreter.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ function CC.concrete_eval_eligible(interp::Issue48097Interp,
134134
end
135135
@overlay Issue48097MT @noinline Core.throw_inexacterror(f::Symbol, ::Type{T}, val) where {T} = return
136136
issue48097(; kwargs...) = return 42
137-
@test fully_eliminated(; interp=Issue48097Interp(), retval=42) do
137+
@test_broken fully_eliminated(; interp=Issue48097Interp(), retval=42) do
138138
issue48097(; a=1f0, b=1.0)
139139
end
140140

test/compiler/effects.jl

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,22 +89,31 @@ Base.@assume_effects :terminates_globally function recur_termination1(x)
8989
0 x < 20 || error("bad fact")
9090
return x * recur_termination1(x-1)
9191
end
92-
@test Core.Compiler.is_foldable(Base.infer_effects(recur_termination1, (Int,)))
93-
@test fully_eliminated() do
92+
@test_broken Core.Compiler.is_foldable(Base.infer_effects(recur_termination1, (Int,)))
93+
@test Core.Compiler.is_terminates(Base.infer_effects(recur_termination1, (Int,)))
94+
function recur_termination2()
95+
Base.@assume_effects :total !:terminates_globally
9496
recur_termination1(12)
9597
end
98+
@test_broken fully_eliminated(recur_termination2)
99+
@test fully_eliminated() do; recur_termination2(); end
96100

97101
Base.@assume_effects :terminates_globally function recur_termination21(x)
98102
x == 0 && return 1
99103
0 x < 20 || error("bad fact")
100104
return recur_termination22(x)
101105
end
102106
recur_termination22(x) = x * recur_termination21(x-1)
103-
@test Core.Compiler.is_foldable(Base.infer_effects(recur_termination21, (Int,)))
104-
@test Core.Compiler.is_foldable(Base.infer_effects(recur_termination22, (Int,)))
105-
@test fully_eliminated() do
107+
@test_broken Core.Compiler.is_foldable(Base.infer_effects(recur_termination21, (Int,)))
108+
@test_broken Core.Compiler.is_foldable(Base.infer_effects(recur_termination22, (Int,)))
109+
@test Core.Compiler.is_terminates(Base.infer_effects(recur_termination21, (Int,)))
110+
@test Core.Compiler.is_terminates(Base.infer_effects(recur_termination22, (Int,)))
111+
function recur_termination2x()
112+
Base.@assume_effects :total !:terminates_globally
106113
recur_termination21(12) + recur_termination22(12)
107114
end
115+
@test_broken fully_eliminated(recur_termination2x)
116+
@test fully_eliminated() do; recur_termination2x(); end
108117

109118
# anonymous function support for `@assume_effects`
110119
@test fully_eliminated() do

test/compiler/inference.jl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5172,3 +5172,13 @@ end |> only === Val{5}
51725172
@test fully_eliminated() do
51735173
length(continue_const_prop(1, 5))
51745174
end
5175+
5176+
# issue #51090
5177+
@noinline function bar51090(b)
5178+
b == 0 && return
5179+
r = foo51090(b - 1)
5180+
Base.donotdelete(b)
5181+
return r
5182+
end
5183+
foo51090(b) = return bar51090(b)
5184+
@test !fully_eliminated(foo51090, (Int,))

0 commit comments

Comments
 (0)