Skip to content

Commit 26d84cd

Browse files
committed
Make unvetted size throw an error for arrays with non-1 indexing
1 parent 10013c5 commit 26d84cd

16 files changed

+139
-97
lines changed

base/abstractarray.jl

Lines changed: 52 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,44 @@ end
2323

2424
size{T,N}(t::AbstractArray{T,N}, d) = d <= N ? size(t)[d] : 1
2525
size{N}(x, d1::Integer, d2::Integer, dx::Vararg{Integer, N}) = (size(x, d1), size(x, d2), ntuple(k->size(x, dx[k]), Val{N})...)
26+
27+
# trait to indicate "vetted" usage for indexing that may not start at 1
28+
abstract IndicesSafety
29+
immutable SafeIndices <: IndicesSafety end
30+
immutable UnsafeIndices <: IndicesSafety end
31+
32+
macro safeindices(ex)
33+
esc(_safeindices(ex))
34+
end
35+
36+
function _safeindices(ex::Expr)
37+
if ex.head == :call
38+
f = ex.args[1]
39+
if f == :size || f == :length
40+
return Expr(:call, f, :(Base.SafeIndices()), ex.args[2:end]...)
41+
end
42+
end
43+
return Expr(ex.head, map(_safeindices, ex.args)...)
44+
end
45+
46+
_safeindices(arg) = arg
47+
48+
# The default is that size is safe, but array types that use non-1
49+
# indexing should specialize this to make it unsafe without
50+
# SafeIndices().
51+
size( ::IndicesSafety, A) = size(A)
52+
size( ::IndicesSafety, A::AbstractArray, d) = size(A,d) # fixme
53+
# size(s::IndicesSafety, A::AbstractArray, d) = d <= ndims(A) ? size(s, A)[d] : 1
54+
size{N}(s::IndicesSafety, A::AbstractArray, d1::Integer, d2::Integer, dx::Vararg{Integer, N}) = (size(s, A, d1), size(s, A, d2), ntuple(k->size(s, A, dx[k]), Val{N})...)
55+
2656
"""
2757
indices(A, d)
2858
2959
Returns the valid range of indices for array `A` along dimension `d`.
3060
"""
3161
function indices(A::AbstractArray, d)
3262
@_inline_meta
33-
1:size(A,d)
63+
1:size(SafeIndices(),A,d)
3464
end
3565
"""
3666
indices(A)
@@ -61,15 +91,17 @@ is `indices(A, 1)`.
6191
Calling this function is the "safe" way to write algorithms that
6292
exploit linear indexing.
6393
"""
64-
linearindices(A) = 1:length(A)
94+
linearindices(A) = 1:length(SafeIndices(), A)
6595
linearindices(A::AbstractVector) = indices1(A)
6696
eltype{T}(::Type{AbstractArray{T}}) = T
6797
eltype{T,N}(::Type{AbstractArray{T,N}}) = T
6898
elsize{T}(::AbstractArray{T}) = sizeof(T)
6999
ndims{T,N}(::AbstractArray{T,N}) = N
70100
ndims{T,N}(::Type{AbstractArray{T,N}}) = N
71101
ndims{T<:AbstractArray}(::Type{T}) = ndims(supertype(T))
72-
length(t::AbstractArray) = prod(size(t))::Int
102+
length(s::IndicesSafety, t::AbstractArray) = prod(size(s,t))
103+
length(s::IndicesSafety, t) = length(t)
104+
length(t::AbstractArray) = length(UnsafeIndices(), t)
73105
endof(a::AbstractArray) = length(a)
74106
first(a::AbstractArray) = a[first(eachindex(a))]
75107

@@ -120,13 +152,14 @@ function isassigned(a::AbstractArray, i::Int...)
120152
end
121153

122154
# used to compute "end" for last index
123-
function trailingsize(A, n)
124-
s = 1
155+
function trailingsize(s::IndicesSafety, A, n)
156+
sz = 1
125157
for i=n:ndims(A)
126-
s *= size(A,i)
158+
sz *= size(s,A,i)
127159
end
128-
return s
160+
return sz
129161
end
162+
trailingsize(A, n) = trailingsize(UnsafeIndices(), A, n)
130163

131164
## Traits for array types ##
132165

