Skip to content

trait for bounds checking #321

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions lib/ArrayInterfaceCore/src/ArrayInterfaceCore.jl
Original file line number Diff line number Diff line change
Expand Up @@ -626,8 +626,116 @@ known_step(x) = known_step(typeof(x))
known_step(T::Type) = is_forwarding_wrapper(T) ? known_step(parent_type(T)) : nothing
known_step(@nospecialize T::Type{<:AbstractUnitRange}) = 1

abstract type CheckIndexStyle end

"""
ArrayInterfaceCore.CheckIndexNone

A [`ArrayInterfaceCore.CheckIndexStyle`](@ref) trait-value indicating that no bounds-checking
is needed. This is only appropriate for select types such as slice indexing
(e.g., `:`, `Base.Slice`).

# Examples

```jldoctest
julia> ArrayInterfaceCore.CheckIndexStyle(:)
ArrayInterfaceCore.CheckIndexNone()

julia> ArrayInterfaceCore.CheckIndexStyle(Base.Slice(1:10))
ArrayInterfaceCore.CheckIndexNone()

```

"""
struct CheckIndexNone <: CheckIndexStyle end

"""
ArrayInterfaceCore.CheckIndexFirstLast()

A [`ArrayInterfaceCore.CheckIndexStyle`](@ref) trait-value indicating that bounds-checking
only need test the first and last elements in an index vector.

# Examples

```jldoctest
julia> r = 3:7; ArrayInterfaceCore.CheckIndexStyle(r)
ArrayInterfaceCore.CheckIndexFirstLast()

```
Ranges are declared `CheckIndexFirstLast` because `x[r]` can be tested
for out-of-bounds indexing using just the first and last elements of `r`.
See also [`ArrayInterfaceCore.CheckIndexAll`](@ref) and [`ArrayInterfaceCore.CheckIndexAxes`](@ref).
"""
struct CheckIndexFirstLast <: CheckIndexStyle end

"""
ArrayInterfaceCore.CheckIndexAll()

A [`ArrayInterfaceCore.CheckIndexStyle`](@ref) trait-value indicating that bounds-checking
must test all elements in an index vector.

# Examples

```jldoctest
julia> idx = [3,4,5,6,7]; ArrayInterfaceCore.CheckIndexStyle(idx)
ArrayInterfaceCore.CheckIndexAll()

```

Since the entries in `idx` could be arbitrary, we have to check each
entry for bounds violations.
See also [`ArrayInterfaceCore..CheckIndexFirstLast`](@ref) and [`ArrayInterfaceCore.CheckIndexAxes`](@ref).
"""
struct CheckIndexAll <: CheckIndexStyle end

"""
ArrayInterfaceCore.CheckIndexAxes()

A [`CheckIndexStyle`](@ref) trait-value indicating that bounds-checking
should consider the axes of the index rather than the values of the
index. This is used in cases where the index acts as a filter to
select elements.

# Examples

```jldoctest
julia> idx = [true, false, true]; ArrayInterfaceCore.CheckIndexStyle(idx)
ArrayInterfaceCore.CheckIndexAxes()

```
When `idx` is used in `x[idx]`, it returns the entries in `x`
corresponding to `true` entries in `idx`. Consequently, indexing
should insist on `idx` and `x` having identical axes.
See also [`ArrayInterfaceCore.CheckIndexFirstLast`](@ref) and [`ArrayInterfaceCore.CheckIndexAll`](@ref).
"""
struct CheckIndexAxes <: CheckIndexStyle end

"""
ArrayInterfaceCore.CheckIndexStyle(typeof(idx))
ArrayInterfaceCore.CheckIndexStyle(::Type{T})

`CheckIndexStyle` specifies how bounds-checking of `x[idx]` should be performed. Certain
types of `idx`, such as ranges, may have particularly efficient ways to perform the
bounds-checking. When you define a new [`AbstractArray`](@ref) type, you can choose to
define a specific value for this trait:

ArrayInterfaceCore.CheckIndexStyle(::Type{<:MyRange}) = CheckIndexFirstLast()
The default is [`CheckIndexAll()`](@ref), except for `AbstractVector`s with `Bool`
`eltype` (which default to [`ArrayInterfaceCore.CheckIndexAxes()`](@ref)) and subtypes of `AbstractRange`
(which default to [`ArrayInterfaceCore.CheckIndexFirstLast()`](@ref).)
"""
CheckIndexStyle(x) = CheckIndexStyle(typeof(x))
CheckIndexStyle(::Type) = CheckIndexAll()
CheckIndexStyle(@nospecialize T::Type{<:AbstractArray}) = eltype(T) === Bool ? CheckIndexAxes() : CheckIndexAll()
CheckIndexStyle(@nospecialize T::Type{<:AbstractRange}) = eltype(T) === Bool ? CheckIndexAxes() : CheckIndexFirstLast()
CheckIndexStyle(@nospecialize T::Type{<:Base.FastContiguousSubArray}) = CheckIndexStyle(parent_type(T))
CheckIndexStyle(@nospecialize T::Type{<:Union{Base.Slice,Colon}}) = CheckIndexNone()
CheckIndexStyle(@nospecialize T::Type{<:Base.Fix2{<:Union{typeof(<),typeof(isless),typeof(>=),typeof(>),typeof(isless)},<:Number}}) = CheckIndexNone()
CheckIndexStyle(@nospecialize T::Type{<:Number}) = CheckIndexFirstLast()

"""
is_splat_index(::Type{T}) -> Bool

Returns `static(true)` if `T` is a type that splats across multiple dimensions.
"""
is_splat_index(T::Type) = false
Expand Down