diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 962574a3bf5ee..3e82aeb2245fa 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -154,8 +154,8 @@ linearindexing(::LinearIndexing, ::LinearIndexing) = LinearSlow() # The overall hierarchy is # `checkbounds(A, I...)` -> # `checkbounds(Bool, A, I...)` -> either of: -# - `checkbounds_indices(IA, I)` which calls `checkindex(Bool, inds, i)` -# - `checkbounds_logical(A, I)` when `I` is a single logical array +# - `checkbounds_logical(Bool, A, I)` when `I` is a single logical array +# - `checkbounds_indices(Bool, IA, I)` otherwise (uses `checkindex`) # # See the "boundscheck" devdocs for more information. # @@ -177,23 +177,35 @@ See also `checkindex`. """ function checkbounds(::Type{Bool}, A::AbstractArray, I...) @_inline_meta - checkbounds_indices(indices(A), I) + checkbounds_indices(Bool, indices(A), I) end function checkbounds(::Type{Bool}, A::AbstractArray, I::AbstractArray{Bool}) @_inline_meta - checkbounds_logical(A, I) + checkbounds_logical(Bool, A, I) end """ - checkbounds_indices(IA, I) + checkbounds(A, I...) + +Throw an error if the specified indices `I` are not in bounds for the given array `A`. +""" +function checkbounds(A::AbstractArray, I...) + @_inline_meta + checkbounds(Bool, A, I...) || throw_boundserror(A, I) + nothing +end +checkbounds(A::AbstractArray) = checkbounds(A, 1) # 0-d case -checks whether the "requested" indices in the tuple `I` fall within +""" + checkbounds_indices(Bool, IA, I) + +Return `true` if the "requested" indices in the tuple `I` fall within the bounds of the "permitted" indices specified by the tuple `IA`. This function recursively consumes elements of these tuples, usually in a 1-for-1 fashion, - checkbounds_indices((IA1, IA...), (I1, I...)) = checkindex(Bool, IA1, I1) & - checkbounds_indices(IA, I) + checkbounds_indices(Bool, (IA1, IA...), (I1, I...)) = checkindex(Bool, IA1, I1) & + checkbounds_indices(Bool, IA, I) Note that `checkindex` is being used to perform the actual bounds-check for a single dimension of the array. @@ -202,48 +214,55 @@ There are two important exceptions to the 1-1 rule: linear indexing and CartesianIndex{N}, both of which may "consume" more than one element of `IA`. """ -function checkbounds_indices(IA::Tuple, I::Tuple) +function checkbounds_indices(::Type{Bool}, IA::Tuple, I::Tuple) @_inline_meta - checkindex(Bool, IA[1], I[1]) & checkbounds_indices(tail(IA), tail(I)) + checkindex(Bool, IA[1], I[1]) & checkbounds_indices(Bool, tail(IA), tail(I)) end -checkbounds_indices(::Tuple{}, ::Tuple{}) = true -checkbounds_indices(::Tuple{}, I::Tuple{Any}) = (@_inline_meta; checkindex(Bool, 1:1, I[1])) -function checkbounds_indices(::Tuple{}, I::Tuple) +checkbounds_indices(::Type{Bool}, ::Tuple{}, ::Tuple{}) = true +checkbounds_indices(::Type{Bool}, ::Tuple{}, I::Tuple{Any}) = (@_inline_meta; checkindex(Bool, 1:1, I[1])) +function checkbounds_indices(::Type{Bool}, ::Tuple{}, I::Tuple) @_inline_meta - checkindex(Bool, 1:1, I[1]) & checkbounds_indices((), tail(I)) + checkindex(Bool, 1:1, I[1]) & checkbounds_indices(Bool, (), tail(I)) end -function checkbounds_indices(IA::Tuple{Any}, I::Tuple{Any}) +function checkbounds_indices(::Type{Bool}, IA::Tuple{Any}, I::Tuple{Any}) @_inline_meta checkindex(Bool, IA[1], I[1]) end -function checkbounds_indices(IA::Tuple, I::Tuple{Any}) +function checkbounds_indices(::Type{Bool}, IA::Tuple, I::Tuple{Any}) @_inline_meta checkindex(Bool, 1:prod(map(dimlength, IA)), I[1]) # linear indexing end """ - checkbounds_logical(A, I::AbstractArray{Bool}) + checkbounds_logical(Bool, A, I::AbstractArray{Bool}) -tests whether the logical array `I` is consistent with the indices of `A`. +Return `true` if the logical array `I` is consistent with the indices +of `A`. `I` and `A` should have the same size and compatible indices. """ -checkbounds_logical(A::AbstractArray, I::AbstractArray{Bool}) = indices(A) == indices(I) -checkbounds_logical(A::AbstractArray, I::AbstractVector{Bool}) = length(A) == length(I) -checkbounds_logical(A::AbstractVector, I::AbstractArray{Bool}) = length(A) == length(I) -checkbounds_logical(A::AbstractVector, I::AbstractVector{Bool}) = indices(A) == indices(I) - -throw_boundserror(A, I) = (@_noinline_meta; throw(BoundsError(A, I))) +function checkbounds_logical(::Type{Bool}, A::AbstractArray, I::AbstractArray{Bool}) + indices(A) == indices(I) +end +function checkbounds_logical(::Type{Bool}, A::AbstractArray, I::AbstractVector{Bool}) + length(A) == length(I) +end +function checkbounds_logical(::Type{Bool}, A::AbstractVector, I::AbstractArray{Bool}) + length(A) == length(I) +end +function checkbounds_logical(::Type{Bool}, A::AbstractVector, I::AbstractVector{Bool}) + indices(A) == indices(I) +end """ - checkbounds(A, I...) + checkbounds_logical(A, I::AbstractArray{Bool}) -Throw an error if the specified indices `I` are not in bounds for the given array `A`. +Throw an error if the logical array `I` is inconsistent with the indices of `A`. """ -function checkbounds(A::AbstractArray, I...) - @_inline_meta - checkbounds(Bool, A, I...) || throw_boundserror(A, I) +function checkbounds_logical(A, I::AbstractVector{Bool}) + checkbounds_logical(Bool, A, I) || throw_boundserror(A, I) nothing end -checkbounds(A::AbstractArray) = checkbounds(A, 1) # 0-d case + +throw_boundserror(A, I) = (@_noinline_meta; throw(BoundsError(A, I))) @generated function trailingsize{T,N,n}(A::AbstractArray{T,N}, ::Type{Val{n}}) (isa(n, Int) && isa(N, Int)) || error("Must have concrete type") diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 6fe1ca22f52d5..199ef18cf5afc 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -154,31 +154,31 @@ end # IteratorsMD using .IteratorsMD ## Bounds-checking with CartesianIndex -@inline checkbounds_indices(::Tuple{}, I::Tuple{CartesianIndex,Vararg{Any}}) = - checkbounds_indices((), (I[1].I..., tail(I)...)) -@inline checkbounds_indices(IA::Tuple{Any}, I::Tuple{CartesianIndex,Vararg{Any}}) = - checkbounds_indices(IA, (I[1].I..., tail(I)...)) -@inline checkbounds_indices(IA::Tuple, I::Tuple{CartesianIndex,Vararg{Any}}) = - checkbounds_indices(IA, (I[1].I..., tail(I)...)) +@inline checkbounds_indices(::Type{Bool}, ::Tuple{}, I::Tuple{CartesianIndex,Vararg{Any}}) = + checkbounds_indices(Bool, (), (I[1].I..., tail(I)...)) +@inline checkbounds_indices(::Type{Bool}, IA::Tuple{Any}, I::Tuple{CartesianIndex,Vararg{Any}}) = + checkbounds_indices(Bool, IA, (I[1].I..., tail(I)...)) +@inline checkbounds_indices(::Type{Bool}, IA::Tuple, I::Tuple{CartesianIndex,Vararg{Any}}) = + checkbounds_indices(Bool, IA, (I[1].I..., tail(I)...)) # Support indexing with an array of CartesianIndex{N}s # Here we try to consume N of the indices (if there are that many available) # The first two simply handle ambiguities -@inline function checkbounds_indices{N}(::Tuple{}, I::Tuple{AbstractArray{CartesianIndex{N}},Vararg{Any}}) - checkindex(Bool, (), I[1]) & checkbounds_indices((), tail(I)) +@inline function checkbounds_indices{N}(::Type{Bool}, ::Tuple{}, I::Tuple{AbstractArray{CartesianIndex{N}},Vararg{Any}}) + checkindex(Bool, (), I[1]) & checkbounds_indices(Bool, (), tail(I)) end -@inline function checkbounds_indices{N}(IA::Tuple{Any}, I::Tuple{AbstractArray{CartesianIndex{N}},Vararg{Any}}) - checkindex(Bool, IA, I[1]) & checkbounds_indices((), tail(I)) +@inline function checkbounds_indices{N}(::Type{Bool}, IA::Tuple{Any}, I::Tuple{AbstractArray{CartesianIndex{N}},Vararg{Any}}) + checkindex(Bool, IA, I[1]) & checkbounds_indices(Bool, (), tail(I)) end -@inline function checkbounds_indices{N}(IA::Tuple, I::Tuple{AbstractArray{CartesianIndex{N}},Vararg{Any}}) +@inline function checkbounds_indices{N}(::Type{Bool}, IA::Tuple, I::Tuple{AbstractArray{CartesianIndex{N}},Vararg{Any}}) IA1, IArest = IteratorsMD.split(IA, Val{N}) - checkindex(Bool, IA1, I[1]) & checkbounds_indices(IArest, tail(I)) + checkindex(Bool, IA1, I[1]) & checkbounds_indices(Bool, IArest, tail(I)) end function checkindex{N}(::Type{Bool}, inds::Tuple, I::AbstractArray{CartesianIndex{N}}) b = true for i in I - b &= checkbounds_indices(inds, (i,)) + b &= checkbounds_indices(Bool, inds, (i,)) end b end diff --git a/doc/devdocs/boundscheck.rst b/doc/devdocs/boundscheck.rst index 1d324e9aa546b..6a85da5c0225e 100644 --- a/doc/devdocs/boundscheck.rst +++ b/doc/devdocs/boundscheck.rst @@ -67,8 +67,8 @@ The overall hierarchy is: | ``checkbounds(A, I...)`` which calls | ``checkbounds(Bool, A, I...)`` which calls either of: -| ``checkbounds_logical(A, I)`` when ``I`` is a single logical array -| ``checkbounds_indices(indices(A), I)`` otherwise +| ``checkbounds_logical(Bool, A, I)`` when ``I`` is a single logical array +| ``checkbounds_indices(Bool, indices(A), I)`` otherwise | Here ``A`` is the array, and ``I`` contains the "requested" indices. @@ -85,8 +85,8 @@ dimensions handled by calling another important function, ``checkindex``: typically, :: - checkbounds_indices((IA1, IA...), (I1, I...)) = checkindex(Bool, IA1, I1) & - checkbounds_indices(IA, I) + checkbounds_indices(Bool, (IA1, IA...), (I1, I...)) = checkindex(Bool, IA1, I1) & + checkbounds_indices(Bool, IA, I) so ``checkindex`` checks a single dimension. All of these functions, including the unexported ``checkbounds_indices`` and diff --git a/doc/stdlib/arrays.rst b/doc/stdlib/arrays.rst index b62866876cba1..7b222dea1416c 100644 --- a/doc/stdlib/arrays.rst +++ b/doc/stdlib/arrays.rst @@ -1080,4 +1080,3 @@ dense counterparts. The following functions are specific to sparse arrays. For additional (algorithmic) information, and for versions of these methods that forgo argument checking, see (unexported) parent methods :func:`Base.SparseArrays.unchecked_noalias_permute!` and :func:`Base.SparseArrays.unchecked_aliasing_permute!`\ . See also: :func:`Base.SparseArrays.permute` - diff --git a/test/replutil.jl b/test/replutil.jl index 3bd83c8f7f29a..c0a34b29df5e9 100644 --- a/test/replutil.jl +++ b/test/replutil.jl @@ -222,6 +222,9 @@ let undefvar err_str = @except_strbt (-1)^0.25 DomainError @test contains(err_str, "Exponentiation yielding a complex result requires a complex argument") + err_str = @except_str (1,2,3)[4] BoundsError + @test err_str == "BoundsError: attempt to access (1,2,3)\n at index [4]" + err_str = @except_str [5,4,3][-2,1] BoundsError @test err_str == "BoundsError: attempt to access 3-element Array{$Int,1} at index [-2,1]" err_str = @except_str [5,4,3][1:5] BoundsError