Skip to content

Commit 413fc47

Browse files
authored
Detangle Slice and fix mixed-dimension in-place reductions (#28941)
* Detangle Slice between whole dimensions and axes We use axes in many downstream computations that may not re-index directly into the original array, so we add a second type parameter to `Slice` that is turned on when converting `:` in indexing expressions -- and really only `SubArray` cares about. * Reduce allocations for in-place reductions and fix mixed-dimensionality edge-cases Alleviates #28928 but does not completely remove allocations due to the allocation of the view that gets passed to `map!`. * Introduce a whole new IdentityUnitRange that we will encourage offset array implementations to use instead of Base.Slice * Test that maximum! allocates less
1 parent ecd7291 commit 413fc47

File tree

12 files changed

+115
-41
lines changed

12 files changed

+115
-41
lines changed

base/indices.jl

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,8 @@ _maybetail(t::Tuple) = tail(t)
303303
"""
304304
Slice(indices)
305305
306-
Represent an AbstractUnitRange of indices as a vector of the indices themselves.
306+
Represent an AbstractUnitRange of indices as a vector of the indices themselves,
307+
with special handling to signal they represent a complete slice of a dimension (:).
307308
308309
Upon calling `to_indices`, Colons are converted to Slice objects to represent
309310
the indices over which the Colon spans. Slice objects are themselves unit
@@ -315,9 +316,9 @@ struct Slice{T<:AbstractUnitRange} <: AbstractUnitRange{Int}
315316
indices::T
316317
end
317318
Slice(S::Slice) = S
318-
axes(S::Slice) = (S,)
319-
unsafe_indices(S::Slice) = (S,)
320-
axes1(S::Slice) = S
319+
axes(S::Slice) = (IdentityUnitRange(S.indices),)
320+
unsafe_indices(S::Slice) = (IdentityUnitRange(S.indices),)
321+
axes1(S::Slice) = IdentityUnitRange(S.indices)
321322
axes(S::Slice{<:OneTo}) = (S.indices,)
322323
unsafe_indices(S::Slice{<:OneTo}) = (S.indices,)
323324
axes1(S::Slice{<:OneTo}) = S.indices
@@ -333,6 +334,38 @@ getindex(S::Slice, i::StepRange{<:Integer}) = (@_inline_meta; @boundscheck check
333334
show(io::IO, r::Slice) = print(io, "Base.Slice(", r.indices, ")")
334335
iterate(S::Slice, s...) = iterate(S.indices, s...)
335336

337+
338+
"""
339+
IdentityUnitRange(range::AbstractUnitRange)
340+
341+
Represent an AbstractUnitRange `range` as an offset vector such that `range[i] == i`.
342+
343+
`IdentityUnitRange`s are frequently used as axes for offset arrays.
344+
"""
345+
struct IdentityUnitRange{T<:AbstractUnitRange} <: AbstractUnitRange{Int}
346+
indices::T
347+
end
348+
IdentityUnitRange(S::IdentityUnitRange) = S
349+
# IdentityUnitRanges are offset and thus have offset axes, so they are their own axes... but
350+
# we need to strip the wholedim marker because we don't know how they'll be used
351+
axes(S::IdentityUnitRange) = (S,)
352+
unsafe_indices(S::IdentityUnitRange) = (S,)
353+
axes1(S::IdentityUnitRange) = S
354+
axes(S::IdentityUnitRange{<:OneTo}) = (S.indices,)
355+
unsafe_indices(S::IdentityUnitRange{<:OneTo}) = (S.indices,)
356+
axes1(S::IdentityUnitRange{<:OneTo}) = S.indices
357+
358+
first(S::IdentityUnitRange) = first(S.indices)
359+
last(S::IdentityUnitRange) = last(S.indices)
360+
size(S::IdentityUnitRange) = (length(S.indices),)
361+
length(S::IdentityUnitRange) = length(S.indices)
362+
unsafe_length(S::IdentityUnitRange) = unsafe_length(S.indices)
363+
getindex(S::IdentityUnitRange, i::Int) = (@_inline_meta; @boundscheck checkbounds(S, i); i)
364+
getindex(S::IdentityUnitRange, i::AbstractUnitRange{<:Integer}) = (@_inline_meta; @boundscheck checkbounds(S, i); i)
365+
getindex(S::IdentityUnitRange, i::StepRange{<:Integer}) = (@_inline_meta; @boundscheck checkbounds(S, i); i)
366+
show(io::IO, r::IdentityUnitRange) = print(io, "Base.IdentityUnitRange(", r.indices, ")")
367+
iterate(S::IdentityUnitRange, s...) = iterate(S.indices, s...)
368+
336369
"""
337370
LinearIndices(A::AbstractArray)
338371

base/multidimensional.jl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,6 @@ index_dimsum() = ()
472472
index_lengths() = ()
473473
@inline index_lengths(::Real, rest...) = (1, index_lengths(rest...)...)
474474
@inline index_lengths(A::AbstractArray, rest...) = (length(A), index_lengths(rest...)...)
475-
@inline index_lengths(A::Slice, rest...) = (length(axes1(A)), index_lengths(rest...)...)
476475

477476
# shape of array to create for getindex() with indices I, dropping scalars
478477
# returns a Tuple{Vararg{AbstractUnitRange}} of indices

base/reducedim.jl

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
# for reductions that expand 0 dims to 1
66
reduced_index(i::OneTo) = OneTo(1)
7-
reduced_index(i::Slice) = first(i):first(i)
7+
reduced_index(i::Union{Slice, IdentityUnitRange}) = first(i):first(i)
88
reduced_index(i::AbstractUnitRange) =
99
throw(ArgumentError(
1010
"""
@@ -218,17 +218,20 @@ Extract first entry of slices of array A into existing array R.
218218
"""
219219
copyfirst!(R::AbstractArray, A::AbstractArray) = mapfirst!(identity, R, A)
220220

221-
function mapfirst!(f, R::AbstractArray, A::AbstractArray)
221+
function mapfirst!(f, R::AbstractArray, A::AbstractArray{<:Any,N}) where {N}
222222
lsiz = check_reducedims(R, A)
223-
iA = axes(A)
224-
iR = axes(R)
225-
t = []
226-
for i in 1:length(iR)
227-
iAi = iA[i]
228-
push!(t, iAi == iR[i] ? iAi : first(iAi))
229-
end
223+
t = _firstreducedslice(axes(R), axes(A))
230224
map!(f, R, view(A, t...))
231225
end
226+
# We know that the axes of R and A are compatible, but R might have a different number of
227+
# dimensions than A, which is trickier than it seems due to offset arrays and type stability
228+
_firstreducedslice(::Tuple{}, a::Tuple{}) = ()
229+
_firstreducedslice(::Tuple, ::Tuple{}) = ()
230+
@inline _firstreducedslice(::Tuple{}, a::Tuple) = (_firstslice(a[1]), _firstreducedslice((), tail(a))...)
231+
@inline _firstreducedslice(r::Tuple, a::Tuple) = (length(r[1])==1 ? _firstslice(a[1]) : r[1], _firstreducedslice(tail(r), tail(a))...)
232+
_firstslice(i::OneTo) = OneTo(1)
233+
_firstslice(i::Slice) = Slice(_firstslice(i.indices))
234+
_firstslice(i) = i[firstindex(i):firstindex(i)]
232235

233236
function _mapreducedim!(f, op, R::AbstractArray, A::AbstractArray)
234237
lsiz = check_reducedims(R,A)

base/show.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1831,7 +1831,7 @@ dims2string(d) = isempty(d) ? "0-dimensional" :
18311831

18321832
inds2string(inds) = join(map(_indsstring,inds), '×')
18331833
_indsstring(i) = string(i)
1834-
_indsstring(i::Slice) = string(i.indices)
1834+
_indsstring(i::Union{IdentityUnitRange, Slice}) = string(i.indices)
18351835

18361836
# anything array-like gets summarized e.g. 10-element Array{Int64,1}
18371837
summary(io::IO, a::AbstractArray) = summary(io, a, axes(a))
@@ -1906,7 +1906,7 @@ function showarg(io::IO, v::SubArray, toplevel)
19061906
print(io, ')')
19071907
toplevel && print(io, " with eltype ", eltype(v))
19081908
end
1909-
showindices(io, ::Slice, inds...) =
1909+
showindices(io, ::Union{Slice,IdentityUnitRange}, inds...) =
19101910
(print(io, ", :"); showindices(io, inds...))
19111911
showindices(io, ind1, inds...) =
19121912
(print(io, ", ", ind1); showindices(io, inds...))

base/sort.jl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import .Base:
1919
sort!,
2020
issorted,
2121
sortperm,
22-
Slice,
2322
to_indices
2423

2524
export # also exported by Base

base/subarray.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ compute_offset1(parent::AbstractVector, stride1::Integer, I::Tuple{AbstractRange
316316
# linear indexing always starts with 1.
317317
compute_offset1(parent, stride1::Integer, I::Tuple) =
318318
(@_inline_meta; compute_offset1(parent, stride1, find_extended_dims(1, I...), find_extended_inds(I...), I))
319-
compute_offset1(parent, stride1::Integer, dims::Tuple{Int}, inds::Tuple{Slice}, I::Tuple) =
319+
compute_offset1(parent, stride1::Integer, dims::Tuple{Int}, inds::Tuple{Union{Slice, IdentityUnitRange}}, I::Tuple) =
320320
(@_inline_meta; compute_linindex(parent, I) - stride1*first(axes(parent, dims[1]))) # index-preserving case
321321
compute_offset1(parent, stride1::Integer, dims, inds, I::Tuple) =
322322
(@_inline_meta; compute_linindex(parent, I) - stride1) # linear indexing starts with 1
@@ -384,7 +384,7 @@ function parentdims(s::SubArray)
384384
j = 1
385385
for i = 1:ndims(s.parent)
386386
r = s.indices[i]
387-
if j <= nd && (isa(r,Union{Slice,AbstractRange}) ? sp[i]*step(r) : sp[i]) == sv[j]
387+
if j <= nd && (isa(r,AbstractRange) ? sp[i]*step(r) : sp[i]) == sv[j]
388388
dimindex[j] = i
389389
j += 1
390390
end

stdlib/LinearAlgebra/test/adjtrans.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,7 @@ using .Main.OffsetArrays
489489

490490
@testset "offset axes" begin
491491
s = Base.Slice(-3:3)'
492-
@test axes(s) === (Base.OneTo(1), Base.Slice(-3:3))
492+
@test axes(s) === (Base.OneTo(1), Base.IdentityUnitRange(-3:3))
493493
@test collect(LinearIndices(s)) == reshape(1:7, 1, 7)
494494
@test collect(CartesianIndices(s)) == reshape([CartesianIndex(1,i) for i = -3:3], 1, 7)
495495
@test s[1] == -3

test/arrayops.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1767,7 +1767,7 @@ end
17671767
@test CartesianIndices(CartesianIndex{3}(1,2,3)) == CartesianIndices((1, 2, 3))
17681768
@test Tuple{}(CartesianIndices{0,Tuple{}}(())) == ()
17691769

1770-
R = CartesianIndices(map(Base.Slice, (2:5, 3:5)))
1770+
R = CartesianIndices(map(Base.IdentityUnitRange, (2:5, 3:5)))
17711771
@test eltype(R) <: CartesianIndex{2}
17721772
@test eltype(typeof(R)) <: CartesianIndex{2}
17731773
@test eltype(CartesianIndices{2}) <: CartesianIndex{2}

test/offsetarray.jl

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ for i = 1:9 @test A_3_3[i] == i end
8181
@test_throws BoundsError A[CartesianIndex(1,1)]
8282
@test_throws BoundsError S[CartesianIndex(1,1)]
8383
@test eachindex(A) == 1:4
84-
@test eachindex(S) == CartesianIndices(axes(S)) == CartesianIndices(map(Base.Slice, (0:1,3:4)))
84+
@test eachindex(S) == CartesianIndices(axes(S)) == CartesianIndices(map(Base.IdentityUnitRange, (0:1,3:4)))
8585

8686
# LinearIndices
8787
# issue 27986
@@ -122,13 +122,13 @@ S = view(A, :, 3)
122122
@test S[0] == 1
123123
@test S[1] == 2
124124
@test_throws BoundsError S[2]
125-
@test axes(S) === (Base.Slice(0:1),)
125+
@test axes(S) === (Base.IdentityUnitRange(0:1),)
126126
S = view(A, 0, :)
127127
@test S == OffsetArray([1,3], (A.offsets[2],))
128128
@test S[3] == 1
129129
@test S[4] == 3
130130
@test_throws BoundsError S[1]
131-
@test axes(S) === (Base.Slice(3:4),)
131+
@test axes(S) === (Base.IdentityUnitRange(3:4),)
132132
S = view(A, 0:0, 4)
133133
@test S == [3]
134134
@test S[1] == 3
@@ -147,17 +147,17 @@ S = view(A, :, :)
147147
@test S[0,4] == S[3] == 3
148148
@test S[1,4] == S[4] == 4
149149
@test_throws BoundsError S[1,1]
150-
@test axes(S) === Base.Slice.((0:1, 3:4))
150+
@test axes(S) === Base.IdentityUnitRange.((0:1, 3:4))
151151
# https://github.com/JuliaArrays/OffsetArrays.jl/issues/27
152152
g = OffsetArray(Vector(-2:3), (-3,))
153153
gv = view(g, -1:2)
154154
@test axes(gv, 1) === Base.OneTo(4)
155155
@test collect(gv) == -1:2
156156
gv = view(g, OffsetArray(-1:2, (-2,)))
157-
@test axes(gv, 1) === Base.Slice(-1:2)
157+
@test axes(gv, 1) === Base.IdentityUnitRange(-1:2)
158158
@test collect(gv) == -1:2
159159
gv = view(g, OffsetArray(-1:2, (-1,)))
160-
@test axes(gv, 1) === Base.Slice(0:3)
160+
@test axes(gv, 1) === Base.IdentityUnitRange(0:3)
161161
@test collect(gv) == -1:2
162162

163163
# iteration
@@ -234,26 +234,26 @@ B = similar(A, (3,4))
234234
@test axes(B) === (Base.OneTo(3), Base.OneTo(4))
235235
B = similar(A, (-3:3,1:4))
236236
@test isa(B, OffsetArray{Int,2})
237-
@test axes(B) === Base.Slice.((-3:3, 1:4))
237+
@test axes(B) === Base.IdentityUnitRange.((-3:3, 1:4))
238238
B = similar(parent(A), (-3:3,1:4))
239239
@test isa(B, OffsetArray{Int,2})
240-
@test axes(B) === Base.Slice.((-3:3, 1:4))
240+
@test axes(B) === Base.IdentityUnitRange.((-3:3, 1:4))
241241

242242
# Indexing with OffsetArray indices
243243
i1 = OffsetArray([2,1], (-5,))
244244
i1 = OffsetArray([2,1], -5)
245245
b = A0[i1, 1]
246-
@test axes(b) === (Base.Slice(-4:-3),)
246+
@test axes(b) === (Base.IdentityUnitRange(-4:-3),)
247247
@test b[-4] == 2
248248
@test b[-3] == 1
249249
b = A0[1,i1]
250-
@test axes(b) === (Base.Slice(-4:-3),)
250+
@test axes(b) === (Base.IdentityUnitRange(-4:-3),)
251251
@test b[-4] == 3
252252
@test b[-3] == 1
253253
v = view(A0, i1, 1)
254-
@test axes(v) === (Base.Slice(-4:-3),)
254+
@test axes(v) === (Base.IdentityUnitRange(-4:-3),)
255255
v = view(A0, 1:1, i1)
256-
@test axes(v) === (Base.OneTo(1), Base.Slice(-4:-3))
256+
@test axes(v) === (Base.OneTo(1), Base.IdentityUnitRange(-4:-3))
257257

258258
# copyto! and fill!
259259
a = OffsetArray{Int}(undef, (-3:-1,))
@@ -340,7 +340,7 @@ a = OffsetArray(a0, (-1,2,3,4,5))
340340
v = OffsetArray(v0, (-3,))
341341
@test lastindex(v) == 1
342342
@test v v
343-
@test axes(v') === (Base.OneTo(1),Base.Slice(-2:1))
343+
@test axes(v') === (Base.OneTo(1),Base.IdentityUnitRange(-2:1))
344344
@test parent(v) == collect(v)
345345
rv = reverse(v)
346346
@test axes(rv) == axes(v)
@@ -356,7 +356,7 @@ A = OffsetArray(rand(4,4), (-3,5))
356356
@test lastindex(A, 1) == 1
357357
@test lastindex(A, 2) == 9
358358
@test A A
359-
@test axes(A') === Base.Slice.((6:9, -2:1))
359+
@test axes(A') === Base.IdentityUnitRange.((6:9, -2:1))
360360
@test parent(copy(A')) == copy(parent(A)')
361361
@test collect(A) == parent(A)
362362
@test maximum(A) == maximum(parent(A))
@@ -498,3 +498,25 @@ end
498498
A = OffsetArray(reshape(16:-1:1, (4, 4)), (-3,5))
499499
@test maximum(A, dims=1) == OffsetArray(maximum(parent(A), dims=1), A.offsets)
500500
end
501+
502+
@testset "in-place reductions with mismatched dimensionalities" begin
503+
B = OffsetArray(reshape(1:24, 4, 3, 2), -5, 6, -7)
504+
for R in (fill(0, -4:-1), fill(0, -4:-1, 7:7), fill(0, -4:-1, 7:7, -6:-6))
505+
@test @inferred(maximum!(R, B)) == reshape(maximum(B, dims=(2,3)), axes(R)) == reshape(21:24, axes(R))
506+
@test @allocated(maximum!(R, B)) <= 400
507+
@test @inferred(minimum!(R, B)) == reshape(minimum(B, dims=(2,3)), axes(R)) == reshape(1:4, axes(R))
508+
@test @allocated(minimum!(R, B)) <= 400
509+
end
510+
for R in (fill(0, -4:-4, 7:9), fill(0, -4:-4, 7:9, -6:-6))
511+
@test @inferred(maximum!(R, B)) == reshape(maximum(B, dims=(1,3)), axes(R)) == reshape(16:4:24, axes(R))
512+
@test @allocated(maximum!(R, B)) <= 400
513+
@test @inferred(minimum!(R, B)) == reshape(minimum(B, dims=(1,3)), axes(R)) == reshape(1:4:9, axes(R))
514+
@test @allocated(minimum!(R, B)) <= 400
515+
end
516+
@test_throws DimensionMismatch maximum!(fill(0, -4:-1, 7:7, -6:-6, 1:1), B)
517+
@test_throws DimensionMismatch minimum!(fill(0, -4:-1, 7:7, -6:-6, 1:1), B)
518+
@test_throws DimensionMismatch maximum!(fill(0, -4:-4, 7:9, -6:-6, 1:1), B)
519+
@test_throws DimensionMismatch minimum!(fill(0, -4:-4, 7:9, -6:-6, 1:1), B)
520+
@test_throws DimensionMismatch maximum!(fill(0, -4:-4, 7:7, -6:-5, 1:1), B)
521+
@test_throws DimensionMismatch minimum!(fill(0, -4:-4, 7:7, -6:-5, 1:1), B)
522+
end

test/reducedim.jl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,3 +380,21 @@ end
380380
@test B[argmax(B, dims=[2, 3])] == maximum(B, dims=[2, 3])
381381
@test B[argmin(B, dims=[2, 3])] == minimum(B, dims=[2, 3])
382382
end
383+
384+
@testset "in-place reductions with mismatched dimensionalities" begin
385+
B = reshape(1:24, 4, 3, 2)
386+
for R in (fill(0, 4), fill(0, 4, 1), fill(0, 4, 1, 1))
387+
@test @inferred(maximum!(R, B)) == reshape(21:24, size(R))
388+
@test @inferred(minimum!(R, B)) == reshape(1:4, size(R))
389+
end
390+
for R in (fill(0, 1, 3), fill(0, 1, 3, 1))
391+
@test @inferred(maximum!(R, B)) == reshape(16:4:24, size(R))
392+
@test @inferred(minimum!(R, B)) == reshape(1:4:9, size(R))
393+
end
394+
@test_throws DimensionMismatch maximum!(fill(0, 4, 1, 1, 1), B)
395+
@test_throws DimensionMismatch minimum!(fill(0, 4, 1, 1, 1), B)
396+
@test_throws DimensionMismatch maximum!(fill(0, 1, 3, 1, 1), B)
397+
@test_throws DimensionMismatch minimum!(fill(0, 1, 3, 1, 1), B)
398+
@test_throws DimensionMismatch maximum!(fill(0, 1, 1, 2, 1), B)
399+
@test_throws DimensionMismatch minimum!(fill(0, 1, 1, 2, 1), B)
400+
end

test/reinterpretarray.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ let a = [0.1 0.2; 0.3 0.4], at = reshape([(i,i+1) for i = 1:2:8], 2, 2)
136136
@test r[1,2] === reinterpret(Int64, v[1,2])
137137
@test r[0,3] === reinterpret(Int64, v[0,3])
138138
@test r[1,3] === reinterpret(Int64, v[1,3])
139-
@test_throws ArgumentError("cannot reinterpret a `Float64` array to `UInt32` when the first axis is Base.Slice(0:1). Try reshaping first.") reinterpret(UInt32, v)
139+
@test_throws ArgumentError("cannot reinterpret a `Float64` array to `UInt32` when the first axis is Base.IdentityUnitRange(0:1). Try reshaping first.") reinterpret(UInt32, v)
140140
v = OffsetArray(a, (0, 1))
141141
r = reinterpret(UInt32, v)
142142
axsv = axes(v)
@@ -155,7 +155,7 @@ let a = [0.1 0.2; 0.3 0.4], at = reshape([(i,i+1) for i = 1:2:8], 2, 2)
155155
offsetvt = (-2, 4)
156156
vt = OffsetArray(at, offsetvt)
157157
istr = string(Int)
158-
@test_throws ArgumentError("cannot reinterpret a `Tuple{$istr,$istr}` array to `$istr` when the first axis is Base.Slice(-1:0). Try reshaping first.") reinterpret(Int, vt)
158+
@test_throws ArgumentError("cannot reinterpret a `Tuple{$istr,$istr}` array to `$istr` when the first axis is Base.IdentityUnitRange(-1:0). Try reshaping first.") reinterpret(Int, vt)
159159
vt = reshape(vt, 1:1, axes(vt)...)
160160
r = reinterpret(Int, vt)
161161
@test r == OffsetArray(reshape(1:8, 2, 2, 2), (0, offsetvt...))

test/testhelpers/OffsetArrays.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,13 @@ Base.eachindex(::IndexLinear, A::OffsetVector) = axes(A, 1)
3939
# Implementations of indices and axes1. Since bounds-checking is
4040
# performance-critical and relies on indices, these are usually worth
4141
# optimizing thoroughly.
42-
@inline Base.axes(A::OffsetArray, d) = 1 <= d <= length(A.offsets) ? Base.Slice(axes(parent(A))[d] .+ A.offsets[d]) : Base.Slice(1:1)
42+
@inline Base.axes(A::OffsetArray, d) = 1 <= d <= length(A.offsets) ? Base.IdentityUnitRange(axes(parent(A))[d] .+ A.offsets[d]) : Base.IdentityUnitRange(1:1)
4343
@inline Base.axes(A::OffsetArray) = _indices(axes(parent(A)), A.offsets) # would rather use ntuple, but see #15276
44-
@inline _indices(inds, offsets) = (Base.Slice(inds[1] .+ offsets[1]), _indices(tail(inds), tail(offsets))...)
44+
@inline _indices(inds, offsets) = (Base.IdentityUnitRange(inds[1] .+ offsets[1]), _indices(tail(inds), tail(offsets))...)
4545
_indices(::Tuple{}, ::Tuple{}) = ()
46-
Base.axes1(A::OffsetArray{T,0}) where {T} = Base.Slice(1:1) # we only need to specialize this one
46+
Base.axes1(A::OffsetArray{T,0}) where {T} = Base.IdentityUnitRange(1:1) # we only need to specialize this one
4747

48-
const OffsetAxis = Union{Integer, UnitRange, Base.Slice{<:UnitRange}, Base.OneTo}
48+
const OffsetAxis = Union{Integer, UnitRange, Base.IdentityUnitRange{<:UnitRange}, Base.OneTo}
4949
function Base.similar(A::OffsetArray, T::Type, dims::Dims)
5050
B = similar(parent(A), T, dims)
5151
end

0 commit comments

Comments
 (0)