Skip to content

Commit c1f41ad

Browse files
improve inferrability of tuple slicing (#39074)
* RFC: improve inferrability of tuple slicing This makes slicing tuples type stable in a lot more cases. This now only calls `ntuple` if the resulting tuple has no more than 10 elements, as just relying on the fallback in `ntuple` did have some compile-time overhead in my artificial benchmarks. I also widened the signature to `AbstractUnitRange`, since I don't see why we shouldn't have this for ranges like `OneTo` as well. * add check for offset ranges and more tests * add guard if OffsetArrays is already imported Co-authored-by: Matt Bauman <mbauman@juliacomputing.com> * import OffsetArrays correctly Co-authored-by: Matt Bauman <mbauman@juliacomputing.com>
1 parent eb352b7 commit c1f41ad

File tree

2 files changed

+35
-9
lines changed

2 files changed

+35
-9
lines changed

base/range.jl

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -299,16 +299,18 @@ unitrange(x) = UnitRange(x)
299299

300300
if isdefined(Main, :Base)
301301
# Constant-fold-able indexing into tuples to functionally expose Base.tail and Base.front
302-
function getindex(@nospecialize(t::Tuple), r::UnitRange)
302+
function getindex(@nospecialize(t::Tuple), r::AbstractUnitRange)
303303
@_inline_meta
304-
r.start > r.stop && return ()
305-
if r.start == 1
306-
r.stop == length(t) && return t
307-
r.stop == length(t)-1 && return front(t)
308-
r.stop == length(t)-2 && return front(front(t))
309-
elseif r.stop == length(t)
310-
r.start == 2 && return tail(t)
311-
r.start == 3 && return tail(tail(t))
304+
require_one_based_indexing(r)
305+
if length(r) <= 10
306+
return ntuple(i -> t[i + first(r) - 1], length(r))
307+
elseif first(r) == 1
308+
last(r) == length(t) && return t
309+
last(r) == length(t)-1 && return front(t)
310+
last(r) == length(t)-2 && return front(front(t))
311+
elseif last(r) == length(t)
312+
first(r) == 2 && return tail(t)
313+
first(r) == 3 && return tail(tail(t))
312314
end
313315
return (eltype(t)[t[ri] for ri in r]...,)
314316
end

test/tuple.jl

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# This file is a part of Julia. License is MIT: https://julialang.org/license
22

3+
isdefined(Main, :OffsetArrays) || @eval Main include("testhelpers/OffsetArrays.jl")
4+
using .Main.OffsetArrays
5+
36
struct BitPerm_19352
47
p::NTuple{8,UInt8}
58
function BitPerm(p::NTuple{8,UInt8})
@@ -598,3 +601,24 @@ end
598601
# issue #38837
599602
f38837(xs) = map((F,x)->F(x), (Float32, Float64), xs)
600603
@test @inferred(f38837((1,2))) === (1.0f0, 2.0)
604+
605+
@testset "indexing with UnitRanges" begin
606+
f(t) = t[3:end-2]
607+
@test @inferred(f(Tuple(1:10))) === Tuple(3:8)
608+
@test @inferred(f((true, 2., 3, 4f0, 0x05, 6, 7.))) === (3, 4f0, 0x05)
609+
610+
f(t) = t[Base.OneTo(5)]
611+
@test @inferred(f(Tuple(1:10))) === Tuple(1:5)
612+
@test @inferred(f((true, 2., 3, 4f0, 0x05, 6, 7.))) === (true, 2., 3, 4f0, 0x05)
613+
614+
@test @inferred((t -> t[1:end])(Tuple(1:15))) === Tuple(1:15)
615+
@test @inferred((t -> t[2:end])(Tuple(1:15))) === Tuple(2:15)
616+
@test @inferred((t -> t[3:end])(Tuple(1:15))) === Tuple(3:15)
617+
@test @inferred((t -> t[1:end-1])(Tuple(1:15))) === Tuple(1:14)
618+
@test @inferred((t -> t[1:end-2])(Tuple(1:15))) === Tuple(1:13)
619+
@test @inferred((t -> t[3:2])(Tuple(1:15))) === ()
620+
621+
@test_throws BoundsError (1, 2)[1:4]
622+
@test_throws BoundsError (1, 2)[0:2]
623+
@test_throws ArgumentError (1, 2)[OffsetArrays.IdOffsetRange(1:2, -1)]
624+
end

0 commit comments

Comments
 (0)