Bounds checking
The @boundscheck(...)
macro marks blocks of code that perform bounds checking. When such blocks are inlined into an @inbounds(...)
block, the compiler may remove these blocks. The compiler removes the @boundscheck
block only if it is inlined into the calling function. For example, you might write the method sum
as:
With a custom array-like type MyArray
having:
Then when getindex
is inlined into sum
, the call to checkbounds(A,i)
will be elided. If your function contains multiple layers of inlining, only @boundscheck
blocks at most one level of inlining deeper are eliminated. The rule prevents unintended changes in program behavior from code further up the stack.
It is easy to accidentally expose unsafe operations with @inbounds
. You might be tempted to write the above example as
Which quietly assumes 1-based indexing and therefore exposes unsafe memory access when used with :
There may be certain scenarios where for code-organization reasons you want more than one layer between the and @boundscheck
declarations. For instance, the default getindex
methods have the chain getindex(A::AbstractArray, i::Real)
calls getindex(IndexStyle(A), A, i)
calls _getindex(::IndexLinear, A, i)
.
To override the “one layer of inlining” rule, a function may be marked with to propagate an inbounds context (or out of bounds context) through one additional layer of inlining.
The overall hierarchy is:
Here A
is the array, and I
contains the “requested” indices. axes(A)
returns a tuple of “permitted” indices of A
.
checkbounds(A, I...)
throws an error if the indices are invalid, whereas returns false
in that circumstance. checkbounds_indices
discards any information about the array other than its axes
tuple, and performs a pure indices-vs-indices comparison: this allows relatively few compiled methods to serve a huge variety of array types. Indices are specified as tuples, and are usually compared in a 1-1 fashion with individual dimensions handled by calling another important function, checkindex
: typically,
so checkindex
checks a single dimension. All of these functions, including the unexported checkbounds_indices
have docstrings accessible with ?
.
If you have to customize bounds checking for a specific array type, you should specialize checkbounds(Bool, A, I...)
. However, in most cases you should be able to rely on checkbounds_indices
as long as you supply useful axes
for your array type.
If you have novel index types, first consider specializing checkindex
, which handles a single index for a particular dimension of an array. If you have a custom multidimensional index type (similar to CartesianIndex
), then you may have to consider specializing checkbounds_indices
.
Julia can be launched with --check-bounds={yes|no|auto}
to emit bounds checks always, never, or respect @inbounds declarations.