Skip to content

Commit b1bb0a6

Browse files
N5N3vtjnash
andcommitted
Fix for length(::StepRange{T}) where T isa Union
And define `firstindex` accordingly. Co-Authored-By: Jameson Nash <vtjnash+github@gmail.com>
1 parent dde6f0b commit b1bb0a6

File tree

2 files changed

+31
-25
lines changed

2 files changed

+31
-25
lines changed

base/range.jl

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -691,10 +691,6 @@ axes(r::AbstractRange) = (oneto(length(r)),)
691691

692692
# Needed to ensure `has_offset_axes` can constant-fold.
693693
has_offset_axes(::StepRange) = false
694-
let baseints = Union{Int8,UInt8,Int16,UInt16,Int32,UInt32,Int64,UInt64,Int128,UInt128}
695-
global firstindex
696-
firstindex(::StepRange{T,<:baseints}) where {T<:baseints} = sizeof(T) < sizeof(Int) ? 1 : one(T)
697-
end
698694

699695
# n.b. checked_length for these is defined iff checked_add and checked_sub are
700696
# defined between the relevant types
@@ -757,64 +753,66 @@ length(r::OneTo) = Integer(r.stop - zero(r.stop))
757753
length(r::StepRangeLen) = r.len
758754
length(r::LinRange) = r.len
759755

760-
let bigints = Union{Int, UInt, Int64, UInt64, Int128, UInt128}
761-
global length, checked_length
756+
let bigints = Union{Int, UInt, Int64, UInt64, Int128, UInt128},
757+
smallints = (Int === Int64 ?
758+
Union{Int8, UInt8, Int16, UInt16, Int32, UInt32} :
759+
Union{Int8, UInt8, Int16, UInt16}),
760+
bitints = Union{bigints, smallints}
761+
global length, checked_length, firstindex
762762
# compile optimization for which promote_type(T, Int) == T
763763
length(r::OneTo{T}) where {T<:bigints} = r.stop
764764
# slightly more accurate length and checked_length in extreme cases
765765
# (near typemax) for types with known `unsigned` functions
766766
function length(r::OrdinalRange{T}) where T<:bigints
767767
s = step(r)
768-
isempty(r) && return zero(T)
769768
diff = last(r) - first(r)
769+
isempty(r) && return zero(diff)
770770
# if |s| > 1, diff might have overflowed, but unsigned(diff)÷s should
771771
# therefore still be valid (if the result is representable at all)
772772
# n.b. !(s isa T)
773773
if s isa Unsigned || -1 <= s <= 1 || s == -s
774-
a = div(diff, s) % T
774+
a = div(diff, s) % typeof(diff)
775775
elseif s < 0
776-
a = div(unsigned(-diff), -s) % T
776+
a = div(unsigned(-diff), -s) % typeof(diff)
777777
else
778-
a = div(unsigned(diff), s) % T
778+
a = div(unsigned(diff), s) % typeof(diff)
779779
end
780-
return a + oneunit(T)
780+
return a + oneunit(a)
781781
end
782782
function checked_length(r::OrdinalRange{T}) where T<:bigints
783783
s = step(r)
784-
isempty(r) && return zero(T)
785784
stop, start = last(r), first(r)
785+
ET = promote_type(typeof(stop), typeof(start))
786+
isempty(r) && return zero(ET)
786787
# n.b. !(s isa T)
787788
if s > 1
788789
diff = stop - start
789-
a = convert(T, div(unsigned(diff), s))
790+
a = convert(ET, div(unsigned(diff), s))
790791
elseif s < -1
791792
diff = start - stop
792-
a = convert(T, div(unsigned(diff), -s))
793+
a = convert(ET, div(unsigned(diff), -s))
793794
elseif s > 0
794-
a = div(checked_sub(stop, start), s)
795+
a = convert(ET, div(checked_sub(stop, start), s))
795796
else
796-
a = div(checked_sub(start, stop), -s)
797+
a = convert(ET, div(checked_sub(start, stop), -s))
797798
end
798-
return checked_add(convert(T, a), oneunit(T))
799+
return checked_add(a, oneunit(a))
799800
end
800-
end
801+
firstindex(r::StepRange{<:bigints,<:bitints}) = one(last(r)-first(r))
801802

802-
# some special cases to favor default Int type
803-
let smallints = (Int === Int64 ?
804-
Union{Int8, UInt8, Int16, UInt16, Int32, UInt32} :
805-
Union{Int8, UInt8, Int16, UInt16})
806-
global length, checked_length
807-
# n.b. !(step isa T)
803+
# some special cases to favor default Int type
808804
function length(r::OrdinalRange{<:smallints})
809805
s = step(r)
810806
isempty(r) && return 0
807+
# n.b. !(step isa T)
811808
return Int(div(Int(last(r)) - Int(first(r)), s)) + 1
812809
end
813810
length(r::AbstractUnitRange{<:smallints}) = Int(last(r)) - Int(first(r)) + 1
814811
length(r::OneTo{<:smallints}) = Int(r.stop)
815812
checked_length(r::OrdinalRange{<:smallints}) = length(r)
816813
checked_length(r::AbstractUnitRange{<:smallints}) = length(r)
817814
checked_length(r::OneTo{<:smallints}) = length(r)
815+
firstindex(::StepRange{<:smallints,<:bitints}) = 1
818816
end
819817

820818
first(r::OrdinalRange{T}) where {T} = convert(T, r.start)

test/ranges.jl

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2036,6 +2036,12 @@ end
20362036
@test typeof(length(r1)) == typeof(checked_length(r1)) ==
20372037
typeof(length(r2)) == typeof(checked_length(r2))
20382038
end
2039+
SR = StepRange{Union{Int64,Int128},Int}
2040+
test_length(r, l) = length(r) === checked_length(r) === l
2041+
@test test_length(SR(Int64(1), 1, Int128(1)), Int128(1))
2042+
@test test_length(SR(Int64(1), 1, Int128(0)), Int128(0))
2043+
@test test_length(SR(Int64(1), 1, Int64(1)), Int64(1))
2044+
@test test_length(SR(Int64(1), 1, Int64(0)), Int64(0))
20392045
end
20402046

20412047
@testset "LinRange eltype for element types that wrap integers" begin
@@ -2350,10 +2356,12 @@ end
23502356

23512357
@test length(range(1, length=typemax(Int128))) === typemax(Int128)
23522358

2353-
@testset "firstindex(::StepRange{T,T})" begin
2359+
@testset "firstindex(::StepRange{<:Base.BitInteger})" begin
23542360
test_firstindex(x) = firstindex(x) === first(Base.axes1(x))
23552361
for T in Base.BitInteger_types, S in Base.BitInteger_types
23562362
@test test_firstindex(StepRange{T,S}(1, 1, 1))
23572363
@test test_firstindex(StepRange{T,S}(1, 1, 0))
23582364
end
2365+
@test test_firstindex(StepRange{Union{Int64,Int128},Int}(Int64(1), 1, Int128(1)))
2366+
@test test_firstindex(StepRange{Union{Int64,Int128},Int}(Int64(1), 1, Int128(0)))
23592367
end

0 commit comments

Comments
 (0)