From d6c216b152192140727f46654eed2cbc75139c87 Mon Sep 17 00:00:00 2001 From: Matt Bauman Date: Thu, 25 Jun 2015 17:21:54 -0400 Subject: [PATCH] Use a traits based mechanism for boundscheck flags This removes `unsafe_getindex` and `unsafe_setindex!` in favor of `getindex(::BoundsCheckOff, ...)` and `setindex!(::BoundsCheckOff, ...). `getindex(...)` falls back to `getindex(BoundsCheckOn(), ...)`, which will error if neither was defined. `getindex(BoundsCheckOff(), ...)` will fall back to `getindex(...)` if not defined. --- base/abstractarray.jl | 176 ++++++++++++++------------------------- base/array.jl | 36 ++++---- base/bitarray.jl | 20 +++-- base/deprecated.jl | 6 +- base/essentials.jl | 7 ++ base/exports.jl | 3 + base/linalg.jl | 2 +- base/linalg/diagonal.jl | 12 +-- base/linalg/symmetric.jl | 4 +- base/multidimensional.jl | 98 +++++++++++----------- base/number.jl | 2 +- base/range.jl | 41 +++++---- base/subarray.jl | 29 +++---- 13 files changed, 203 insertions(+), 233 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 683b7a9f44fef..2250680f98e10 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -416,49 +416,51 @@ pointer{T}(x::AbstractArray{T}, i::Integer) = unsafe_convert(Ptr{T},x) + (i-1)*e # fallback to the safe version if the subtype hasn't defined the required # unsafe method. +# When getindex is called directly without the boundschecking trait, we need to +# first determine if it was the 'canonical' method that *must* be implemented +# by the type in order to throw an error (instead of a recursive stack overflow) function getindex(A::AbstractArray, I...) @_inline_meta - _getindex(linearindexing(A), A, I...) + getindex(BoundsCheckOn(), A, I...) end -function unsafe_getindex(A::AbstractArray, I...) +function getindex(b::BoundsCheck, A::AbstractArray, I...) @_inline_meta - _unsafe_getindex(linearindexing(A), A, I...) + _getindex(b, linearindexing(A), A, I...) end ## Internal defitions -_getindex(::LinearFast, A::AbstractArray) = (@_inline_meta; getindex(A, 1)) -_getindex(::LinearSlow, A::AbstractArray) = (@_inline_meta; _getindex(A, 1)) -_unsafe_getindex(::LinearFast, A::AbstractArray) = (@_inline_meta; unsafe_getindex(A, 1)) -_unsafe_getindex(::LinearSlow, A::AbstractArray) = (@_inline_meta; _unsafe_getindex(A, 1)) -_getindex(::LinearIndexing, A::AbstractArray, I...) = error("indexing $(typeof(A)) with types $(typeof(I)) is not supported") -_unsafe_getindex(::LinearIndexing, A::AbstractArray, I...) = error("indexing $(typeof(A)) with types $(typeof(I)) is not supported") +_getindex(b::BoundsCheckOn, ::LinearFast, A::AbstractArray) = (@_inline_meta; getindex(A, 1)) +_getindex(b::BoundsCheckOff, ::LinearFast, A::AbstractArray) = (@_inline_meta; getindex(b, A, 1)) +_getindex(b::BoundsCheck, ::LinearSlow, A::AbstractArray) = (@_inline_meta; _getindex(b, A, 1)) +_getindex(::BoundsCheck, ::LinearIndexing, A::AbstractArray, I...) = error("indexing $(typeof(A)) with types $(typeof(I)) is not supported") ## LinearFast Scalar indexing -_getindex(::LinearFast, A::AbstractArray, I::Int) = error("indexing not defined for ", typeof(A)) -function _getindex(::LinearFast, A::AbstractArray, I::Real...) +_getindex(::BoundsCheckOn, ::LinearFast, A::AbstractArray, I::Int) = error("indexing not defined for ", typeof(A)) +_getindex(::BoundsCheckOff, ::LinearFast, A::AbstractArray, I::Int) = (@_inline_meta; getindex(A, I)) +function _getindex(b::BoundsCheck, ::LinearFast, A::AbstractArray, I::Real...) @_inline_meta - # We must check bounds for sub2ind; so we can then call unsafe_getindex - checkbounds(A, I...) - unsafe_getindex(A, sub2ind(size(A), to_index(I)...)) -end -_unsafe_getindex(::LinearFast, A::AbstractArray, I::Int) = (@_inline_meta; getindex(A, I)) -function _unsafe_getindex(::LinearFast, A::AbstractArray, I::Real...) - @_inline_meta - unsafe_getindex(A, sub2ind(size(A), to_index(I)...)) + # We must check bounds for sub2ind; so afterwards we can turn off boundscheck + Bool(b) && checkbounds(A, I...) + getindex(BoundsCheckOff(), A, sub2ind(size(A), to_index(I)...)) end # LinearSlow Scalar indexing -@generated function _getindex{T,AN}(::LinearSlow, A::AbstractArray{T,AN}, I::Real...) +@generated function _getindex{T,AN}(b::BoundsCheck, ::LinearSlow, A::AbstractArray{T,AN}, I::Real...) N = length(I) - if N == AN - :(error("indexing not defined for ", typeof(A))) - elseif N > AN - # Drop trailing ones + if N == AN && I == ntuple(i->Int, AN) + if b <: BoundsCheckOn + :(error("indexing not defined for ", typeof(A))) + else + :($(Expr(:meta, :inline)); getindex(A, I...)) + end + elseif N >= AN + # Drop trailing ones and convert to_index Isplat = Expr[:(to_index(I[$d])) for d = 1:AN] - Osplat = Expr[:(to_index(I[$d]) == 1) for d = AN+1:N] + Osplat = Expr[:(to_index(I[$d])) for d = AN+1:N] quote $(Expr(:meta, :inline)) - (&)($(Osplat...)) || throw_boundserror(A, I) - getindex(A, $(Isplat...)) + # Remove trailing indices; ensure all were 1 if bounds checks were on + Bool(b) && checkbounds(A, $(Isplat...), $(Osplat...)) + getindex(BoundsCheckOff(), A, $(Isplat...)) end else # Expand the last index into the appropriate number of indices @@ -472,85 +474,57 @@ end quote $(Expr(:meta, :inline)) # ind2sub requires all dimensions to be nonzero, so checkbounds first - checkbounds(A, I...) + Bool(b) && checkbounds(A, I...) s = ind2sub($sz, to_index(I[$N])) - unsafe_getindex(A, $(Isplat...)) - end - end -end -@generated function _unsafe_getindex{T,AN}(::LinearSlow, A::AbstractArray{T,AN}, I::Real...) - N = length(I) - if N == AN - Isplat = Expr[:(to_index(I[$d])) for d = 1:N] - :($(Expr(:meta, :inline)); getindex(A, $(Isplat...))) - elseif N > AN - # Drop trailing dimensions (unchecked) - Isplat = Expr[:(to_index(I[$d])) for d = 1:AN] - quote - $(Expr(:meta, :inline)) - unsafe_getindex(A, $(Isplat...)) - end - else - # Expand the last index into the appropriate number of indices - Isplat = Expr[:(to_index(I[$d])) for d = 1:N-1] - for d=N:AN - push!(Isplat, :(s[$(d-N+1)])) - end - sz = Expr(:tuple) - sz.args = Expr[:(size(A, $d)) for d=N:AN] - quote - $(Expr(:meta, :inline)) - s = ind2sub($sz, to_index(I[$N])) - unsafe_getindex(A, $(Isplat...)) + getindex(BoundsCheckOff(), A, $(Isplat...)) end end end -## Setindex! is defined similarly. We first dispatch to an internal _setindex! -# function that allows dispatch on array storage +## Setindex! is defined identically. It'd be nice to use metaprogramming here +# but a necessary tool (tuple splatting) isn't available yet in bootstrap function setindex!(A::AbstractArray, v, I...) @_inline_meta - _setindex!(linearindexing(A), A, v, I...) + setindex!(BoundsCheckOn(), A, v, I...) end -function unsafe_setindex!(A::AbstractArray, v, I...) +function setindex!(b::BoundsCheck, A::AbstractArray, v, I...) @_inline_meta - _unsafe_setindex!(linearindexing(A), A, v, I...) + _setindex!(b, linearindexing(A), A, v, I...) end ## Internal defitions -_setindex!(::LinearFast, A::AbstractArray, v) = (@_inline_meta; setindex!(A, v, 1)) -_setindex!(::LinearSlow, A::AbstractArray, v) = (@_inline_meta; _setindex!(A, v, 1)) -_unsafe_setindex!(::LinearFast, A::AbstractArray, v) = (@_inline_meta; unsafe_setindex!(A, v, 1)) -_unsafe_setindex!(::LinearSlow, A::AbstractArray, v) = (@_inline_meta; _unsafe_setindex!(A, v, 1)) -_setindex!(::LinearIndexing, A::AbstractArray, v, I...) = error("indexing $(typeof(A)) with types $(typeof(I)) is not supported") -_unsafe_setindex!(::LinearIndexing, A::AbstractArray, v, I...) = error("indexing $(typeof(A)) with types $(typeof(I)) is not supported") +_setindex!(b::BoundsCheckOn, ::LinearFast, A::AbstractArray, v) = (@_inline_meta; setindex!(A, v, 1)) +_setindex!(b::BoundsCheckOff, ::LinearFast, A::AbstractArray, v) = (@_inline_meta; setindex!(b, A, v, 1)) +_setindex!(b::BoundsCheck, ::LinearSlow, A::AbstractArray, v) = (@_inline_meta; _setindex!(b, A, v, 1)) +_setindex!(::BoundsCheck, ::LinearIndexing, A::AbstractArray, v, I...) = error("indexing $(typeof(A)) with types $(typeof(I)) is not supported") ## LinearFast Scalar indexing -_setindex!(::LinearFast, A::AbstractArray, v, I::Int) = error("indexed assignment not defined for ", typeof(A)) -function _setindex!(::LinearFast, A::AbstractArray, v, I::Real...) +_setindex!(::BoundsCheckOn, ::LinearFast, A::AbstractArray, v, I::Int) = error("indexing not defined for ", typeof(A)) +_setindex!(::BoundsCheckOff, ::LinearFast, A::AbstractArray, v, I::Int) = setindex!(A, v, I) +function _setindex!(b::BoundsCheck, ::LinearFast, A::AbstractArray, v, I::Real...) @_inline_meta - # We must check bounds for sub2ind; so we can then call unsafe_setindex! - checkbounds(A, I...) - unsafe_setindex!(A, v, sub2ind(size(A), to_index(I)...)) -end -_unsafe_setindex!(::LinearFast, A::AbstractArray, v, I::Int) = (@_inline_meta; setindex!(A, v, I)) -function _unsafe_setindex!(::LinearFast, A::AbstractArray, v, I::Real...) - @_inline_meta - unsafe_setindex!(A, v, sub2ind(size(A), to_index(I)...)) + # We must check bounds for sub2ind; so afterwards we can turn off boundscheck + Bool(b) && checkbounds(A, I...) + setindex!(BoundsCheckOff(), A, v, sub2ind(size(A), to_index(I)...)) end # LinearSlow Scalar indexing -@generated function _setindex!{T,AN}(::LinearSlow, A::AbstractArray{T,AN}, v, I::Real...) +@generated function _setindex!{T,AN}(b::BoundsCheck, ::LinearSlow, A::AbstractArray{T,AN}, v, I::Real...) N = length(I) - if N == AN - :(error("indexed assignment not defined for ", typeof(A))) - elseif N > AN - # Drop trailing ones + if N == AN && I == ntuple(i->Int, AN) + if b <: BoundsCheckOn + :(error("indexing not defined for ", typeof(A))) + else + :($(Expr(:meta, :inline)); setindex!(A, v, I...)) + end + elseif N >= AN + # Drop trailing ones and convert to_index Isplat = Expr[:(to_index(I[$d])) for d = 1:AN] - Osplat = Expr[:(to_index(I[$d]) == 1) for d = AN+1:N] + Osplat = Expr[:(to_index(I[$d])) for d = AN+1:N] quote $(Expr(:meta, :inline)) - (&)($(Osplat...)) || throw_boundserror(A, I) - setindex!(A, v, $(Isplat...)) + # Remove trailing indices; ensure all were 1 if bounds checks were on + Bool(b) && checkbounds(A, $(Isplat...), $(Osplat...)) + setindex!(BoundsCheckOff(), A, v, $(Isplat...)) end else # Expand the last index into the appropriate number of indices @@ -563,36 +537,10 @@ end sz.args = Expr[:(size(A, $d)) for d=N:AN] quote $(Expr(:meta, :inline)) - checkbounds(A, I...) - s = ind2sub($sz, to_index(I[$N])) - unsafe_setindex!(A, v, $(Isplat...)) - end - end -end -@generated function _unsafe_setindex!{T,AN}(::LinearSlow, A::AbstractArray{T,AN}, v, I::Real...) - N = length(I) - if N == AN - Isplat = Expr[:(to_index(I[$d])) for d = 1:N] - :(setindex!(A, v, $(Isplat...))) - elseif N > AN - # Drop trailing dimensions (unchecked) - Isplat = Expr[:(to_index(I[$d])) for d = 1:AN] - quote - $(Expr(:meta, :inline)) - unsafe_setindex!(A, v, $(Isplat...)) - end - else - # Expand the last index into the appropriate number of indices - Isplat = Expr[:(to_index(I[$d])) for d = 1:N-1] - for d=N:AN - push!(Isplat, :(s[$(d-N+1)])) - end - sz = Expr(:tuple) - sz.args = Expr[:(size(A, $d)) for d=N:AN] - quote - $(Expr(:meta, :inline)) + # ind2sub requires all dimensions to be nonzero, so checkbounds first + Bool(b) && checkbounds(A, I...) s = ind2sub($sz, to_index(I[$N])) - unsafe_setindex!(A, v, $(Isplat...)) + setindex!(BoundsCheckOff(), A, v, $(Isplat...)) end end end diff --git a/base/array.jl b/base/array.jl index b28837d4931e4..8156c766db39f 100644 --- a/base/array.jl +++ b/base/array.jl @@ -295,15 +295,15 @@ getindex(A::Array, i1::Real, i2::Real, i3::Real) = arrayref(A, to_index(i1), to_ getindex(A::Array, i1::Real, i2::Real, i3::Real, i4::Real) = arrayref(A, to_index(i1), to_index(i2), to_index(i3), to_index(i4)) getindex(A::Array, i1::Real, i2::Real, i3::Real, i4::Real, I::Real...) = arrayref(A, to_index(i1), to_index(i2), to_index(i3), to_index(i4), to_index(I)...) -unsafe_getindex(A::Array, i1::Real) = @inbounds return arrayref(A, to_index(i1)) -unsafe_getindex(A::Array, i1::Real, i2::Real) = @inbounds return arrayref(A, to_index(i1), to_index(i2)) -unsafe_getindex(A::Array, i1::Real, i2::Real, i3::Real) = @inbounds return arrayref(A, to_index(i1), to_index(i2), to_index(i3)) -unsafe_getindex(A::Array, i1::Real, i2::Real, i3::Real, i4::Real) = @inbounds return arrayref(A, to_index(i1), to_index(i2), to_index(i3), to_index(i4)) -unsafe_getindex(A::Array, i1::Real, i2::Real, i3::Real, i4::Real, I::Real...) = @inbounds return arrayref(A, to_index(i1), to_index(i2), to_index(i3), to_index(i4), to_index(I)...) +getindex(::BoundsCheckOff, A::Array, i1::Real) = @inbounds return arrayref(A, to_index(i1)) +getindex(::BoundsCheckOff, A::Array, i1::Real, i2::Real) = @inbounds return arrayref(A, to_index(i1), to_index(i2)) +getindex(::BoundsCheckOff, A::Array, i1::Real, i2::Real, i3::Real) = @inbounds return arrayref(A, to_index(i1), to_index(i2), to_index(i3)) +getindex(::BoundsCheckOff, A::Array, i1::Real, i2::Real, i3::Real, i4::Real) = @inbounds return arrayref(A, to_index(i1), to_index(i2), to_index(i3), to_index(i4)) +getindex(::BoundsCheckOff, A::Array, i1::Real, i2::Real, i3::Real, i4::Real, I::Real...) = @inbounds return arrayref(A, to_index(i1), to_index(i2), to_index(i3), to_index(i4), to_index(I)...) # Faster contiguous indexing using copy! for UnitRange and Colon -getindex(A::Array, I::UnitRange{Int}) = (checkbounds(A, I); unsafe_getindex(A, I)) -function unsafe_getindex(A::Array, I::UnitRange{Int}) +function getindex(b::BoundsCheck, A::Array, I::UnitRange{Int}) + Bool(b) && checkbounds(A, I) lI = length(I) X = similar(A, lI) if lI > 0 @@ -311,8 +311,8 @@ function unsafe_getindex(A::Array, I::UnitRange{Int}) end return X end -getindex(A::Array, c::Colon) = unsafe_getindex(A, c) -function unsafe_getindex(A::Array, ::Colon) +function getindex(::BoundsCheck, A::Array, ::Colon) + # Colon is always inbounds lI = length(A) X = similar(A, lI) if lI > 0 @@ -333,11 +333,11 @@ setindex!{T}(A::Array{T}, x, i1::Real, i2::Real, i3::Real) = arrayset(A, convert setindex!{T}(A::Array{T}, x, i1::Real, i2::Real, i3::Real, i4::Real) = arrayset(A, convert(T,x), to_index(i1), to_index(i2), to_index(i3), to_index(i4)) setindex!{T}(A::Array{T}, x, i1::Real, i2::Real, i3::Real, i4::Real, I::Real...) = arrayset(A, convert(T,x), to_index(i1), to_index(i2), to_index(i3), to_index(i4), to_index(I)...) -unsafe_setindex!{T}(A::Array{T}, x, i1::Real) = @inbounds return arrayset(A, convert(T,x), to_index(i1)) -unsafe_setindex!{T}(A::Array{T}, x, i1::Real, i2::Real) = @inbounds return arrayset(A, convert(T,x), to_index(i1), to_index(i2)) -unsafe_setindex!{T}(A::Array{T}, x, i1::Real, i2::Real, i3::Real) = @inbounds return arrayset(A, convert(T,x), to_index(i1), to_index(i2), to_index(i3)) -unsafe_setindex!{T}(A::Array{T}, x, i1::Real, i2::Real, i3::Real, i4::Real) = @inbounds return arrayset(A, convert(T,x), to_index(i1), to_index(i2), to_index(i3), to_index(i4)) -unsafe_setindex!{T}(A::Array{T}, x, i1::Real, i2::Real, i3::Real, i4::Real, I::Real...) = @inbounds return arrayset(A, convert(T,x), to_index(i1), to_index(i2), to_index(i3), to_index(i4), to_index(I)...) +setindex!{T}(::BoundsCheckOff, A::Array{T}, x, i1::Real) = @inbounds return arrayset(A, convert(T,x), to_index(i1)) +setindex!{T}(::BoundsCheckOff, A::Array{T}, x, i1::Real, i2::Real) = @inbounds return arrayset(A, convert(T,x), to_index(i1), to_index(i2)) +setindex!{T}(::BoundsCheckOff, A::Array{T}, x, i1::Real, i2::Real, i3::Real) = @inbounds return arrayset(A, convert(T,x), to_index(i1), to_index(i2), to_index(i3)) +setindex!{T}(::BoundsCheckOff, A::Array{T}, x, i1::Real, i2::Real, i3::Real, i4::Real) = @inbounds return arrayset(A, convert(T,x), to_index(i1), to_index(i2), to_index(i3), to_index(i4)) +setindex!{T}(::BoundsCheckOff, A::Array{T}, x, i1::Real, i2::Real, i3::Real, i4::Real, I::Real...) = @inbounds return arrayset(A, convert(T,x), to_index(i1), to_index(i2), to_index(i3), to_index(i4), to_index(I)...) # These are redundant with the abstract fallbacks but needed for bootstrap function setindex!(A::Array, x, I::AbstractVector{Int}) @@ -364,8 +364,8 @@ function setindex!(A::Array, X::AbstractArray, I::AbstractVector{Int}) end # Faster contiguous setindex! with copy! -setindex!{T}(A::Array{T}, X::Array{T}, I::UnitRange{Int}) = (checkbounds(A, I); unsafe_setindex!(A, X, I)) -function unsafe_setindex!{T}(A::Array{T}, X::Array{T}, I::UnitRange{Int}) +function setindex!{T}(b::BoundsCheck, A::Array{T}, X::Array{T}, I::UnitRange{Int}) + Bool(b) && checkbounds(A, I) lI = length(I) setindex_shape_check(X, lI) if lI > 0 @@ -373,8 +373,8 @@ function unsafe_setindex!{T}(A::Array{T}, X::Array{T}, I::UnitRange{Int}) end return A end -setindex!{T}(A::Array{T}, X::Array{T}, c::Colon) = unsafe_setindex!(A, X, c) -function unsafe_setindex!{T}(A::Array{T}, X::Array{T}, ::Colon) +function setindex!{T}(::BoundsCheck, A::Array{T}, X::Array{T}, ::Colon) + # Colon is always inbounds, no need to checkbounds lI = length(A) setindex_shape_check(X, lI) if lI > 0 diff --git a/base/bitarray.jl b/base/bitarray.jl index c5ae55b75fa02..eeed1ab32ffcf 100644 --- a/base/bitarray.jl +++ b/base/bitarray.jl @@ -348,8 +348,10 @@ bitpack{T,N}(A::AbstractArray{T,N}) = convert(BitArray{N}, A) return r end -@inline getindex(B::BitArray, i::Int) = (checkbounds(B, i); unsafe_getindex(B, i)) -@inline unsafe_getindex(B::BitArray, i::Int) = unsafe_bitgetindex(B.chunks, i) +@inline function getindex(b::BoundsCheck, B::BitArray, i::Int) + Bool(b) && checkbounds(B, i) + unsafe_bitgetindex(B.chunks, i) +end ## Indexing: setindex! ## @@ -365,8 +367,8 @@ end end end -setindex!(B::BitArray, x, i::Int) = (checkbounds(B, i); unsafe_setindex!(B, x, i)) -@inline function unsafe_setindex!(B::BitArray, x, i::Int) +@inline function setindex!(b::BoundsCheck, B::BitArray, x, i::Int) + Bool(b) && checkbounds(B, i); unsafe_bitsetindex!(B.chunks, convert(Bool, x), i) return B end @@ -377,8 +379,8 @@ end # we can't and must use getindex, otherwise silent corruption can happen) # When indexing with a BitArray, we can operate whole chunks at a time for a ~100x gain -setindex!(B::BitArray, x, I::BitArray) = (checkbounds(B, I); unsafe_setindex!(B, x, I)) -function unsafe_setindex!(B::BitArray, x, I::BitArray) +function setindex!(b::BoundsCheck, B::BitArray, x, I::BitArray) + Bool(b) && checkbounds(B, I) y = convert(Bool, x) Bc = B.chunks Ic = I.chunks @@ -397,8 +399,8 @@ end # Assigning an array of bools is more complicated, but we can still do some # work on chunks by combining X and I 64 bits at a time to improve perf by ~40% -setindex!(B::BitArray, X::AbstractArray, I::BitArray) = (checkbounds(B, I); unsafe_setindex!(B, X, I)) -function unsafe_setindex!(B::BitArray, X::AbstractArray, I::BitArray) +function setindex!(b::BoundsCheck, B::BitArray, X::AbstractArray, I::BitArray) + Bool(b) && checkbounds(B, I) Bc = B.chunks Ic = I.chunks length(Bc) == length(Ic) || throw_boundserror(B, I) @@ -414,7 +416,7 @@ function unsafe_setindex!(B::BitArray, X::AbstractArray, I::BitArray) for j = 1:(i < lc ? 64 : last_chunk_len) if Imsk & u != 0 lx < c && throw_setindex_mismatch(X, c) - x = convert(Bool, unsafe_getindex(X, c)) + x = convert(Bool, getindex(BoundsCheckOff(), X, c)) if x C |= u else diff --git a/base/deprecated.jl b/base/deprecated.jl index ada1adfca536d..629a80f5e8336 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -494,9 +494,9 @@ _ensure_vector(A::AbstractArray) = vec(A) _ensure_vector(A) = A _ensure_vectors() = () _ensure_vectors(A, As...) = (_ensure_vector(A), _ensure_vectors(As...)...) -function _unsafe_setindex!(l::LinearIndexing, A::AbstractArray, x, J::Union{Real,AbstractArray,Colon}...) - depwarn("multidimensional indexed assignment with multidimensional arrays is deprecated, use vec to convert indices to vectors", :_unsafe_setindex!) - _unsafe_setindex!(l, A, x, _ensure_vectors(J...)...) +function _setindex!(b::BoundsCheck, l::LinearIndexing, A::AbstractArray, x, J::Union{Real,AbstractArray,Colon}...) + depwarn("multidimensional indexed assignment with multidimensional arrays is deprecated, use vec to convert indices to vectors", :_setindex!) + _setindex!(b, l, A, x, _ensure_vectors(J...)...) end # 11554 diff --git a/base/essentials.jl b/base/essentials.jl index ef72f6bb81427..749f4d460b80a 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -245,3 +245,10 @@ const (:) = Colon() # For passing constants through type inference immutable Val{T} end + +# A trait for bounds check dispatch +abstract BoundsCheck +immutable BoundsCheckOff <: BoundsCheck end +immutable BoundsCheckOn <: BoundsCheck end +Bool(::BoundsCheckOn) = true +Bool(::BoundsCheckOff) = false diff --git a/base/exports.jl b/base/exports.jl index 69dfe92570ad0..afa846c4b7c84 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -36,6 +36,9 @@ export BitArray, BitMatrix, BitVector, + BoundsCheck, + BoundsCheckOn, + BoundsCheckOff, BufferStream, CartesianIndex, CartesianRange, diff --git a/base/linalg.jl b/base/linalg.jl index 8659acc51bdbb..bc97adf7fdc08 100644 --- a/base/linalg.jl +++ b/base/linalg.jl @@ -4,7 +4,7 @@ module LinAlg importall Base import Base: USE_BLAS64, size, copy, copy_transpose!, power_by_squaring, - print_matrix, transpose!, unsafe_getindex, unsafe_setindex! + print_matrix, transpose! export # Modules diff --git a/base/linalg/diagonal.jl b/base/linalg/diagonal.jl index 3f1034e05dff0..f7e9d17738f1c 100644 --- a/base/linalg/diagonal.jl +++ b/base/linalg/diagonal.jl @@ -35,13 +35,15 @@ fill!(D::Diagonal, x) = (fill!(D.diag, x); D) full(D::Diagonal) = diagm(D.diag) -getindex(D::Diagonal, i::Int, j::Int) = (checkbounds(D, i, j); unsafe_getindex(D, i, j)) -unsafe_getindex{T}(D::Diagonal{T}, i::Int, j::Int) = i == j ? unsafe_getindex(D.diag, i) : zero(T) +function getindex{T}(b::BoundsCheck, D::Diagonal{T}, i::Int, j::Int) + Bool(b) && checkbounds(D, i, j) + i == j ? getindex(BoundsCheckOff(), D.diag, i) : zero(T) +end -setindex!(D::Diagonal, v, i::Int, j::Int) = (checkbounds(D, i, j); unsafe_setindex!(D, v, i, j)) -function unsafe_setindex!(D::Diagonal, v, i::Int, j::Int) +function setindex!(b::BoundsCheck, D::Diagonal, v, i::Int, j::Int) + Bool(b) && checkbounds(D, i, j) if i == j - unsafe_setindex!(D.diag, v, i) + setindex!(BoundsCheckOff(), D.diag, v, i) else v == 0 || throw(ArgumentError("cannot set an off-diagonal index ($i, $j) to a nonzero value ($v)")) end diff --git a/base/linalg/symmetric.jl b/base/linalg/symmetric.jl index 6b3bd6c46b186..a9666369bc4d1 100644 --- a/base/linalg/symmetric.jl +++ b/base/linalg/symmetric.jl @@ -25,8 +25,8 @@ typealias RealHermSymComplexHerm{T<:Real,S} Union{Hermitian{T,S}, Symmetric{T,S} size(A::HermOrSym, args...) = size(A.data, args...) getindex(A::Symmetric, i::Integer, j::Integer) = (A.uplo == 'U') == (i < j) ? getindex(A.data, i, j) : getindex(A.data, j, i) getindex(A::Hermitian, i::Integer, j::Integer) = (A.uplo == 'U') == (i < j) ? getindex(A.data, i, j) : conj(getindex(A.data, j, i)) -unsafe_getindex(A::Symmetric, i::Integer, j::Integer) = (A.uplo == 'U') == (i < j) ? unsafe_getindex(A.data, i, j) : unsafe_getindex(A.data, j, i) -unsafe_getindex(A::Hermitian, i::Integer, j::Integer) = (A.uplo == 'U') == (i < j) ? unsafe_getindex(A.data, i, j) : conj(unsafe_getindex(A.data, j, i)) +getindex(b::BoundsCheckOff, A::Symmetric, i::Integer, j::Integer) = (A.uplo == 'U') == (i < j) ? getindex(b, A.data, i, j) : getindex(b, A.data, j, i) +getindex(b::BoundsCheckOff, A::Hermitian, i::Integer, j::Integer) = (A.uplo == 'U') == (i < j) ? getindex(b, A.data, i, j) : conj(getindex(b, A.data, j, i)) full(A::Symmetric) = copytri!(copy(A.data), A.uplo) full(A::Hermitian) = copytri!(copy(A.data), A.uplo, true) convert{T,S<:AbstractMatrix}(::Type{Symmetric{T,S}},A::Symmetric{T,S}) = A diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 38b5f1632fc9f..9e6fbe9a976d1 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -201,14 +201,11 @@ index_shape_dim(A, dim, ::Colon) = (trailingsize(A, dim),) # ambiguities for AbstractArray subtypes. See the note in abstractarray.jl # Note that it's most efficient to call checkbounds first, and then to_index -@inline function _getindex(l::LinearIndexing, A::AbstractArray, I::Union{Real, AbstractArray, Colon}...) - checkbounds(A, I...) - _unsafe_getindex(l, A, I...) -end -@generated function _unsafe_getindex(l::LinearIndexing, A::AbstractArray, I::Union{Real, AbstractArray, Colon}...) +@generated function _getindex(b::BoundsCheck, l::LinearIndexing, A::AbstractArray, I::Union{Real, AbstractArray, Colon}...) N = length(I) quote # This is specifically *not* inlined. + Bool(b) && checkbounds(A, I...) @nexprs $N d->(I_d = to_index(I[d])) dest = similar(A, @ncall $N index_shape A I) @ncall $N checksize dest I @@ -219,8 +216,9 @@ end # logical indexing optimization - don't use find (within to_index) # This is inherently a linear operation in the source, but we could potentially # use fast dividing integers to speed it up. -function _unsafe_getindex(::LinearIndexing, src::AbstractArray, I::AbstractArray{Bool}) +function _getindex(b::BoundsCheck, ::LinearIndexing, src::AbstractArray, I::AbstractArray{Bool}) # Both index_shape and checksize compute sum(I); manually hoist it out + Bool(b) && checkbounds(src, I) N = sum(I) dest = similar(src, (N,)) size(dest) == (N,) || throw(DimensionMismatch()) @@ -229,9 +227,9 @@ function _unsafe_getindex(::LinearIndexing, src::AbstractArray, I::AbstractArray s = 0 for b in eachindex(I) s+=1 - if unsafe_getindex(I, b) + if getindex(BoundsCheckOff(), I, b) d, Ds = next(D, Ds) - unsafe_setindex!(dest, unsafe_getindex(src, s), d) + setindex!(BoundsCheckOff(), dest, getindex(BoundsCheckOff(), src, s), d) end end dest @@ -244,7 +242,7 @@ end Ds = start(D) for idx in I d, Ds = next(D, Ds) - unsafe_setindex!(dest, unsafe_getindex(src, idx), d) + setindex!(BoundsCheckOff(), dest, getindex(BoundsCheckOff(), src, idx), d) end dest end @@ -259,9 +257,9 @@ end $(symbol(:offset_, N)) = 1 D = eachindex(dest) Ds = start(D) - @nloops $N i dest d->(offset_{d-1} = offset_d + (unsafe_getindex(I[d], i_d)-1)*stride_d) begin + @nloops $N i dest d->(offset_{d-1} = offset_d + (getindex(BoundsCheckOff(), I[d], i_d)-1)*stride_d) begin d, Ds = next(D, Ds) - unsafe_setindex!(dest, unsafe_getindex(src, offset_0), d) + setindex!(BoundsCheckOff(), dest, getindex(BoundsCheckOff(), src, offset_0), d) end dest end @@ -274,10 +272,10 @@ end $(Expr(:meta, :inline)) D = eachindex(dest) Ds = start(D) - @nloops $N i dest d->(j_d = unsafe_getindex(I[d], i_d)) begin + @nloops $N i dest d->(j_d = getindex(BoundsCheckOff(), I[d], i_d)) begin d, Ds = next(D, Ds) - v = @ncall $N unsafe_getindex src j - unsafe_setindex!(dest, v, d) + v = @ncall $N getindex BoundsCheckOff() src j + setindex!(BoundsCheckOff(), dest, v, d) end dest end @@ -297,38 +295,33 @@ _checksize(A::AbstractArray, dim, I::AbstractVector{Bool}) = size(A, dim) == sum _checksize(A::AbstractArray, dim, ::Colon) = true _checksize(A::AbstractArray, dim, ::Real) = size(A, dim) == 1 -@inline unsafe_setindex!(v::BitArray, x::Bool, ind::Int) = (Base.unsafe_bitsetindex!(v.chunks, x, ind); v) -@inline unsafe_setindex!(v::BitArray, x, ind::Real) = (Base.unsafe_bitsetindex!(v.chunks, convert(Bool, x), to_index(ind)); v) - ## setindex! ## # For multi-element setindex!, we check bounds, convert the indices (to_index), # and ensure the value to set is either an AbstractArray or a Repeated scalar # before redispatching to the _unsafe_batchsetindex! _iterable(v::AbstractArray) = v _iterable(v) = repeated(v) -@inline function _setindex!(l::LinearIndexing, A::AbstractArray, x, J::Union{Real,AbstractArray,Colon}...) - checkbounds(A, J...) - _unsafe_setindex!(l, A, x, J...) -end -@inline function _unsafe_setindex!(l::LinearIndexing, A::AbstractArray, x, J::Union{Real,AbstractVector,Colon}...) +@inline function _setindex!(b::BoundsCheck, l::LinearIndexing, A::AbstractArray, x, J::Union{Real,AbstractVector,Colon}...) + Bool(b) && checkbounds(A, J...) _unsafe_batchsetindex!(l, A, _iterable(x), to_index(J)...) end # While setindex! with one array argument doesn't mean anything special, it is # still supported for symmetry with getindex. -_unsafe_setindex!(l::LinearIndexing, A::AbstractArray, x, I::AbstractArray) = _unsafe_setindex!(l, A, x, vec(I)) +_setindex!(b::BoundsCheck, l::LinearIndexing, A::AbstractArray, x, I::AbstractArray) = _setindex!(b, l, A, x, vec(I)) # 1-d logical indexing: override the above to avoid calling find (in to_index) -function _unsafe_setindex!(::LinearIndexing, A::AbstractArray, x, I::AbstractVector{Bool}) +function _setindex!(b::BoundsCheck, ::LinearIndexing, A::AbstractArray, x, I::AbstractVector{Bool}) + Bool(b) && checkbounds(A, I) X = _iterable(x) Xs = start(X) i = 0 c = 0 for b in eachindex(I) i+=1 - if unsafe_getindex(I, b) + if getindex(BoundsCheckOff(), I, b) done(X, Xs) && throw_setindex_mismatch(x, c+1) (v, Xs) = next(X, Xs) - unsafe_setindex!(A, v, i) + setindex!(BoundsCheckOff(), A, v, i) c += 1 end end @@ -347,9 +340,9 @@ end stride_1 = 1 @nexprs $N d->(stride_{d+1} = stride_d*size(A,d)) $(symbol(:offset_, N)) = 1 - @nloops $N i d->(1:idxlens[d]) d->(offset_{d-1} = offset_d + (unsafe_getindex(I_d, i_d)-1)*stride_d) begin + @nloops $N i d->(1:idxlens[d]) d->(offset_{d-1} = offset_d + (getindex(BoundsCheckOff(), I_d, i_d)-1)*stride_d) begin v, Xs = next(X, Xs) - unsafe_setindex!(A, v, offset_0) + setindex!(BoundsCheckOff(), A, v, offset_0) end A end @@ -361,9 +354,9 @@ end idxlens = @ncall $N index_lengths A I @ncall $N setindex_shape_check X (d->idxlens[d]) Xs = start(X) - @nloops $N i d->(1:idxlens[d]) d->(j_d = unsafe_getindex(I_d, i_d)) begin + @nloops $N i d->(1:idxlens[d]) d->(j_d = getindex(BoundsCheckOff(), I_d, i_d)) begin v, Xs = next(X, Xs) - @ncall $N unsafe_setindex! A v j + @ncall $N setindex! BoundsCheckOff() A v j end A end @@ -386,17 +379,19 @@ function cartindex_exprs(indexes, syms) end exprs end -@generated function _getindex{T,N}(l::LinearIndexing, A::AbstractArray{T,N}, I::Union{Real,AbstractArray,Colon,CartesianIndex}...) - :($(Expr(:meta, :inline)); getindex(A, $(cartindex_exprs(I, :I)...))) -end -@generated function _unsafe_getindex{T,N}(l::LinearIndexing, A::AbstractArray{T,N}, I::Union{Real,AbstractArray,Colon,CartesianIndex}...) - :($(Expr(:meta, :inline)); unsafe_getindex(A, $(cartindex_exprs(I, :I)...))) -end -@generated function _setindex!{T,N}(l::LinearIndexing, A::AbstractArray{T,N}, v, I::Union{Real,AbstractArray,Colon,CartesianIndex}...) - :($(Expr(:meta, :inline)); setindex!(A, v, $(cartindex_exprs(I, :I)...))) +@generated function _getindex{T,N}(b::BoundsCheck, l::LinearIndexing, A::AbstractArray{T,N}, I::Union{Real,AbstractArray,Colon,CartesianIndex}...) + if b <: BoundsCheckOn + :($(Expr(:meta, :inline)); getindex(A, $(cartindex_exprs(I, :I)...))) + else + :($(Expr(:meta, :inline)); getindex(b, A, $(cartindex_exprs(I, :I)...))) + end end -@generated function _unsafe_setindex!{T,N}(l::LinearIndexing, A::AbstractArray{T,N}, v, I::Union{Real,AbstractArray,Colon,CartesianIndex}...) - :($(Expr(:meta, :inline)); unsafe_setindex!(A, v, $(cartindex_exprs(I, :I)...))) +@generated function _setindex!{T,N}(b::BoundsCheck, l::LinearIndexing, A::AbstractArray{T,N}, v, I::Union{Real,AbstractArray,Colon,CartesianIndex}...) + if b <: BoundsCheckOn + :($(Expr(:meta, :inline)); setindex!(A, v, $(cartindex_exprs(I, :I)...))) + else + :($(Expr(:meta, :inline)); setindex!(b, A, v, $(cartindex_exprs(I, :I)...))) + end end @@ -524,9 +519,9 @@ end Base.Cartesian.@nexprs $N d->(d < $N ? begin c, k = divrem(k, Istride_{$N-d+1}) - j += (Base.unsafe_getindex(I_{$N-d+1}, c+1)-1)*Pstride_{$N-d+1} + j += (Base.getindex(BoundsCheckOff(), I_{$N-d+1}, c+1)-1)*Pstride_{$N-d+1} end : begin - j += Base.unsafe_getindex(I_1, k+1) + j += Base.getindex(BoundsCheckOff(), I_1, k+1) end) index[i] = j end @@ -665,7 +660,7 @@ end $(symbol(:offset_, N)) = 1 ind = 0 Xc, Bc = X.chunks, B.chunks - @nloops $N i X d->(offset_{d-1} = offset_d + (unsafe_getindex(I[d], i_d)-1)*stride_d) begin + @nloops $N i X d->(offset_{d-1} = offset_d + (getindex(BoundsCheckOff(), I[d], i_d)-1)*stride_d) begin ind += 1 unsafe_bitsetindex!(Xc, unsafe_bitgetindex(Bc, offset_0), ind) end @@ -678,7 +673,9 @@ end # contiguous multidimensional indexing: if the first dimension is a range, # we can get some performance from using copy_chunks! -function unsafe_setindex!(B::BitArray, X::BitArray, I0::UnitRange{Int}) +function setindex!(b::BoundsCheck, B::BitArray, X::BitArray, I0::UnitRange{Int}) + Bool(b) && checkbounds(B, I0) + setindex_shape_check(X, length(I0)) l0 = length(I0) l0 == 0 && return B f0 = first(I0) @@ -686,7 +683,8 @@ function unsafe_setindex!(B::BitArray, X::BitArray, I0::UnitRange{Int}) return B end -function unsafe_setindex!(B::BitArray, x::Bool, I0::UnitRange{Int}) +function setindex!(b::BoundsCheck, B::BitArray, x::Bool, I0::UnitRange{Int}) + Bool(b) && checkbounds(B, I0) l0 = length(I0) l0 == 0 && return B f0 = first(I0) @@ -694,9 +692,11 @@ function unsafe_setindex!(B::BitArray, x::Bool, I0::UnitRange{Int}) return B end -@generated function unsafe_setindex!(B::BitArray, X::BitArray, I0::UnitRange{Int}, I::Union{Int,UnitRange{Int}}...) +@generated function setindex!(b::BoundsCheck, B::BitArray, X::BitArray, I0::UnitRange{Int}, I::Union{Int,UnitRange{Int}}...) N = length(I) + Isplat = Expr[:(I[$d]) for d=1:length(I)] quote + Bool(b) && checkbounds(B, I0, $(Isplat...)) length(X) == 0 && return B f0 = first(I0) l0 = length(I0) @@ -720,14 +720,16 @@ end copy_chunks!(B.chunks, ind, X.chunks, refind, l0) refind += l0 end) - + # TODO: check indexing shape return B end end -@generated function unsafe_setindex!(B::BitArray, x::Bool, I0::UnitRange{Int}, I::Union{Int,UnitRange{Int}}...) +@generated function setindex!(b::BoundsCheck, B::BitArray, x::Bool, I0::UnitRange{Int}, I::Union{Int,UnitRange{Int}}...) N = length(I) + Isplat = Expr[:(I[$d]) for d=1:length(I)] quote + Bool(b) && checkbounds(B, I0, $(Isplat...)) f0 = first(I0) l0 = length(I0) l0 == 0 && return B diff --git a/base/number.jl b/base/number.jl index fcd6a02d5f5d0..3dc298eef9885 100644 --- a/base/number.jl +++ b/base/number.jl @@ -15,7 +15,7 @@ getindex(x::Number) = x getindex(x::Number, i::Integer) = i == 1 ? x : throw(BoundsError()) getindex(x::Number, I::Integer...) = all([i == 1 for i in I]) ? x : throw(BoundsError()) getindex(x::Number, I::Real...) = getindex(x, to_index(I)...) -unsafe_getindex(x::Real, i::Real) = x +getindex(::BoundsCheckOff, x::Real, i::Real) = x first(x::Number) = x last(x::Number) = x diff --git a/base/range.jl b/base/range.jl index 00cfdeef620a1..cfccbb6665d8d 100644 --- a/base/range.jl +++ b/base/range.jl @@ -347,43 +347,48 @@ done(r::UnitRange, i) = i==oftype(i,r.stop)+1 ## indexing -getindex(r::Range, i::Integer) = (checkbounds(r, i); unsafe_getindex(r, i)) -unsafe_getindex{T}(v::Range{T}, i::Integer) = convert(T, first(v) + (i-1)*step(v)) +function getindex{T}(b::BoundsCheck, v::Range{T}, i::Integer) + Bool(b) && checkbounds(v, i) + convert(T, first(v) + (i-1)*step(v)) +end -getindex{T}(r::FloatRange{T}, i::Integer) = (checkbounds(r, i); unsafe_getindex(r, i)) -unsafe_getindex{T}(r::FloatRange{T}, i::Integer) = convert(T, (r.start + (i-1)*r.step)/r.divisor) +function getindex{T}(b::BoundsCheck, r::FloatRange{T}, i::Integer) + Bool(b) && checkbounds(r, i) + convert(T, (r.start + (i-1)*r.step)/r.divisor) +end -getindex{T}(r::LinSpace{T}, i::Integer) = (checkbounds(r, i); unsafe_getindex(r, i)) -unsafe_getindex{T}(r::LinSpace{T}, i::Integer) = convert(T, ((r.len-i)*r.start + (i-1)*r.stop)/r.divisor) +function getindex{T}(b::BoundsCheck, r::LinSpace{T}, i::Integer) + Bool(b) && checkbounds(r, i) + convert(T, ((r.len-i)*r.start + (i-1)*r.stop)/r.divisor) +end -getindex(r::Range, ::Colon) = copy(r) -unsafe_getindex(r::Range, ::Colon) = copy(r) +getindex(::BoundsCheck, r::Range, ::Colon) = copy(r) -getindex(r::UnitRange, s::UnitRange{Int}) = (checkbounds(r, s); unsafe_getindex(r, s)) -function unsafe_getindex(r::UnitRange, s::UnitRange{Int}) +function getindex(b::BoundsCheck, r::UnitRange, s::UnitRange{Int}) + Bool(b) && checkbounds(r, s) st = oftype(r.start, r.start + s.start-1) range(st, length(s)) end -getindex(r::UnitRange, s::StepRange{Int}) = (checkbounds(r, s); unsafe_getindex(r, s)) -function unsafe_getindex(r::UnitRange, s::StepRange{Int}) +function getindex(b::BoundsCheck, r::UnitRange, s::StepRange{Int}) + Bool(b) && checkbounds(r, s) st = oftype(r.start, r.start + s.start-1) range(st, step(s), length(s)) end -getindex(r::StepRange, s::Range{Int}) = (checkbounds(r, s); unsafe_getindex(r, s)) -function unsafe_getindex(r::StepRange, s::Range{Int}) +function getindex(b::BoundsCheck, r::StepRange, s::Range{Int}) + Bool(b) && checkbounds(r, s) st = oftype(r.start, r.start + (first(s)-1)*step(r)) range(st, step(r)*step(s), length(s)) end -getindex(r::FloatRange, s::OrdinalRange) = (checkbounds(r, s); unsafe_getindex(r, s)) -function unsafe_getindex(r::FloatRange, s::OrdinalRange) +function getindex(b::BoundsCheck, r::FloatRange, s::OrdinalRange) + Bool(b) && checkbounds(r, s) FloatRange(r.start + (first(s)-1)*r.step, step(s)*r.step, length(s), r.divisor) end -getindex(r::LinSpace, s::OrdinalRange) = (checkbounds(r, s); unsafe_getindex(r, s)) -function unsafe_getindex{T}(r::LinSpace{T}, s::OrdinalRange) +function getindex{T}(b::BoundsCheck, r::LinSpace{T}, s::OrdinalRange) + Bool(b) && checkbounds(r, s) sl::T = length(s) ifirst = first(s) ilast = last(s) diff --git a/base/subarray.jl b/base/subarray.jl index 0fceca29ea199..7a0788bc22760 100644 --- a/base/subarray.jl +++ b/base/subarray.jl @@ -309,7 +309,7 @@ end function rangetype(T1, T2) rt = return_types(getindex, Tuple{T1, T2}) - length(rt) == 1 || error("Can't infer return type") + length(rt) == 1 || error("Can't infer return type for $T1 and $T2") rt[1] end @@ -317,7 +317,7 @@ reindex(a, b) = a[b] reindex(a::UnitRange, b::UnitRange{Int}) = range(oftype(first(a), first(a)+first(b)-1), length(b)) reindex(a::UnitRange, b::StepRange{Int}) = range(oftype(first(a), first(a)+first(b)-1), step(b), length(b)) reindex(a::StepRange, b::Range{Int}) = range(oftype(first(a), first(a)+(first(b)-1)*step(a)), step(a)*step(b), length(b)) -reindex(a, b::Int) = unsafe_getindex(a, b) +reindex(a, b::Int) = getindex(BoundsCheckOff(), a, b) dimsizeexpr(Itype, d::Int, len::Int, Asym::Symbol, Isym::Symbol) = :(length($Isym[$d])) function dimsizeexpr(Itype::Type{Colon}, d::Int, len::Int, Asym::Symbol, Isym::Symbol) @@ -346,8 +346,9 @@ end length(I.parameters) == LD ? (:(LinearFast())) : (:(LinearSlow())) end +# Colon isn't an AbstractArray so it doesn't get the same BoundsCheck fallbacks getindex(::Colon, i) = to_index(i) -unsafe_getindex(v::Colon, i) = to_index(i) +getindex(::BoundsCheck, ::Colon, i) = to_index(i) step(::Colon) = 1 first(::Colon) = 1 @@ -551,20 +552,20 @@ end getindex(V.parent, $(idxs...)) end end -@generated function unsafe_getindex{T,N,P,IV,LD}(V::SubArray{T,N,P,IV,LD}, I::Int...) +@generated function getindex{T,N,P,IV,LD}(b::BoundsCheckOff, V::SubArray{T,N,P,IV,LD}, I::Int...) ni = length(I) if ni == 1 && length(IV.parameters) == LD # linear indexing meta = Expr(:meta, :inline) if iscontiguous(V) - return :($meta; unsafe_getindex(V.parent, V.first_index + I[1] - 1)) + return :($meta; getindex(b, V.parent, V.first_index + I[1] - 1)) end - return :($meta; unsafe_getindex(V.parent, V.first_index + V.stride1*(I[1]-1))) + return :($meta; getindex(b, V.parent, V.first_index + V.stride1*(I[1]-1))) end Isyms = [:(I[$d]) for d = 1:ni] exhead, idxs = index_generate(ndims(P), IV, :V, Isyms) quote $exhead - unsafe_getindex(V.parent, $(idxs...)) + getindex(b, V.parent, $(idxs...)) end end @generated function setindex!{T,N,P,IV,LD}(V::SubArray{T,N,P,IV,LD}, v, I::Int...) @@ -583,20 +584,20 @@ end setindex!(V.parent, v, $(idxs...)) end end -@generated function unsafe_setindex!{T,N,P,IV,LD}(V::SubArray{T,N,P,IV,LD}, v, I::Int...) +@generated function setindex!{T,N,P,IV,LD}(b::BoundsCheckOff, V::SubArray{T,N,P,IV,LD}, v, I::Int...) ni = length(I) if ni == 1 && length(IV.parameters) == LD # linear indexing meta = Expr(:meta, :inline) if iscontiguous(V) - return :($meta; unsafe_setindex!(V.parent, v, V.first_index + I[1] - 1)) + return :($meta; setindex!(b, V.parent, v, V.first_index + I[1] - 1)) end - return :($meta; unsafe_setindex!(V.parent, v, V.first_index + V.stride1*(I[1]-1))) + return :($meta; setindex!(b, V.parent, v, V.first_index + V.stride1*(I[1]-1))) end Isyms = [:(I[$d]) for d = 1:ni] exhead, idxs = index_generate(ndims(P), IV, :V, Isyms) quote $exhead - unsafe_setindex!(V.parent, v, $(idxs...)) + setindex!(b, V.parent, v, $(idxs...)) end end @@ -604,8 +605,8 @@ end # is just a matter of deleting the explicit call to copy. getindex{T,N,P,IV}(V::SubArray{T,N,P,IV}, I::ViewIndex...) = copy(sub(V, I...)) getindex{T,N,P,IV}(V::SubArray{T,N,P,IV}, I::Union{Real, AbstractVector, Colon}...) = getindex(V, to_index(I)...) -unsafe_getindex{T,N,P,IV}(V::SubArray{T,N,P,IV}, I::ViewIndex...) = copy(sub_unsafe(V, I)) -unsafe_getindex{T,N,P,IV}(V::SubArray{T,N,P,IV}, I::Union{Real, AbstractVector, Colon}...) = unsafe_getindex(V, to_index(I)...) +getindex{T,N,P,IV}(::BoundsCheckOff, V::SubArray{T,N,P,IV}, I::ViewIndex...) = copy(sub_unsafe(V, I)) +getindex{T,N,P,IV}(b::BoundsCheckOff, V::SubArray{T,N,P,IV}, I::Union{Real, AbstractVector, Colon}...) = getindex(b, V, to_index(I)...) # Nonscalar setindex! falls back to the AbstractArray versions @@ -657,7 +658,7 @@ function index_generate(NP, Itypes, Vsym, Isyms) indexexprs[i] = :($Vsym.indexes[$i]) else j += 1 - indexexprs[i] = :(unsafe_getindex($Vsym.indexes[$i], $(Isyms[j]))) + indexexprs[i] = :(getindex(BoundsCheckOff(), $Vsym.indexes[$i], $(Isyms[j]))) end end # Note that we drop any extra indices. We're trusting that the indices are