Skip to content

Commit 7e7300e

Browse files
authored
Merge pull request #23516 from JuliaLang/mb/docboundscheck
Document boundscheck macro
2 parents b83c228 + 09f727e commit 7e7300e

File tree

3 files changed

+53
-0
lines changed

3 files changed

+53
-0
lines changed

base/essentials.jl

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,46 @@ section of the Metaprogramming chapter of the manual for more details and exampl
412412
"""
413413
esc(@nospecialize(e)) = Expr(:escape, e)
414414

415+
"""
416+
@boundscheck(blk)
417+
418+
Annotates the expression `blk` as a bounds checking block, allowing it to be elided by [`@inbounds`](@ref).
419+
420+
Note that the function in which `@boundscheck` is written must be inlined into
421+
its caller with [`@inline`](@ref) in order for `@inbounds` to have effect.
422+
423+
```jldoctest
424+
julia> @inline function g(A, i)
425+
@boundscheck checkbounds(A, i)
426+
return "accessing (\$A)[\$i]"
427+
end
428+
f1() = return g(1:2, -1)
429+
f2() = @inbounds return g(1:2, -1)
430+
f2 (generic function with 1 method)
431+
432+
julia> f1()
433+
ERROR: BoundsError: attempt to access 2-element UnitRange{Int64} at index [-1]
434+
Stacktrace:
435+
[1] throw_boundserror(::UnitRange{Int64}, ::Tuple{Int64}) at ./abstractarray.jl:428
436+
[2] checkbounds at ./abstractarray.jl:392 [inlined]
437+
[3] g at ./REPL[20]:2 [inlined]
438+
[4] f1() at ./REPL[20]:5
439+
440+
julia> f2()
441+
"accessing (1:2)[-1]"
442+
```
443+
444+
!!! warning
445+
446+
The `@boundscheck` annotation allows you, as a library writer, to opt-in to
447+
allowing *other code* to remove your bounds checks with [`@inbounds`](@ref).
448+
As noted there, the caller must verify—using information they can access—that
449+
their accesses are valid before using `@inbounds`. For indexing into your
450+
[`AbstractArray`](@ref) subclasses, for example, this involves checking the
451+
indices against its [`size`](@ref). Therefore, `@boundscheck` annotations
452+
should only be added to a [`getindex`](@ref) or [`setindex!`](@ref)
453+
implementation after you are certain its behavior is correct.
454+
"""
415455
macro boundscheck(blk)
416456
return Expr(:if, Expr(:boundscheck), esc(blk))
417457
end
@@ -438,6 +478,8 @@ end
438478
439479
Using `@inbounds` may return incorrect results/crashes/corruption
440480
for out-of-bounds indices. The user is responsible for checking it manually.
481+
Only use `@inbounds` when it is certain from the information locally available
482+
that all accesses are in bounds.
441483
"""
442484
macro inbounds(blk)
443485
return Expr(:block,

doc/src/stdlib/base.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ Base.@eval
146146
Base.evalfile
147147
Base.esc
148148
Base.@inbounds
149+
Base.@boundscheck
149150
Base.@inline
150151
Base.@noinline
151152
Base.@nospecialize

test/boundscheck_exec.jl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,4 +229,14 @@ else
229229
@test inbounds_isassigned(Int[], 2) == false
230230
end
231231

232+
# Test that @inbounds annotations don't propagate too far for Array; Issue #20469
233+
struct BadVector20469{T} <: AbstractVector{Int}
234+
data::T
235+
end
236+
Base.size(X::BadVector20469) = size(X.data)
237+
Base.getindex(X::BadVector20469, i::Int) = X.data[i-1]
238+
if bc_opt != bc_off
239+
@test_throws BoundsError BadVector20469([1,2,3])[:]
240+
end
241+
232242
end

0 commit comments

Comments
 (0)