@@ -178,21 +211,22 @@ start at something different from 1), it is equivalent to `indices(A,
178211
d)`.
179212
"""
180213
shape(a, d) = shape(indicesbehavior(a), a, d)
181-
shape(::IndicesStartAt1, a) = size(a)
182-
shape(::IndicesStartAt1, a, d) = size(a, d)
214+
shape(::IndicesStartAt1, a) = size(SafeIndices(), a)
215+
shape(::IndicesStartAt1, a, d) = size(SafeIndices(), a, d)
183216
shape(::IndicesBehavior, a) = indices(a)
184217
shape(::IndicesBehavior, a, d) = indices(a, d)
185218

186219
## Bounds checking ##
187-
@generated function trailingsize{T,N,n}(A::AbstractArray{T,N}, ::Type{Val{n}})
220+
@generated function trailingsize{T,N,n}(s::IndicesSafety, A::AbstractArray{T,N}, ::Type{Val{n}})
188221
(isa(n, Int) && isa(N, Int)) || error("Must have concrete type")
189222
n > N && return 1
190-
ex = :(size(A, $n))
223+
ex = :(size(s, A, $n))
191224
for m = n+1:N
192-
ex = :($ex * size(A, $m))
225+
ex = :($ex * size(s, A, $m))
193226
end
194227
Expr(:block, Expr(:meta, :inline), ex)
195228
end
229+
trailingsize{n}(A::AbstractArray, ::Type{Val{n}}) = trailingsize(UnsafeIndices(), A, Val{n})
196230

197231
# check along a single dimension
198232
"""
@@ -265,13 +299,9 @@ _chkbnds(A::AbstractArray, ::NTuple{1,Bool}, I::AbstractVector{Bool}) = length(A
265299
_chkbnds(A::AbstractVector, ::NTuple{1,Bool}, I::AbstractArray{Bool}) = length(A) == length(I)
266300
_chkbnds(A::AbstractVector, ::NTuple{1,Bool}, I::AbstractVector{Bool}) = indices(A) == indices(I)
267301
# Linear indexing:
268-
function _chkbnds(A::AbstractVector, ::NTuple{1,Bool}, I)
269-
@_inline_meta
270-
checkindex(Bool, indices1(A), I)
271-
end
272302
function _chkbnds(A::AbstractArray, ::NTuple{1,Bool}, I)
273303
@_inline_meta
274-
checkindex(Bool, 1:length(A), I)
304+
checkindex(Bool, linearindices(A), I)
275305
end
276306
# When all indices have been checked:
277307
_chkbnds{M}(A, checked::NTuple{M,Bool}) = checked[M]
@@ -359,7 +389,7 @@ similar( a::AbstractArray, T::Type, dims::DimsInteger) = similar(a, T, convert
359389
# similar creates an Array by default
360390
similar( a::AbstractArray, T::Type, dims::Dims) = Array(T, dims)
361391

362-
_similar(::IndicesStartAt1, a::AbstractArray, T::Type) = similar(a, T, size(a))
392+
_similar(::IndicesStartAt1, a::AbstractArray, T::Type) = similar(a, T, size(SafeIndices(), a))
363393
_similar(::IndicesBehavior, a::AbstractArray, T::Type) = similar(a, T, indices(a))
364394

365395
"""
@@ -525,7 +555,7 @@ function copy!(::LinearIndexing, dest::AbstractArray, ::LinearSlow, src::Abstrac
525555
end
526556

527557
function copy!(dest::AbstractArray, dstart::Integer, src::AbstractArray)
528-
copy!(dest, dstart, src, first(linearindices(src)), length(src))
558+
copy!(dest, dstart, src, first(linearindices(src)), length(SafeIndices(), src))
529559
end
530560

531561
function copy!(dest::AbstractArray, dstart::Integer, src::AbstractArray, sstart::Integer)
@@ -650,7 +680,7 @@ function _maxlength(A, B, C...)
650680
max(length(A), _maxlength(B, C...))
651681
end
652682

653-
isempty(a::AbstractArray) = (length(a) == 0)
683+
isempty(a::AbstractArray) = (length(SafeIndices(), a) == 0)
654684

655685
## Conversions ##
656686

@@ -1417,7 +1447,7 @@ function mapslices(f, A::AbstractArray, dims::AbstractVector)
14171447
idx[d] = Colon()
14181448
end
14191449

1420-
r1 = f(view(A, idx...))
1450+
r1 = f(A[idx...])
14211451

14221452
# determine result size and allocate
14231453
Rsize = copy(dimsA)
@@ -1427,7 +1457,7 @@ function mapslices(f, A::AbstractArray, dims::AbstractVector)
14271457
end
14281458
nextra = max(0,length(dims)-ndims(r1))
14291459
if eltype(Rsize) == Int
1430-
Rsize[dims] = [size(r1)..., ntuple(d->1, nextra)...]
1460+
Rsize[dims] = [size(SafeIndices(), r1)..., ntuple(d->1, nextra)...]
14311461
else
14321462
Rsize[dims] = [indices(r1)..., ntuple(d->1:1, nextra)...]
14331463
end

base/abstractarraymath.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ transpose(a::AbstractArray) = error("transpose not implemented for $(typeof(a)).
1111

1212
## Constructors ##
1313

14-
vec(a::AbstractArray) = reshape(a,length(a))
14+
@safeindices vec(a::AbstractArray) = reshape(a,length(a))
1515
vec(a::AbstractVector) = a
1616

1717
_sub(::Tuple{}, ::Tuple{}) = ()
@@ -69,7 +69,7 @@ function flipdim(A::AbstractVector, d::Integer)
6969
reverse(A)
7070
end
7171

72-
function flipdim(A::AbstractArray, d::Integer)
72+
@safeindices function flipdim(A::AbstractArray, d::Integer)
7373
nd = ndims(A)
7474
if d > nd || isempty(A)
7575
return copy(A)
@@ -107,7 +107,7 @@ function circshift{T,N}(a::AbstractArray{T,N}, shiftamts)
107107
end
108108

109109
# Uses K-B-N summation
110-
function cumsum_kbn{T<:AbstractFloat}(v::AbstractVector{T})
110+
@safeindices function cumsum_kbn{T<:AbstractFloat}(v::AbstractVector{T})
111111
r = similar(v)
112112
if isempty(v); return r; end
113113

base/arraymath.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ function ctranspose!(B::AbstractMatrix, A::AbstractVector)
271271
end
272272

273273
const transposebaselength=64
274-
function transpose_f!(f,B::AbstractMatrix,A::AbstractMatrix)
274+
@safeindices function transpose_f!(f,B::AbstractMatrix,A::AbstractMatrix)
275275
indices(B,1) == indices(A,2) && indices(B,2) == indices(A,1) || throw(DimensionMismatch(string(f)))
276276

277277
m, n = size(A)

base/broadcast.jl

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ end
6464
@inline _newindex(out, I, keep::Bool, indexmap...) = _newindex((out..., ifelse(keep, I[1], 1)), tail(I), indexmap...)
6565

6666
newindexer(sz, x::Number) = ()
67-
@inline newindexer(sz, A) = _newindexer(sz, size(A))
67+
@safeindices @inline newindexer(sz, A) = _newindexer(sz, size(A))
6868
@inline _newindexer(sz, szA::Tuple{}) = ()
6969
@inline _newindexer(sz, szA) = (sz[1] == szA[1], _newindexer(tail(sz), tail(szA))...)
7070

@@ -132,7 +132,7 @@ end
132132
end
133133
end
134134

135-
@inline function broadcast!{nargs}(f, B::AbstractArray, As::Vararg{Any,nargs})
135+
@safeindices @inline function broadcast!{nargs}(f, B::AbstractArray, As::Vararg{Any,nargs})
136136
check_broadcast_shape(shape(B), As...)
137137
sz = size(B)
138138
mapindex = map(x->newindexer(sz, x), As)
@@ -175,7 +175,7 @@ end
175175
end
176176
end
177177

178-
function broadcast_t(f, ::Type{Any}, As...)
178+
@safeindices function broadcast_t(f, ::Type{Any}, As...)
179179
shp = broadcast_shape(As...)
180180
iter = CartesianRange(shp)
181181
if isempty(iter)
@@ -218,12 +218,12 @@ end
218218
@inline bitbroadcast(f, As...) = broadcast!(f, allocate_for(BitArray, As, broadcast_shape(As...)), As...)
219219

220220
broadcast_getindex(src::AbstractArray, I::AbstractArray...) = broadcast_getindex!(Array{eltype(src)}(broadcast_shape(I...)), src, I...)
221-
@generated function broadcast_getindex!(dest::AbstractArray, src::AbstractArray, I::AbstractArray...)
221+
@safeindices @generated function broadcast_getindex!(dest::AbstractArray, src::AbstractArray, I::AbstractArray...)
222222
N = length(I)
223223
Isplat = Expr[:(I[$d]) for d = 1:N]
224224
quote
225225
@nexprs $N d->(I_d = I[d])
226-
check_broadcast_shape(size(dest), $(Isplat...)) # unnecessary if this function is never called directly
226+
check_broadcast_shape(shape(dest), $(Isplat...)) # unnecessary if this function is never called directly
227227
checkbounds(src, $(Isplat...))
228228
@nloops $N i dest d->(@nexprs $N k->(j_d_k = size(I_k, d) == 1 ? 1 : i_d)) begin
229229
@nexprs $N k->(@inbounds J_k = @nref $N I_k d->j_d_k)
@@ -233,7 +233,7 @@ broadcast_getindex(src::AbstractArray, I::AbstractArray...) = broadcast_getindex
233233
end
234234
end
235235

236-
@generated function broadcast_setindex!(A::AbstractArray, x, I::AbstractArray...)
236+
@safeindices @generated function broadcast_setindex!(A::AbstractArray, x, I::AbstractArray...)
237237
N = length(I)
238238
Isplat = Expr[:(I[$d]) for d = 1:N]
239239
quote

base/exports.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,7 @@ export
577577
rot180,
578578
rotl90,
579579
rotr90,
580+
@safeindices,
580581
searchsorted,
581582
searchsortedfirst,
582583
searchsortedlast,

base/multidimensional.jl

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ index_lengths(A::AbstractArray, I::Colon) = (length(A),)
165165
@inline index_lengths(A::AbstractArray, I...) = index_lengths_dim(A, 1, I...)
166166
index_lengths_dim(A, dim) = ()
167167
index_lengths_dim(A, dim, ::Colon) = (trailingsize(A, dim),)
168-
@inline index_lengths_dim(A, dim, ::Colon, i, I...) = (size(A, dim), index_lengths_dim(A, dim+1, i, I...)...)
168+
@safeindices @inline index_lengths_dim(A, dim, ::Colon, i, I...) = (size(A, dim), index_lengths_dim(A, dim+1, i, I...)...)
169169
@inline index_lengths_dim(A, dim, ::Real, I...) = (1, index_lengths_dim(A, dim+1, I...)...)
170170
@inline index_lengths_dim{N}(A, dim, ::CartesianIndex{N}, I...) = (1, index_lengths_dim(A, dim+N, I...)...)
171171
@inline index_lengths_dim(A, dim, i::AbstractArray, I...) = (length(i), index_lengths_dim(A, dim+1, I...)...)
@@ -178,9 +178,9 @@ index_lengths_dim(A, dim, ::Colon) = (trailingsize(A, dim),)
178178
# allows us to dispatch, which is important for the type-stability of
179179
# the lines involving Colon as the final index.
180180
index_shape(A::AbstractVector, I::Colon) = shape(A)
181-
index_shape(A::AbstractArray, I::Colon) = (length(A),)
181+
index_shape(A::AbstractArray, I::Colon) = (length(SafeIndices(), A),)
182182
@inline index_shape(A::AbstractArray, I...) = index_shape_dim(A, (true,), I...)
183-
@inline index_shape_dim(A, dim, ::Colon) = (trailingsize(A, length(dim)),)
183+
@inline index_shape_dim{N}(A, dim::NTuple{N}, ::Colon) = (trailingsize(SafeIndices(), A, Val{N}),)
184184
@inline index_shape_dim{T,N}(A::AbstractArray{T,N}, dim::NTuple{N}, ::Colon) = (shape(A, N),)
185185
@inline index_shape_dim(A, dim, I::Real...) = ()
186186
@inline index_shape_dim(A, dim, ::Colon, i, I...) = (shape(A, length(dim)), index_shape_dim(A, (dim...,true), i, I...)...)
@@ -190,8 +190,7 @@ index_shape(A::AbstractArray, I::Colon) = (length(A),)
190190
@inline index_shape_dim(A, dim, i::AbstractArray{Bool}, I...) = (sum(i), index_shape_dim(A, (dim...,true), I...)...)
191191
@inline index_shape_dim{N}(A, dim, i::AbstractArray{CartesianIndex{N}}, I...) = (shape(i)..., index_shape_dim(A, (dim...,ntuple(d->true,Val{N})...), I...)...)
192192

193-
@inline decolon(A::AbstractVector, ::Colon) = (indices(A,1),)
194-
@inline decolon(A::AbstractArray, ::Colon) = (1:length(A),)
193+
@inline decolon(A::AbstractArray, ::Colon) = (linearindices(A),)
195194
@inline decolon(A::AbstractArray, I...) = decolon_dim(A, (true,), I...)
196195
@inline decolon_dim(A::AbstractArray, dim) = ()
197196
@inline decolon_dim{T,N}(A::AbstractArray{T,N}, dim::NTuple{N}, ::Colon) = (indices(A, N),)
@@ -231,13 +230,13 @@ end
231230
@nexprs $N d->(I_d = to_index(I[d]))
232231
shape = @ncall $N index_shape A I
233232
dest = similar(A, shape)
234-
size(dest) == map(dimlength, shape) || throw_checksize_error(dest, shape)
233+
size(SafeIndices(), dest) == map(dimlength, shape) || throw_checksize_error(dest, shape)
235234
@ncall $N _unsafe_getindex! dest A I
236235
end
237236
end
238237

239238
# logical indexing optimization - don't use find (within to_index)
240-
function _unsafe_getindex(::LinearIndexing, src::AbstractArray, I::AbstractArray{Bool})
239+
@safeindices function _unsafe_getindex(::LinearIndexing, src::AbstractArray, I::AbstractArray{Bool})
241240
shape = index_shape(src, I)
242241
dest = similar(src, shape)
243242
size(dest) == map(dimlength, shape) || throw_checksize_error(dest, shape)
@@ -254,7 +253,7 @@ function _unsafe_getindex(::LinearIndexing, src::AbstractArray, I::AbstractArray
254253
end
255254

256255
# specialized form for LinearFast
257-
function _unsafe_getindex(::LinearFast, src::AbstractArray, I::AbstractArray{Bool})
256+
@safeindices function _unsafe_getindex(::LinearFast, src::AbstractArray, I::AbstractArray{Bool})
258257
shape = index_shape(src, I)
259258
dest = similar(src, shape)
260259
size(dest) == shape || throw_checksize_error(dest, shape)
@@ -410,22 +409,24 @@ for (f, fmod, op) = ((:cummin, :_cummin!, :min), (:cummax, :_cummax!, :max))
410409
return res
411410
end
412411

413-
@eval function ($f)(A::AbstractArray, axis::Integer)
412+
@safeindices @eval function ($f)(A::AbstractArray, axis::Integer)
414413
res = similar(A)
415414
if size(A, axis) < 1
416415
return res
417416
end
418-
R1 = CartesianRange(size(A)[1:axis-1])
419-
R2 = CartesianRange(size(A)[axis+1:end])
417+
R1 = CartesianRange(indices(A)[1:axis-1])
418+
R2 = CartesianRange(indices(A)[axis+1:end])
420419
($fmod)(res, A, R1, R2, axis)
421420
end
422421

423422
@eval @noinline function ($fmod)(res, A::AbstractArray, R1::CartesianRange, R2::CartesianRange, axis::Integer)
423+
inds = indices(A, axis)
424+
i1 = first(inds)
424425
for I2 in R2
425426
for I1 in R1
426-
res[I1, 1, I2] = A[I1, 1, I2]
427+
res[I1, i1, I2] = A[I1, i1, I2]
427428
end
428-
for i = 2:size(A, axis)
429+
for i = i1+1:last(inds)
429430
for I1 in R1
430431
res[I1, i, I2] = ($op)(A[I1, i, I2], res[I1, i-1, I2])
431432
end
@@ -445,7 +446,7 @@ cumprod!(B, A) = cumprod!(B, A, 1)
445446
cumsum!(B, A, axis::Integer) = cumop!(+, B, A, axis)
446447
cumprod!(B, A, axis::Integer) = cumop!(*, B, A, axis)
447448

448-
function cumop!(op, B, A, axis::Integer)
449+
@safeindices function cumop!(op, B, A, axis::Integer)
449450
if size(B, axis) < 1
450451
return B
451452
end

0 commit comments

Comments
 (0)