Skip to content

Commit d75f726

Browse files
topolarityXnartharax
authored andcommitted
effects: Do not over-taint :terminates in abstract cycle (JuliaLang#49119)
We already infer non-termination as part of the conservative effects we assume at the point in the call-graph that recursion is detected. As a result, it should be sound to allow this to propagate through the Effects system naturally rather than eagerly marking our callers as non-terminating. Doing this is important to avoid tainting non-termination from purely abstract cycles, where inference is forced to analyze methods that do not correspond to real calls at runtime, such as `Base._return_type`. Resolves JuliaLang#48983.
1 parent b3305ca commit d75f726

File tree

2 files changed

+11
-4
lines changed

2 files changed

+11
-4
lines changed

base/compiler/typeinfer.jl

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -779,13 +779,9 @@ function merge_call_chain!(interp::AbstractInterpreter, parent::InferenceState,
779779
# then add all backedges of parent <- parent.parent
780780
# and merge all of the callers into ancestor.callers_in_cycle
781781
# and ensure that walking the parent list will get the same result (DAG) from everywhere
782-
# Also taint the termination effect, because we can no longer guarantee the absence
783-
# of recursion.
784-
merge_effects!(interp, parent, Effects(EFFECTS_TOTAL; terminates=false))
785782
while true
786783
add_cycle_backedge!(parent, child, parent.currpc)
787784
union_caller_cycle!(ancestor, child)
788-
merge_effects!(interp, child, Effects(EFFECTS_TOTAL; terminates=false))
789785
child = parent
790786
child === ancestor && break
791787
parent = child.parent::InferenceState

test/compiler/effects.jl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -805,5 +805,16 @@ unknown_sparam_nothrow2(x::Ref{Ref{T}}) where T = (T; nothing)
805805
@test Core.Compiler.is_nothrow(Base.infer_effects(unknown_sparam_nothrow1, (Ref,)))
806806
@test Core.Compiler.is_nothrow(Base.infer_effects(unknown_sparam_nothrow2, (Ref{Ref{T}} where T,)))
807807

808+
# purely abstract recursion should not taint :terminates
809+
# https://github.com/JuliaLang/julia/issues/48983
810+
abstractly_recursive1() = abstractly_recursive2()
811+
abstractly_recursive2() = (Core.Compiler._return_type(abstractly_recursive1, Tuple{}); 1)
812+
abstractly_recursive3() = abstractly_recursive2()
813+
@test Core.Compiler.is_terminates(Base.infer_effects(abstractly_recursive3, ()))
814+
actually_recursive1(x) = actually_recursive2(x)
815+
actually_recursive2(x) = (x <= 0) ? 1 : actually_recursive1(x - 1)
816+
actually_recursive3(x) = actually_recursive2(x)
817+
@test !Core.Compiler.is_terminates(Base.infer_effects(actually_recursive3, (Int,)))
818+
808819
# https://github.com/JuliaLang/julia/issues/48856
809820
@test Base.ismutationfree(Vector{Any}) == false

0 commit comments

Comments
 (0)