Skip to content

Inference for splatting numbers (and more) #27434

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 33 additions & 10 deletions base/compiler/abstractinterpretation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -370,37 +370,57 @@ function precise_container_type(@nospecialize(arg), @nospecialize(typ), vtypes::
elseif tti0 <: Array
return Any[Vararg{eltype(tti0)}]
else
return Any[abstract_iteration(typ, vtypes, sv)]
return abstract_iteration(typ, vtypes, sv)
end
end

# simulate iteration protocol on container type up to fixpoint
function abstract_iteration(@nospecialize(itertype), vtypes::VarTable, sv::InferenceState)
tm = _topmod(sv)
if !isdefined(tm, :iterate) || !isconst(tm, :iterate)
return Vararg{Any}
if !isdefined(Main, :Base) || !isdefined(Main.Base, :iterate) || !isconst(Main.Base, :iterate)
return Any[Vararg{Any}]
end
iteratef = getfield(tm, :iterate)
iteratef = getfield(Main.Base, :iterate)
stateordonet = abstract_call(iteratef, (), Any[Const(iteratef), itertype], vtypes, sv)
# Return Bottom if this is not an iterator.
# WARNING: Changes to the iteration protocol must be reflected here,
# this is not just an optimization.
stateordonet === Bottom && return Bottom
stateordonet === Bottom && return Any[Bottom]
valtype = statetype = Bottom
while valtype !== Any
ret = Any[]
stateordonet = widenconst(stateordonet)
while !(Nothing <: stateordonet) && length(ret) < sv.params.MAX_TUPLETYPE_LEN
if !isa(stateordonet, DataType) || !(stateordonet <: Tuple) || isvatuple(stateordonet) || length(stateordonet.parameters) != 2
break
end
if stateordonet.parameters[2] <: statetype
# infinite (or failing) iterator
return Any[Bottom]
end
valtype = stateordonet.parameters[1]
statetype = stateordonet.parameters[2]
push!(ret, valtype)
stateordonet = abstract_call(iteratef, (), Any[Const(iteratef), itertype, statetype], vtypes, sv)
stateordonet = widenconst(stateordonet)
nounion = Nothing <: stateordonet ? typesubtract(stateordonet, Nothing) : stateordonet
end
if stateordonet === Nothing
return ret
end
while valtype !== Any
nounion = typesubtract(stateordonet, Nothing)
if !isa(nounion, DataType) || !(nounion <: Tuple) || isvatuple(nounion) || length(nounion.parameters) != 2
return Vararg{Any}
valtype = Any
break
end
if nounion.parameters[1] <: valtype && nounion.parameters[2] <: statetype
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy this conditional above? (or at least the nounion.parameters[2] <: statetype part of it). If the new statetype is narrower, this must have been an infinite iterator (or throws an error).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True. However, for an infinite iterator, we get a slightly wider type then, e.g. Tuple{Char,Vararg{Char,N} where N} instead of Body::Tuple{Char,Char,Char,Char,Char,Char,Char,Char,Char,Char,Char,Char,Char,Vararg{Char,N} where N} for splatting Iterators.repeated('a'). Probably doesn't matter. Or is your suggestion to return Any[Bottom} (instead of breaking out of the loop) then? While theoretically correct (or did I get something wrong?), that looks rather aggressive.

break
end
valtype = tmerge(valtype, nounion.parameters[1])
statetype = tmerge(statetype, nounion.parameters[2])
stateordonet = abstract_call(iteratef, (), Any[Const(iteratef), itertype, statetype], vtypes, sv)
stateordonet = widenconst(stateordonet)
end
return Vararg{valtype}
push!(ret, Vararg{valtype})
return ret
end

# do apply(af, fargs...), where af is a function value
Expand All @@ -422,6 +442,9 @@ function abstract_apply(@nospecialize(aft), fargs::Vector{Any}, aargtypes::Vecto
ctypes´ = []
for ti in (splitunions ? uniontypes(aargtypes[i]) : Any[aargtypes[i]])
cti = precise_container_type(fargs[i], ti, vtypes, sv)
if _any(t -> t === Bottom, cti)
continue
end
for ct in ctypes
if isvarargtype(ct[end])
tail = tuple_tail_elem(unwrapva(ct[end]), cti)
Expand Down
3 changes: 2 additions & 1 deletion base/number.jl
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,8 @@ julia> widemul(Float32(3.), 4.)
"""
widemul(x::Number, y::Number) = widen(x)*widen(y)

iterate(x::Number, done = false) = done ? nothing : (x, true)
iterate(x::Number) = (x, nothing)
iterate(x::Number, ::Any) = nothing
isempty(x::Number) = false
in(x::Number, y::Number) = x == y

Expand Down
14 changes: 14 additions & 0 deletions test/compiler/compiler.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1707,3 +1707,17 @@ function h27316()
return x
end
@test Tuple{Tuple{Vector{Int}}} <: Base.return_types(h27316, Tuple{})[1] == Union{Vector{Int}, Tuple{Any}} # we may be able to improve this bound in the future

# PR 27434, inference when splatting iterators with type-based state
splat27434(x) = (x...,)
struct Iterator27434
x::Int
y::Int
z::Int
end
Base.iterate(i::Iterator27434) = i.x, Val(1)
Base.iterate(i::Iterator27434, ::Val{1}) = i.y, Val(2)
Base.iterate(i::Iterator27434, ::Val{2}) = i.z, Val(3)
Base.iterate(::Iterator27434, ::Any) = nothing
@test @inferred splat27434(Iterator27434(1, 2, 3)) == (1, 2, 3)
@test Core.Compiler.return_type(splat27434, Tuple{typeof(Iterators.repeated(1))}) == Union{}
5 changes: 5 additions & 0 deletions test/core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6235,3 +6235,8 @@ function f27597(y)
end
@test f27597([1]) == [1]
@test f27597([]) == 1:0

# issue #22291
wrap22291(ind) = (ind...,)
@test @inferred(wrap22291(1)) == (1,)
@test @inferred(wrap22291((1, 2))) == (1, 2)