Skip to content

Commit

Permalink
Simulate iteration protocal when inferring splatted arguments
Browse files Browse the repository at this point in the history
Closes #20518 and improves inference when splatting genetal iterables.
  • Loading branch information
martinholters committed Feb 9, 2017
1 parent 6fef09c commit 12d6eb1
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 3 deletions.
25 changes: 22 additions & 3 deletions base/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1421,13 +1421,32 @@ function precise_container_type(arg, typ, vtypes::VarTable, sv)
else
return tti.parameters
end
elseif tti <: AbstractArray
return Any[Vararg{eltype(tti)}]

This comment has been minimized.

Copy link
@vtjnash

vtjnash Feb 9, 2017

Member

Maybe worth keeping this case for <: Array{T}, as a performance optimization in inference?

This comment has been minimized.

Copy link
@martinholters

martinholters Feb 10, 2017

Author Member

I'd be surprised if there was a use case where this actually made a noticeable difference, but no harm in putting it back in. (If someone overwrites start(::Array) or next(::Array, ...) to misbehave, they'll be in trouble anyway.)

else
return Any[Vararg{Any}]
return Any[Vararg{abstract_iteration(tti, vtypes, sv)}]
end
end

# simulate iteration protocol on container type up to fixpoint
function abstract_iteration(itertype, vtypes::VarTable, sv)
if !isdefined(Main, :Base) || !isdefined(Main.Base, :start) || !isdefined(Main.Base, :next)
return Any
end
statetype = abstract_call(Main.Base.start, (), Any[Const(Main.Base.start), itertype], vtypes, sv)
valtype = Bottom
while valtype !== Any
nt = abstract_call(Main.Base.next, (), Any[Const(Main.Base.next), itertype, statetype], vtypes, sv)
if !isa(nt, DataType) || !(nt <: Tuple) || isvatuple(nt) || length(nt.parameters) != 2
return Any
end
if nt.parameters[1] <: valtype && nt.parameters[2] <: statetype
break
end
valtype = tmerge(valtype, nt.parameters[1])
statetype = tmerge(statetype, nt.parameters[2])
end
return valtype
end

# do apply(af, fargs...), where af is a function value
function abstract_apply(af::ANY, fargs, aargtypes::Vector{Any}, vtypes::VarTable, sv)
res = Union{}
Expand Down
7 changes: 7 additions & 0 deletions test/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,13 @@ function i20343()
f20343([1,2,3]..., 4)
end
@test Base.return_types(i20343, ()) == [Int8]
immutable Foo20518 <: AbstractVector{Int}; end # issue #20518; inference assumed AbstractArrays
Base.getindex(::Foo20518, ::Int) = "oops" # not to lie about their element type
Base.indices(::Foo20518) = (Base.OneTo(4),)
foo20518(xs::Any...) = -1
foo20518(xs::Int...) = [0]
bar20518(xs) = sum(foo20518(xs...))
@test bar20518(Foo20518()) == -1

# Inference for some type-level computation
fUnionAll{T}(::Type{T}) = Type{S} where S <: T
Expand Down

3 comments on commit 12d6eb1

@StefanKarpinski
Copy link
Member

Choose a reason for hiding this comment

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

This is a great example of how to add stuff to inference.

@vtjnash
Copy link
Member

Choose a reason for hiding this comment

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

Yes. We also need to somehow tell people that you can do cd("base"); include("coreimg.jl") to test any changes to inference.

@martinholters
Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, knowing that a bit earlier would have been quite helpful.

Please sign in to comment.