Skip to content

Commit

Permalink
Make similar faster and safer (#18107)
Browse files Browse the repository at this point in the history
* Make similar faster and safer

By converting the eltype argument into a ::Type{T} parameter, we avoid runtime method lookup.

Perhaps more importantly, the introduction of NeedsShaping makes `similar` safer, by preventing any possibility of an infinite recursion.

* Test that similar throws a MethodError for unsupported dims types
  • Loading branch information
timholy authored and tkelman committed Aug 19, 2016
1 parent a08bfcd commit 66bacec
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 7 deletions.
15 changes: 8 additions & 7 deletions base/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ typealias RangeIndex Union{Int, Range{Int}, AbstractUnitRange{Int}, Colon}
typealias DimOrInd Union{Integer, AbstractUnitRange}
typealias IntOrInd Union{Int, AbstractUnitRange}
typealias DimsOrInds{N} NTuple{N,DimOrInd}
typealias NeedsShaping Union{Tuple{Integer,Vararg{Integer}}, Tuple{OneTo,Vararg{OneTo}}}

macro _inline_pure_meta()
Expr(:meta, :inline, :pure)
Expand Down Expand Up @@ -420,14 +421,14 @@ different element type it will create a regular `Array` instead:
2.18425e-314 2.18425e-314 2.18425e-314 2.18425e-314
"""
similar{T}(a::AbstractArray{T}) = similar(a, T)
similar( a::AbstractArray, T::Type) = similar(a, T, to_shape(indices(a)))
similar{T}(a::AbstractArray{T}, dims::Tuple) = similar(a, T, to_shape(dims))
similar{T}(a::AbstractArray{T}, dims::DimOrInd...) = similar(a, T, to_shape(dims))
similar( a::AbstractArray, T::Type, dims::DimOrInd...) = similar(a, T, to_shape(dims))
similar( a::AbstractArray, T::Type, dims) = similar(a, T, to_shape(dims))
similar{T}(a::AbstractArray{T}) = similar(a, T)
similar{T}(a::AbstractArray, ::Type{T}) = similar(a, T, to_shape(indices(a)))
similar{T}(a::AbstractArray{T}, dims::Tuple) = similar(a, T, to_shape(dims))
similar{T}(a::AbstractArray{T}, dims::DimOrInd...) = similar(a, T, to_shape(dims))
similar{T}(a::AbstractArray, ::Type{T}, dims::DimOrInd...) = similar(a, T, to_shape(dims))
similar{T}(a::AbstractArray, ::Type{T}, dims::NeedsShaping) = similar(a, T, to_shape(dims))
# similar creates an Array by default
similar{N}(a::AbstractArray, T::Type, dims::Dims{N}) = Array{T,N}(dims)
similar{T,N}(a::AbstractArray, ::Type{T}, dims::Dims{N}) = Array{T,N}(dims)

to_shape(::Tuple{}) = ()
to_shape(dims::Dims) = dims
Expand Down
17 changes: 17 additions & 0 deletions test/offsetarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -369,3 +369,20 @@ for s = -5:5
end

end # let

# Check that similar throws a MethodError rather than a
# StackOverflowError if no appropriate method has been defined
# (#18107)
module SimilarUR
using Base.Test
immutable MyURange <: AbstractUnitRange{Int}
start::Int
stop::Int
end
ur = MyURange(1,3)
a = Array{Int}(2)
@test_throws MethodError similar(a, ur)
@test_throws MethodError similar(a, Float64, ur)
@test_throws MethodError similar(a, Float64, (ur,))
@test_throws MethodError similar(a, (2.0,3.0))
end

0 comments on commit 66bacec

Please sign in to comment.