Skip to content

Commit 0b14b3e

Browse files
martinholtersvtjnash
authored andcommitted
Fix a precision issue in abstract_iteration (JuliaLang#41839)
If the first loop exits in the first iteration, the `statetype` is still `Bottom`. In that case, the new `stateordonet` needs to be determined with the two-arg version of `iterate` again. Explicitly test that inference produces a sound (and reasonably precise) result when splatting an iterator (in this case a long range) that allows constant-propagation up to the `MAX_TUPLE_SPLAT` limit. Fixes JuliaLang#41022 Co-authored-by: Jameson Nash <vtjnash@gmail.com>
1 parent d33f97a commit 0b14b3e

File tree

2 files changed

+41
-11
lines changed

2 files changed

+41
-11
lines changed

base/compiler/abstractinterpretation.jl

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -844,9 +844,11 @@ function abstract_iteration(interp::AbstractInterpreter, @nospecialize(itft), @n
844844
return ret, AbstractIterationInfo(calls)
845845
end
846846
if Nothing <: stateordonet_widened || length(ret) >= InferenceParams(interp).MAX_TUPLE_SPLAT
847+
stateordonet = stateordonet_widened
847848
break
848849
end
849850
if !isa(stateordonet_widened, DataType) || !(stateordonet_widened <: Tuple) || isvatuple(stateordonet_widened) || length(stateordonet_widened.parameters) != 2
851+
stateordonet = stateordonet_widened
850852
break
851853
end
852854
nstatetype = getfield_tfunc(stateordonet, Const(2))
@@ -864,27 +866,40 @@ function abstract_iteration(interp::AbstractInterpreter, @nospecialize(itft), @n
864866
end
865867
# From here on, we start asking for results on the widened types, rather than
866868
# the precise (potentially const) state type
867-
statetype = widenconst(statetype)
868-
valtype = widenconst(valtype)
869+
# statetype and valtype are reinitialized in the first iteration below from the
870+
# (widened) stateordonet, which has not yet been fully analyzed in the loop above
871+
statetype = Bottom
872+
valtype = Bottom
873+
may_have_terminated = Nothing <: stateordonet
869874
while valtype !== Any
870-
stateordonet = abstract_call_known(interp, iteratef, nothing, Any[Const(iteratef), itertype, statetype], sv).rt
871-
stateordonet = widenconst(stateordonet)
872-
nounion = typesubtract(stateordonet, Nothing, 0)
873-
if !isa(nounion, DataType) || !(nounion <: Tuple) || isvatuple(nounion) || length(nounion.parameters) != 2
875+
nounion = typeintersect(stateordonet, Tuple{Any,Any})
876+
if nounion !== Union{} && !isa(nounion, DataType)
877+
# nounion is of a type we cannot handle
874878
valtype = Any
875879
break
876880
end
877-
if nounion.parameters[1] <: valtype && nounion.parameters[2] <: statetype
881+
if nounion === Union{} || (nounion.parameters[1] <: valtype && nounion.parameters[2] <: statetype)
882+
# reached a fixpoint or iterator failed/gave invalid answer
878883
if typeintersect(stateordonet, Nothing) === Union{}
879-
# Reached a fixpoint, but Nothing is not possible => iterator is infinite or failing
880-
return Any[Bottom], nothing
884+
# ... but cannot terminate
885+
if !may_have_terminated
886+
# ... and cannot have terminated prior to this loop
887+
return Any[Bottom], nothing
888+
else
889+
# iterator may have terminated prior to this loop, but not during it
890+
valtype = Bottom
891+
end
881892
end
882893
break
883894
end
884895
valtype = tmerge(valtype, nounion.parameters[1])
885896
statetype = tmerge(statetype, nounion.parameters[2])
897+
stateordonet = abstract_call_known(interp, iteratef, nothing, Any[Const(iteratef), itertype, statetype], sv).rt
898+
stateordonet = widenconst(stateordonet)
899+
end
900+
if valtype !== Union{}
901+
push!(ret, Vararg{valtype})
886902
end
887-
push!(ret, Vararg{valtype})
888903
return ret, nothing
889904
end
890905

test/compiler/inference.jl

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2877,9 +2877,24 @@ partial_return_2(x) = Val{partial_return_1(x)[2]}
28772877

28782878
@test Base.return_types(partial_return_2, (Int,)) == Any[Type{Val{1}}]
28792879

2880-
# Precision of abstract_iteration
2880+
# Soundness and precision of abstract_iteration
2881+
f41839() = (1:100...,)
2882+
@test NTuple{100,Int} <: only(Base.return_types(f41839, ())) <: Tuple{Vararg{Int}}
28812883
f_splat(x) = (x...,)
28822884
@test Base.return_types(f_splat, (Pair{Int,Int},)) == Any[Tuple{Int, Int}]
2885+
@test Base.return_types(f_splat, (UnitRange{Int},)) == Any[Tuple{Vararg{Int}}]
2886+
struct Itr41839_1 end # empty or infinite
2887+
Base.iterate(::Itr41839_1) = rand(Bool) ? (nothing, nothing) : nothing
2888+
Base.iterate(::Itr41839_1, ::Nothing) = (nothing, nothing)
2889+
@test Base.return_types(f_splat, (Itr41839_1,)) == Any[Tuple{}]
2890+
struct Itr41839_2 end # empty or failing
2891+
Base.iterate(::Itr41839_2) = rand(Bool) ? (nothing, nothing) : nothing
2892+
Base.iterate(::Itr41839_2, ::Nothing) = error()
2893+
@test Base.return_types(f_splat, (Itr41839_2,)) == Any[Tuple{}]
2894+
struct Itr41839_3 end
2895+
Base.iterate(::Itr41839_3 ) = rand(Bool) ? nothing : (nothing, 1)
2896+
Base.iterate(::Itr41839_3 , i) = i < 16 ? (i, i + 1) : nothing
2897+
@test only(Base.return_types(f_splat, (Itr41839_3,))) <: Tuple{Vararg{Union{Nothing, Int}}}
28832898

28842899
# issue #32699
28852900
f32699(a) = (id = a[1],).id

0 commit comments

Comments
 (0)