Skip to content

Commit 577d05f

Browse files
committed
Non-throwing boundscheck functions are written as f(Bool, args...)
1 parent 81f8d4f commit 577d05f

File tree

5 files changed

+69
-48
lines changed

5 files changed

+69
-48
lines changed

base/abstractarray.jl

+49-30
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,8 @@ linearindexing(::LinearIndexing, ::LinearIndexing) = LinearSlow()
154154
# The overall hierarchy is
155155
# `checkbounds(A, I...)` ->
156156
# `checkbounds(Bool, A, I...)` -> either of:
157-
# - `checkbounds_indices(IA, I)` which calls `checkindex(Bool, inds, i)`
158-
# - `checkbounds_logical(A, I)` when `I` is a single logical array
157+
# - `checkbounds_logical(Bool, A, I)` when `I` is a single logical array
158+
# - `checkbounds_indices(Bool, IA, I)` otherwise (uses `checkindex`)
159159
#
160160
# See the "boundscheck" devdocs for more information.
161161
#
@@ -177,23 +177,35 @@ See also `checkindex`.
177177
"""
178178
function checkbounds(::Type{Bool}, A::AbstractArray, I...)
179179
@_inline_meta
180-
checkbounds_indices(indices(A), I)
180+
checkbounds_indices(Bool, indices(A), I)
181181
end
182182
function checkbounds(::Type{Bool}, A::AbstractArray, I::AbstractArray{Bool})
183183
@_inline_meta
184-
checkbounds_logical(A, I)
184+
checkbounds_logical(Bool, A, I)
185185
end
186186

187187
"""
188-
checkbounds_indices(IA, I)
188+
checkbounds(A, I...)
189+
190+
Throw an error if the specified indices `I` are not in bounds for the given array `A`.
191+
"""
192+
function checkbounds(A::AbstractArray, I...)
193+
@_inline_meta
194+
checkbounds(Bool, A, I...) || throw_boundserror(A, I)
195+
nothing
196+
end
197+
checkbounds(A::AbstractArray) = checkbounds(A, 1) # 0-d case
189198

190-
checks whether the "requested" indices in the tuple `I` fall within
199+
"""
200+
checkbounds_indices(Bool, IA, I)
201+
202+
Return `true` if the "requested" indices in the tuple `I` fall within
191203
the bounds of the "permitted" indices specified by the tuple
192204
`IA`. This function recursively consumes elements of these tuples,
193205
usually in a 1-for-1 fashion,
194206
195-
checkbounds_indices((IA1, IA...), (I1, I...)) = checkindex(Bool, IA1, I1) &
196-
checkbounds_indices(IA, I)
207+
checkbounds_indices(Bool, (IA1, IA...), (I1, I...)) = checkindex(Bool, IA1, I1) &
208+
checkbounds_indices(Bool, IA, I)
197209
198210
Note that `checkindex` is being used to perform the actual
199211
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
202214
CartesianIndex{N}, both of which may "consume" more than one element
203215
of `IA`.
204216
"""
205-
function checkbounds_indices(IA::Tuple, I::Tuple)
217+
function checkbounds_indices(::Type{Bool}, IA::Tuple, I::Tuple)
206218
@_inline_meta
207-
checkindex(Bool, IA[1], I[1]) & checkbounds_indices(tail(IA), tail(I))
219+
checkindex(Bool, IA[1], I[1]) & checkbounds_indices(Bool, tail(IA), tail(I))
208220
end
209-
checkbounds_indices(::Tuple{}, ::Tuple{}) = true
210-
checkbounds_indices(::Tuple{}, I::Tuple{Any}) = (@_inline_meta; checkindex(Bool, 1:1, I[1]))
211-
function checkbounds_indices(::Tuple{}, I::Tuple)
221+
checkbounds_indices(::Type{Bool}, ::Tuple{}, ::Tuple{}) = true
222+
checkbounds_indices(::Type{Bool}, ::Tuple{}, I::Tuple{Any}) = (@_inline_meta; checkindex(Bool, 1:1, I[1]))
223+
function checkbounds_indices(::Type{Bool}, ::Tuple{}, I::Tuple)
212224
@_inline_meta
213-
checkindex(Bool, 1:1, I[1]) & checkbounds_indices((), tail(I))
225+
checkindex(Bool, 1:1, I[1]) & checkbounds_indices(Bool, (), tail(I))
214226
end
215-
function checkbounds_indices(IA::Tuple{Any}, I::Tuple{Any})
227+
function checkbounds_indices(::Type{Bool}, IA::Tuple{Any}, I::Tuple{Any})
216228
@_inline_meta
217229
checkindex(Bool, IA[1], I[1])
218230
end
219-
function checkbounds_indices(IA::Tuple, I::Tuple{Any})
231+
function checkbounds_indices(::Type{Bool}, IA::Tuple, I::Tuple{Any})
220232
@_inline_meta
221233
checkindex(Bool, 1:prod(map(dimlength, IA)), I[1]) # linear indexing
222234
end
223235

224236
"""
225-
checkbounds_logical(A, I::AbstractArray{Bool})
237+
checkbounds_logical(Bool, A, I::AbstractArray{Bool})
226238
227-
tests whether the logical array `I` is consistent with the indices of `A`.
239+
Return `true` if the logical array `I` is consistent with the indices
240+
of `A`. `I` and `A` should have the same size and compatible indices.
228241
"""
229-
checkbounds_logical(A::AbstractArray, I::AbstractArray{Bool}) = indices(A) == indices(I)
230-
checkbounds_logical(A::AbstractArray, I::AbstractVector{Bool}) = length(A) == length(I)
231-
checkbounds_logical(A::AbstractVector, I::AbstractArray{Bool}) = length(A) == length(I)
232-
checkbounds_logical(A::AbstractVector, I::AbstractVector{Bool}) = indices(A) == indices(I)
233-
234-
throw_boundserror(A, I) = (@_noinline_meta; throw(BoundsError(A, I)))
242+
function checkbounds_logical(::Type{Bool}, A::AbstractArray, I::AbstractArray{Bool})
243+
indices(A) == indices(I)
244+
end
245+
function checkbounds_logical(::Type{Bool}, A::AbstractArray, I::AbstractVector{Bool})
246+
length(A) == length(I)
247+
end
248+
function checkbounds_logical(::Type{Bool}, A::AbstractVector, I::AbstractArray{Bool})
249+
length(A) == length(I)
250+
end
251+
function checkbounds_logical(::Type{Bool}, A::AbstractVector, I::AbstractVector{Bool})
252+
indices(A) == indices(I)
253+
end
235254

236255
"""
237-
checkbounds(A, I...)
256+
checkbounds_logical(A, I::AbstractArray{Bool})
238257
239-
Throw an error if the specified indices `I` are not in bounds for the given array `A`.
258+
Throw an error if the logical array `I` is inconsistent with the indices of `A`.
240259
"""
241-
function checkbounds(A::AbstractArray, I...)
242-
@_inline_meta
243-
checkbounds(Bool, A, I...) || throw_boundserror(A, I)
260+
function checkbounds_logical(A, I::AbstractVector{Bool})
261+
checkbounds_logical(Bool, A, I) || throw_boundserror(A, I)
244262
nothing
245263
end
246-
checkbounds(A::AbstractArray) = checkbounds(A, 1) # 0-d case
264+
265+
throw_boundserror(A, I) = (@_noinline_meta; throw(BoundsError(A, I)))
247266

248267
@generated function trailingsize{T,N,n}(A::AbstractArray{T,N}, ::Type{Val{n}})
249268
(isa(n, Int) && isa(N, Int)) || error("Must have concrete type")

base/multidimensional.jl

+13-13
Original file line numberDiff line numberDiff line change
@@ -154,31 +154,31 @@ end # IteratorsMD
154154
using .IteratorsMD
155155

156156
## Bounds-checking with CartesianIndex
157-
@inline checkbounds_indices(::Tuple{}, I::Tuple{CartesianIndex,Vararg{Any}}) =
158-
checkbounds_indices((), (I[1].I..., tail(I)...))
159-
@inline checkbounds_indices(IA::Tuple{Any}, I::Tuple{CartesianIndex,Vararg{Any}}) =
160-
checkbounds_indices(IA, (I[1].I..., tail(I)...))
161-
@inline checkbounds_indices(IA::Tuple, I::Tuple{CartesianIndex,Vararg{Any}}) =
162-
checkbounds_indices(IA, (I[1].I..., tail(I)...))
157+
@inline checkbounds_indices(::Type{Bool}, ::Tuple{}, I::Tuple{CartesianIndex,Vararg{Any}}) =
158+
checkbounds_indices(Bool, (), (I[1].I..., tail(I)...))
159+
@inline checkbounds_indices(::Type{Bool}, IA::Tuple{Any}, I::Tuple{CartesianIndex,Vararg{Any}}) =
160+
checkbounds_indices(Bool, IA, (I[1].I..., tail(I)...))
161+
@inline checkbounds_indices(::Type{Bool}, IA::Tuple, I::Tuple{CartesianIndex,Vararg{Any}}) =
162+
checkbounds_indices(Bool, IA, (I[1].I..., tail(I)...))
163163

164164
# Support indexing with an array of CartesianIndex{N}s
165165
# Here we try to consume N of the indices (if there are that many available)
166166
# The first two simply handle ambiguities
167-
@inline function checkbounds_indices{N}(::Tuple{}, I::Tuple{AbstractArray{CartesianIndex{N}},Vararg{Any}})
168-
checkindex(Bool, (), I[1]) & checkbounds_indices((), tail(I))
167+
@inline function checkbounds_indices{N}(::Type{Bool}, ::Tuple{}, I::Tuple{AbstractArray{CartesianIndex{N}},Vararg{Any}})
168+
checkindex(Bool, (), I[1]) & checkbounds_indices(Bool, (), tail(I))
169169
end
170-
@inline function checkbounds_indices{N}(IA::Tuple{Any}, I::Tuple{AbstractArray{CartesianIndex{N}},Vararg{Any}})
171-
checkindex(Bool, IA, I[1]) & checkbounds_indices((), tail(I))
170+
@inline function checkbounds_indices{N}(::Type{Bool}, IA::Tuple{Any}, I::Tuple{AbstractArray{CartesianIndex{N}},Vararg{Any}})
171+
checkindex(Bool, IA, I[1]) & checkbounds_indices(Bool, (), tail(I))
172172
end
173-
@inline function checkbounds_indices{N}(IA::Tuple, I::Tuple{AbstractArray{CartesianIndex{N}},Vararg{Any}})
173+
@inline function checkbounds_indices{N}(::Type{Bool}, IA::Tuple, I::Tuple{AbstractArray{CartesianIndex{N}},Vararg{Any}})
174174
IA1, IArest = IteratorsMD.split(IA, Val{N})
175-
checkindex(Bool, IA1, I[1]) & checkbounds_indices(IArest, tail(I))
175+
checkindex(Bool, IA1, I[1]) & checkbounds_indices(Bool, IArest, tail(I))
176176
end
177177

178178
function checkindex{N}(::Type{Bool}, inds::Tuple, I::AbstractArray{CartesianIndex{N}})
179179
b = true
180180
for i in I
181-
b &= checkbounds_indices(inds, (i,))
181+
b &= checkbounds_indices(Bool, inds, (i,))
182182
end
183183
b
184184
end

doc/devdocs/boundscheck.rst

+4-4
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,8 @@ The overall hierarchy is:
6767

6868
| ``checkbounds(A, I...)`` which calls
6969
| ``checkbounds(Bool, A, I...)`` which calls either of:
70-
| ``checkbounds_logical(A, I)`` when ``I`` is a single logical array
71-
| ``checkbounds_indices(indices(A), I)`` otherwise
70+
| ``checkbounds_logical(Bool, A, I)`` when ``I`` is a single logical array
71+
| ``checkbounds_indices(Bool, indices(A), I)`` otherwise
7272
|
7373
7474
Here ``A`` is the array, and ``I`` contains the "requested" indices.
@@ -85,8 +85,8 @@ dimensions handled by calling another important function,
8585
``checkindex``: typically,
8686
::
8787

88-
checkbounds_indices((IA1, IA...), (I1, I...)) = checkindex(Bool, IA1, I1) &
89-
checkbounds_indices(IA, I)
88+
checkbounds_indices(Bool, (IA1, IA...), (I1, I...)) = checkindex(Bool, IA1, I1) &
89+
checkbounds_indices(Bool, IA, I)
9090

9191
so ``checkindex`` checks a single dimension. All of these functions,
9292
including the unexported ``checkbounds_indices`` and

doc/stdlib/arrays.rst

-1
Original file line numberDiff line numberDiff line change
@@ -1080,4 +1080,3 @@ dense counterparts. The following functions are specific to sparse arrays.
10801080
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!`\ .
10811081

10821082
See also: :func:`Base.SparseArrays.permute`
1083-

test/replutil.jl

+3
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,9 @@ let undefvar
222222
err_str = @except_strbt (-1)^0.25 DomainError
223223
@test contains(err_str, "Exponentiation yielding a complex result requires a complex argument")
224224

225+
err_str = @except_str (1,2,3)[4] BoundsError
226+
@test err_str == "BoundsError: attempt to access (1,2,3) at index [4]"
227+
225228
err_str = @except_str [5,4,3][-2,1] BoundsError
226229
@test err_str == "BoundsError: attempt to access 3-element Array{$Int,1} at index [-2,1]"
227230
err_str = @except_str [5,4,3][1:5] BoundsError

0 commit comments

Comments
 (0)