Skip to content

Commit

Permalink
Merge pull request #23516 from JuliaLang/mb/docboundscheck
Browse files Browse the repository at this point in the history
Document boundscheck macro
  • Loading branch information
mbauman authored Sep 7, 2017
2 parents b83c228 + 09f727e commit 7e7300e
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 0 deletions.
42 changes: 42 additions & 0 deletions base/essentials.jl
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,46 @@ section of the Metaprogramming chapter of the manual for more details and exampl
"""
esc(@nospecialize(e)) = Expr(:escape, e)

"""
@boundscheck(blk)
Annotates the expression `blk` as a bounds checking block, allowing it to be elided by [`@inbounds`](@ref).
Note that the function in which `@boundscheck` is written must be inlined into
its caller with [`@inline`](@ref) in order for `@inbounds` to have effect.
```jldoctest
julia> @inline function g(A, i)
@boundscheck checkbounds(A, i)
return "accessing (\$A)[\$i]"
end
f1() = return g(1:2, -1)
f2() = @inbounds return g(1:2, -1)
f2 (generic function with 1 method)
julia> f1()
ERROR: BoundsError: attempt to access 2-element UnitRange{Int64} at index [-1]
Stacktrace:
[1] throw_boundserror(::UnitRange{Int64}, ::Tuple{Int64}) at ./abstractarray.jl:428
[2] checkbounds at ./abstractarray.jl:392 [inlined]
[3] g at ./REPL[20]:2 [inlined]
[4] f1() at ./REPL[20]:5
julia> f2()
"accessing (1:2)[-1]"
```
!!! warning
The `@boundscheck` annotation allows you, as a library writer, to opt-in to
allowing *other code* to remove your bounds checks with [`@inbounds`](@ref).
As noted there, the caller must verify—using information they can access—that
their accesses are valid before using `@inbounds`. For indexing into your
[`AbstractArray`](@ref) subclasses, for example, this involves checking the
indices against its [`size`](@ref). Therefore, `@boundscheck` annotations
should only be added to a [`getindex`](@ref) or [`setindex!`](@ref)
implementation after you are certain its behavior is correct.
"""
macro boundscheck(blk)
return Expr(:if, Expr(:boundscheck), esc(blk))
end
Expand All @@ -438,6 +478,8 @@ end
Using `@inbounds` may return incorrect results/crashes/corruption
for out-of-bounds indices. The user is responsible for checking it manually.
Only use `@inbounds` when it is certain from the information locally available
that all accesses are in bounds.
"""
macro inbounds(blk)
return Expr(:block,
Expand Down
1 change: 1 addition & 0 deletions doc/src/stdlib/base.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ Base.@eval
Base.evalfile
Base.esc
Base.@inbounds
Base.@boundscheck
Base.@inline
Base.@noinline
Base.@nospecialize
Expand Down
10 changes: 10 additions & 0 deletions test/boundscheck_exec.jl
Original file line number Diff line number Diff line change
Expand Up @@ -229,4 +229,14 @@ else
@test inbounds_isassigned(Int[], 2) == false
end

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

end

0 comments on commit 7e7300e

Please sign in to comment.