diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 2508e0f9ae007a..aa8bfbeeb887b9 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -75,13 +75,29 @@ linearindexing{A<:Array}(::Type{A}) = LinearFast() linearindexing{A<:Range}(::Type{A}) = LinearFast() ## Bounds checking ## -_checkbounds(sz::Int, i::Int, A::AbstractArray, J...) = 1 <= i <= sz || throw(BoundsError(A, J)) -_checkbounds(sz::Int, i::Real, A::AbstractArray, J...) = _checkbounds(sz, to_index(i), A, J...) -_checkbounds(sz::Int, I::AbstractVector{Bool}, A::AbstractArray, J...) = length(I) == sz || throw(BoundsError(A, J)) -_checkbounds(sz::Int, r::Range{Int}, A::AbstractArray, J...) = isempty(r) || (minimum(r) >= 1 && maximum(r) <= sz) || throw(BoundsError(A, J)) -_checkbounds{T<:Real}(sz::Int, r::Range{T}, A::AbstractArray, J...) = _checkbounds(sz, to_index(r), A, J...) - -function _checkbounds{T <: Real}(sz::Int, I::AbstractArray{T}, A::AbstractArray, J...) +# note: we force inlining of all _checkbounds methods in order to force splatting +# of the arguments and avoid allocating tuples (unless a BoundsError is raised). +# we can't use @inline at this stage in the build process, since it indirectly uses +# _checkbounds itself (see expr.jl), so we define a crude version +macro _crude_inline(f) + body = arrayref(f.args, 2) + oldargs = body.args + newargs = Array(Any, arraylen(oldargs) + 1) + arrayset(newargs, convert(Any, Expr(:meta, :inline)), 1) + for i = 1:arraylen(oldargs) + arrayset(newargs, convert(Any, arrayref(oldargs, i)), i+1) + end + body.args = newargs + esc(f) +end + +@_crude_inline _checkbounds(sz::Int, i::Int, A::AbstractArray, J...) = 1 <= i <= sz || throw(BoundsError(A, J)) +@_crude_inline _checkbounds(sz::Int, i::Real, A::AbstractArray, J...) = _checkbounds(sz, to_index(i), A, J...) +@_crude_inline _checkbounds(sz::Int, I::AbstractVector{Bool}, A::AbstractArray, J...) = length(I) == sz || throw(BoundsError(A, J)) +@_crude_inline _checkbounds(sz::Int, r::Range{Int}, A::AbstractArray, J...) = isempty(r) || (minimum(r) >= 1 && maximum(r) <= sz) || throw(BoundsError(A, J)) +@_crude_inline _checkbounds{T<:Real}(sz::Int, r::Range{T}, A::AbstractArray, J...) = _checkbounds(sz, to_index(r), A, J...) + +@_crude_inline function _checkbounds{T <: Real}(sz::Int, I::AbstractArray{T}, A::AbstractArray, J...) for i in I _checkbounds(sz, i, A, J...) end diff --git a/base/base.jl b/base/base.jl index daf59b0f9433aa..f61575b0ec13f8 100644 --- a/base/base.jl +++ b/base/base.jl @@ -5,8 +5,18 @@ typealias Callable Union(Function,DataType) const Bottom = Union() # constructors for Core types in boot.jl +call(T::Type{Expr}, args::ANY...) = _expr(args...) call(T::Type{BoundsError}) = Core.call(T) -call(T::Type{BoundsError}, args...) = Core.call(T, args...) +# note: manual @noinline to avoid tuple allocations unless an error actually occurs +# the following should do: +# @noinline call(T::Type{BoundsError}, args...) = Core.call(T, args...) +# except we can't use @noinline at this stage +eval(:(function call(T::Type{BoundsError}, args...) + $(Expr(:meta, :noinline)) + begin + Core.call(T, args...) + end + end)) call(T::Type{DivideError}) = Core.call(T) call(T::Type{DomainError}) = Core.call(T) call(T::Type{OverflowError}) = Core.call(T) @@ -22,7 +32,6 @@ call(T::Type{ASCIIString}, d::Array{UInt8,1}) = Core.call(T, d) call(T::Type{UTF8String}, d::Array{UInt8,1}) = Core.call(T, d) call(T::Type{TypeVar}, args...) = Core.call(T, args...) call(T::Type{TypeConstructor}, args...) = Core.call(T, args...) -call(T::Type{Expr}, args::ANY...) = _expr(args...) call(T::Type{LineNumberNode}, n::Int) = Core.call(T, n) call(T::Type{LabelNode}, n::Int) = Core.call(T, n) call(T::Type{GotoNode}, n::Int) = Core.call(T, n)