Skip to content

Commit

Permalink
Merge pull request JuliaLang#24899 from JuliaLang/teh/remove_the_trai…
Browse files Browse the repository at this point in the history
…ning_wheels

Remove the "experimental" status from non-1 indexing
  • Loading branch information
timholy authored Jul 11, 2018
2 parents f3ad067 + 909f912 commit f104ea4
Show file tree
Hide file tree
Showing 63 changed files with 540 additions and 208 deletions.
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,10 @@ Language changes
* `try` blocks without `catch` or `finally` are no longer allowed. An explicit empty
`catch` block should be written instead ([#27554]).

* `AbstractArray` types that use unconventional (not 1-based) indexing can now support
`size`, `length`, and `@inbounds`. To optionally enforce conventional indices,
you can `@assert !has_offset_axes(A)`.

Breaking changes
----------------

Expand Down
39 changes: 24 additions & 15 deletions base/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,23 @@ function axes(A)
map(OneTo, size(A))
end

"""
has_offset_axes(A)
has_offset_axes(A, B, ...)
Return `true` if the indices of `A` start with something other than 1 along any axis.
If multiple arguments are passed, equivalent to `has_offset_axes(A) | has_offset_axes(B) | ...`.
"""
has_offset_axes(A) = _tuple_any(x->first(x)!=1, axes(A))
has_offset_axes(A...) = _tuple_any(has_offset_axes, A)
has_offset_axes(::Colon) = false

# Performance optimization: get rid of a branch on `d` in `axes(A, d)`
# for d=1. 1d arrays are heavily used, and the first dimension comes up
# in other applications.
indices1(A::AbstractArray{<:Any,0}) = OneTo(1)
indices1(A::AbstractArray) = (@_inline_meta; axes(A)[1])
indices1(iter) = OneTo(length(iter))
axes1(A::AbstractArray{<:Any,0}) = OneTo(1)
axes1(A::AbstractArray) = (@_inline_meta; axes(A)[1])
axes1(iter) = OneTo(length(iter))

unsafe_indices(A) = axes(A)
unsafe_indices(r::AbstractRange) = (OneTo(unsafe_length(r)),) # Ranges use checked_sub for size
Expand Down Expand Up @@ -154,14 +165,12 @@ julia> length([1 2; 3 4])
```
"""
length(t::AbstractArray) = (@_inline_meta; prod(size(t)))
_length(A::AbstractArray) = (@_inline_meta; prod(map(unsafe_length, axes(A)))) # circumvent missing size
_length(A) = (@_inline_meta; length(A))

# `eachindex` is mostly an optimization of `keys`
eachindex(itrs...) = keys(itrs...)

# eachindex iterates over all indices. IndexCartesian definitions are later.
eachindex(A::AbstractVector) = (@_inline_meta(); indices1(A))
eachindex(A::AbstractVector) = (@_inline_meta(); axes1(A))

"""
eachindex(A...)
Expand Down Expand Up @@ -209,8 +218,8 @@ function eachindex(A::AbstractArray, B::AbstractArray...)
@_inline_meta
eachindex(IndexStyle(A,B...), A, B...)
end
eachindex(::IndexLinear, A::AbstractArray) = (@_inline_meta; OneTo(_length(A)))
eachindex(::IndexLinear, A::AbstractVector) = (@_inline_meta; indices1(A))
eachindex(::IndexLinear, A::AbstractArray) = (@_inline_meta; OneTo(length(A)))
eachindex(::IndexLinear, A::AbstractVector) = (@_inline_meta; axes1(A))
function eachindex(::IndexLinear, A::AbstractArray, B::AbstractArray...)
@_inline_meta
indsA = eachindex(IndexLinear(), A)
Expand Down Expand Up @@ -509,7 +518,7 @@ function checkindex(::Type{Bool}, inds::AbstractUnitRange, r::AbstractRange)
@_propagate_inbounds_meta
isempty(r) | (checkindex(Bool, inds, first(r)) & checkindex(Bool, inds, last(r)))
end
checkindex(::Type{Bool}, indx::AbstractUnitRange, I::AbstractVector{Bool}) = indx == indices1(I)
checkindex(::Type{Bool}, indx::AbstractUnitRange, I::AbstractVector{Bool}) = indx == axes1(I)
checkindex(::Type{Bool}, indx::AbstractUnitRange, I::AbstractArray{Bool}) = false
function checkindex(::Type{Bool}, inds::AbstractUnitRange, I::AbstractArray)
@_inline_meta
Expand Down Expand Up @@ -744,7 +753,7 @@ function copyto!(::IndexStyle, dest::AbstractArray, ::IndexCartesian, src::Abstr
end

function copyto!(dest::AbstractArray, dstart::Integer, src::AbstractArray)
copyto!(dest, dstart, src, first(LinearIndices(src)), _length(src))
copyto!(dest, dstart, src, first(LinearIndices(src)), length(src))
end

function copyto!(dest::AbstractArray, dstart::Integer, src::AbstractArray, sstart::Integer)
Expand Down Expand Up @@ -837,7 +846,7 @@ function iterate(A::AbstractArray, state=(eachindex(A),))
A[y[1]], (state[1], tail(y)...)
end

isempty(a::AbstractArray) = (_length(a) == 0)
isempty(a::AbstractArray) = (length(a) == 0)


## range conversions ##
Expand Down Expand Up @@ -1148,9 +1157,9 @@ get(A::AbstractArray, I::Dims, default) = checkbounds(Bool, A, I...) ? A[I...] :

function get!(X::AbstractVector{T}, A::AbstractVector, I::Union{AbstractRange,AbstractVector{Int}}, default::T) where T
# 1d is not linear indexing
ind = findall(in(indices1(A)), I)
ind = findall(in(axes1(A)), I)
X[ind] = A[I[ind]]
Xind = indices1(X)
Xind = axes1(X)
X[first(Xind):first(ind)-1] = default
X[last(ind)+1:last(Xind)] = default
X
Expand Down Expand Up @@ -1776,9 +1785,9 @@ _sub2ind(inds::Union{DimsInteger,Indices}, I1::AbstractVector{T}, I::AbstractVec
_sub2ind_vecs(inds, I1, I...)
function _sub2ind_vecs(inds, I::AbstractVector...)
I1 = I[1]
Iinds = indices1(I1)
Iinds = axes1(I1)
for j = 2:length(I)
indices1(I[j]) == Iinds || throw(DimensionMismatch("indices of I[1] ($(Iinds)) does not match indices of I[$j] ($(indices1(I[j])))"))
axes1(I[j]) == Iinds || throw(DimensionMismatch("indices of I[1] ($(Iinds)) does not match indices of I[$j] ($(axes1(I[j])))"))
end
Iout = similar(I1)
_sub2ind!(Iout, inds, Iinds, I)
Expand Down
6 changes: 3 additions & 3 deletions base/abstractarraymath.jl
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ julia> vec(1:3)
See also [`reshape`](@ref).
"""
vec(a::AbstractArray) = reshape(a,_length(a))
vec(a::AbstractArray) = reshape(a,length(a))
vec(a::AbstractVector) = a

_sub(::Tuple{}, ::Tuple{}) = ()
Expand Down Expand Up @@ -72,7 +72,7 @@ squeeze(A; dims) = _squeeze(A, dims)
function _squeeze(A::AbstractArray, dims::Dims)
for i in 1:length(dims)
1 <= dims[i] <= ndims(A) || throw(ArgumentError("squeezed dims must be in range 1:ndims(A)"))
_length(axes(A, dims[i])) == 1 || throw(ArgumentError("squeezed dims must all be size 1"))
length(axes(A, dims[i])) == 1 || throw(ArgumentError("squeezed dims must all be size 1"))
for j = 1:i-1
dims[j] == dims[i] && throw(ArgumentError("squeezed dims must be unique"))
end
Expand Down Expand Up @@ -158,7 +158,7 @@ function reverse(A::AbstractArray; dims::Integer)
B = similar(A)
nnd = 0
for i = 1:nd
nnd += Int(_length(inds[i])==1 || i==d)
nnd += Int(length(inds[i])==1 || i==d)
end
indsd = inds[d]
sd = first(indsd)+last(indsd)
Expand Down
12 changes: 8 additions & 4 deletions base/array.jl
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,7 @@ for (fname, felt) in ((:zeros, :zero), (:ones, :one))
end

function _one(unit::T, x::AbstractMatrix) where T
@assert !has_offset_axes(x)
m,n = size(x)
m==n || throw(DimensionMismatch("multiplicative identity defined only for square matrices"))
# Matrix{T}(I, m, m)
Expand Down Expand Up @@ -547,9 +548,9 @@ end

_collect_indices(::Tuple{}, A) = copyto!(Array{eltype(A),0}(undef), A)
_collect_indices(indsA::Tuple{Vararg{OneTo}}, A) =
copyto!(Array{eltype(A)}(undef, _length.(indsA)), A)
copyto!(Array{eltype(A)}(undef, length.(indsA)), A)
function _collect_indices(indsA, A)
B = Array{eltype(A)}(undef, _length.(indsA))
B = Array{eltype(A)}(undef, length.(indsA))
copyto!(B, CartesianIndices(axes(B)), A, CartesianIndices(indsA))
end

Expand Down Expand Up @@ -748,6 +749,7 @@ function setindex! end
function setindex!(A::Array, X::AbstractArray, I::AbstractVector{Int})
@_propagate_inbounds_meta
@boundscheck setindex_shape_check(X, length(I))
@assert !has_offset_axes(X)
X′ = unalias(A, X)
I′ = unalias(A, I)
count = 1
Expand Down Expand Up @@ -866,7 +868,7 @@ themselves in another collection. The result is of the preceding example is equi
"""
function append!(a::Array{<:Any,1}, items::AbstractVector)
itemindices = eachindex(items)
n = _length(itemindices)
n = length(itemindices)
_growend!(a, n)
copyto!(a, length(a)-n+1, items, first(itemindices), n)
return a
Expand All @@ -876,6 +878,7 @@ append!(a::Vector, iter) = _append!(a, IteratorSize(iter), iter)
push!(a::Vector, iter...) = append!(a, iter)

function _append!(a, ::Union{HasLength,HasShape}, iter)
@assert !has_offset_axes(a)
n = length(a)
resize!(a, n+length(iter))
@inbounds for (i,item) in zip(n+1:length(a), iter)
Expand Down Expand Up @@ -909,7 +912,7 @@ function prepend! end

function prepend!(a::Array{<:Any,1}, items::AbstractVector)
itemindices = eachindex(items)
n = _length(itemindices)
n = length(itemindices)
_growbeg!(a, n)
if a === items
copyto!(a, 1, items, n+1, n)
Expand All @@ -923,6 +926,7 @@ prepend!(a::Vector, iter) = _prepend!(a, IteratorSize(iter), iter)
pushfirst!(a::Vector, iter...) = prepend!(a, iter)

function _prepend!(a, ::Union{HasLength,HasShape}, iter)
@assert !has_offset_axes(a)
n = length(iter)
_growbeg!(a, n)
i = 0
Expand Down
16 changes: 8 additions & 8 deletions base/arrayshow.jl
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ function alignment(io::IO, X::AbstractVecOrMat,
break
end
end
if 1 < length(a) < _length(axes(X,2))
if 1 < length(a) < length(axes(X,2))
while sum(map(sum,a)) + sep*length(a) >= cols_otherwise
pop!(a)
end
Expand Down Expand Up @@ -168,7 +168,7 @@ function print_matrix(io::IO, X::AbstractVecOrMat,
@assert textwidth(hdots) == textwidth(ddots)
sepsize = length(sep)
rowsA, colsA = axes(X,1), axes(X,2)
m, n = _length(rowsA), _length(colsA)
m, n = length(rowsA), length(colsA)
# To figure out alignments, only need to look at as many rows as could
# fit down screen. If screen has at least as many rows as A, look at A.
# If not, then we only need to look at the first and last chunks of A,
Expand Down Expand Up @@ -266,10 +266,10 @@ function show_nd(io::IO, a::AbstractArray, print_matrix::Function, label_slices:
for i = 1:nd
ii = idxs[i]
ind = tailinds[i]
if _length(ind) > 10
if length(ind) > 10
if ii == ind[firstindex(ind)+3] && all(d->idxs[d]==first(tailinds[d]),1:i-1)
for j=i+1:nd
szj = _length(axes(a, j+2))
szj = length(axes(a, j+2))
indj = tailinds[j]
if szj>10 && first(indj)+2 < idxs[j] <= last(indj)-3
@goto skip
Expand Down Expand Up @@ -313,7 +313,7 @@ print_array(io::IO, X::AbstractArray) = show_nd(io, X, print_matrix, true)
# implements: show(io::IO, ::MIME"text/plain", X::AbstractArray)
function show(io::IO, ::MIME"text/plain", X::AbstractArray)
# 0) compute new IOContext
if !haskey(io, :compact) && _length(axes(X, 2)) > 1
if !haskey(io, :compact) && length(axes(X, 2)) > 1
io = IOContext(io, :compact => true)
end
if get(io, :limit, false) && eltype(X) === Method
Expand Down Expand Up @@ -359,7 +359,7 @@ function _show_nonempty(io::IO, X::AbstractMatrix, prefix::String)
@assert !isempty(X)
limit = get(io, :limit, false)::Bool
indr, indc = axes(X,1), axes(X,2)
nr, nc = _length(indr), _length(indc)
nr, nc = length(indr), length(indc)
rdots, cdots = false, false
rr1, rr2 = UnitRange{Int}(indr), 1:0
cr1, cr2 = UnitRange{Int}(indc), 1:0
Expand Down Expand Up @@ -432,8 +432,8 @@ function show_vector(io::IO, v, opn='[', cls=']')
# directly or indirectly, the context now knows about eltype(v)
io = IOContext(io, :typeinfo => eltype(v), :compact => get(io, :compact, true))
limited = get(io, :limit, false)
if limited && _length(v) > 20
inds = indices1(v)
if limited && length(v) > 20
inds = axes1(v)
show_delim_array(io, v, opn, ",", "", false, inds[1], inds[1]+9)
print(io, "")
show_delim_array(io, v, "", ",", cls, false, inds[end-9], inds[end])
Expand Down
2 changes: 1 addition & 1 deletion base/broadcast.jl
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,7 @@ Base.@propagate_inbounds _newindex(ax::Tuple{}, I::Tuple{}) = ()
@inline function _newindexer(indsA::Tuple)
ind1 = indsA[1]
keep, Idefault = _newindexer(tail(indsA))
(Base._length(ind1)!=1, keep...), (first(ind1), Idefault...)
(Base.length(ind1)!=1, keep...), (first(ind1), Idefault...)
end

@inline function Base.getindex(bc::Broadcasted, I::Union{Integer,CartesianIndex})
Expand Down
2 changes: 2 additions & 0 deletions base/c.jl
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ transcode(T, src::String) = transcode(T, codeunits(src))
transcode(::Type{String}, src) = String(transcode(UInt8, src))

function transcode(::Type{UInt16}, src::AbstractVector{UInt8})
@assert !has_offset_axes(src)
dst = UInt16[]
i, n = 1, length(src)
n > 0 || return dst
Expand Down Expand Up @@ -317,6 +318,7 @@ function transcode(::Type{UInt16}, src::AbstractVector{UInt8})
end

function transcode(::Type{UInt8}, src::AbstractVector{UInt16})
@assert !has_offset_axes(src)
n = length(src)
n == 0 && return UInt8[]

Expand Down
3 changes: 3 additions & 0 deletions base/combinatorics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ isperm(p::Tuple{Int}) = p[1] == 1
isperm(p::Tuple{Int,Int}) = ((p[1] == 1) & (p[2] == 2)) | ((p[1] == 2) & (p[2] == 1))

function permute!!(a, p::AbstractVector{<:Integer})
@assert !has_offset_axes(a, p)
count = 0
start = 0
while count < length(a)
Expand Down Expand Up @@ -114,6 +115,7 @@ julia> A
permute!(a, p::AbstractVector) = permute!!(a, copymutable(p))

function invpermute!!(a, p::AbstractVector{<:Integer})
@assert !has_offset_axes(a, p)
count = 0
start = 0
while count < length(a)
Expand Down Expand Up @@ -194,6 +196,7 @@ julia> B[invperm(v)]
```
"""
function invperm(a::AbstractVector)
@assert !has_offset_axes(a)
b = zero(a) # similar vector of zeros
n = length(a)
@inbounds for (i, j) in enumerate(a)
Expand Down
4 changes: 4 additions & 0 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1747,6 +1747,7 @@ end
@deprecate mapfoldl(f, op, v0, itr) mapfoldl(f, op, itr; init=v0)
@deprecate mapfoldr(f, op, v0, itr) mapfoldr(f, op, itr; init=v0)


@deprecate startswith(a::Vector{UInt8}, b::Vector{UInt8}) length(a) >= length(b) && view(a, 1:length(b)) == b

# PR #27859
Expand All @@ -1759,6 +1760,9 @@ function print(io::IO, ::Nothing)
show(io, nothing)
end

@deprecate indices1 axes1
@deprecate _length length

# END 0.7 deprecations

# BEGIN 1.0 deprecations
Expand Down
20 changes: 9 additions & 11 deletions base/indices.jl
Original file line number Diff line number Diff line change
Expand Up @@ -179,16 +179,16 @@ function setindex_shape_check(X::AbstractArray, I::Integer...)
end

setindex_shape_check(X::AbstractArray) =
(_length(X)==1 || throw_setindex_mismatch(X,()))
(length(X)==1 || throw_setindex_mismatch(X,()))

setindex_shape_check(X::AbstractArray, i::Integer) =
(_length(X)==i || throw_setindex_mismatch(X, (i,)))
(length(X)==i || throw_setindex_mismatch(X, (i,)))

setindex_shape_check(X::AbstractArray{<:Any,1}, i::Integer) =
(_length(X)==i || throw_setindex_mismatch(X, (i,)))
(length(X)==i || throw_setindex_mismatch(X, (i,)))

setindex_shape_check(X::AbstractArray{<:Any,1}, i::Integer, j::Integer) =
(_length(X)==i*j || throw_setindex_mismatch(X, (i,j)))
(length(X)==i*j || throw_setindex_mismatch(X, (i,j)))

function setindex_shape_check(X::AbstractArray{<:Any,2}, i::Integer, j::Integer)
if length(X) != i*j
Expand Down Expand Up @@ -282,17 +282,15 @@ end
Slice(S::Slice) = S
axes(S::Slice) = (S,)
unsafe_indices(S::Slice) = (S,)
indices1(S::Slice) = S
axes1(S::Slice) = S
axes(S::Slice{<:OneTo}) = (S.indices,)
unsafe_indices(S::Slice{<:OneTo}) = (S.indices,)
indices1(S::Slice{<:OneTo}) = S.indices
axes1(S::Slice{<:OneTo}) = S.indices

first(S::Slice) = first(S.indices)
last(S::Slice) = last(S.indices)
errmsg(A) = error("size not supported for arrays with indices $(axes(A)); see https://docs.julialang.org/en/latest/devdocs/offset-arrays/")
size(S::Slice) = first(S.indices) == 1 ? (length(S.indices),) : errmsg(S)
length(S::Slice) = first(S.indices) == 1 ? length(S.indices) : errmsg(S)
_length(S::Slice) = length(S.indices)
size(S::Slice) = (length(S.indices),)
length(S::Slice) = length(S.indices)
unsafe_length(S::Slice) = unsafe_length(S.indices)
getindex(S::Slice, i::Int) = (@_inline_meta; @boundscheck checkbounds(S, i); i)
getindex(S::Slice, i::AbstractUnitRange{<:Integer}) = (@_inline_meta; @boundscheck checkbounds(S, i); i)
Expand Down Expand Up @@ -361,7 +359,7 @@ LinearIndices(A::Union{AbstractArray,SimpleVector}) = LinearIndices(axes(A))

# AbstractArray implementation
IndexStyle(::Type{<:LinearIndices}) = IndexLinear()
axes(iter::LinearIndices) = map(indices1, iter.indices)
axes(iter::LinearIndices) = map(axes1, iter.indices)
size(iter::LinearIndices) = map(unsafe_length, iter.indices)
function getindex(iter::LinearIndices, i::Int)
@_inline_meta
Expand Down
Loading

0 comments on commit f104ea4

Please sign in to comment.