Skip to content

Commit ab36599

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 f867f50 commit ab36599

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
@@ -689,10 +689,6 @@ axes(r::AbstractRange) = (oneto(length(r)),)
689689

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

697693
# n.b. checked_length for these is defined iff checked_add and checked_sub are
698694
# defined between the relevant types
@@ -755,64 +751,66 @@ length(r::OneTo) = Integer(r.stop - zero(r.stop))
755751
length(r::StepRangeLen) = r.len
756752
length(r::LinRange) = r.len
757753

758-
let bigints = Union{Int, UInt, Int64, UInt64, Int128, UInt128}
759-
global length, checked_length
754+
let bigints = Union{Int, UInt, Int64, UInt64, Int128, UInt128},
755+
smallints = (Int === Int64 ?
756+
Union{Int8, UInt8, Int16, UInt16, Int32, UInt32} :
757+
Union{Int8, UInt8, Int16, UInt16}),
758+
bitints = Union{bigints, smallints}
759+
global length, checked_length, firstindex
760760
# compile optimization for which promote_type(T, Int) == T
761761
length(r::OneTo{T}) where {T<:bigints} = r.stop
762762
# slightly more accurate length and checked_length in extreme cases
763763
# (near typemax) for types with known `unsigned` functions
764764
function length(r::OrdinalRange{T}) where T<:bigints
765765
s = step(r)
766-
isempty(r) && return zero(T)
767766
diff = last(r) - first(r)
767+
isempty(r) && return zero(diff)
768768
# if |s| > 1, diff might have overflowed, but unsigned(diff)÷s should
769769
# therefore still be valid (if the result is representable at all)
770770
# n.b. !(s isa T)
771771
if s isa Unsigned || -1 <= s <= 1 || s == -s
772-
a = div(diff, s) % T
772+
a = div(diff, s) % typeof(diff)
773773
elseif s < 0
774-
a = div(unsigned(-diff), -s) % T
774+
a = div(unsigned(-diff), -s) % typeof(diff)
775775
else
776-
a = div(unsigned(diff), s) % T
776+
a = div(unsigned(diff), s) % typeof(diff)
777777
end
778-
return a + oneunit(T)
778+
return a + oneunit(a)
779779
end
780780
function checked_length(r::OrdinalRange{T}) where T<:bigints
781781
s = step(r)
782-
isempty(r) && return zero(T)
783782
stop, start = last(r), first(r)
783+
ET = promote_type(typeof(stop), typeof(start))
784+
isempty(r) && return zero(ET)
784785
# n.b. !(s isa T)
785786
if s > 1
786787
diff = stop - start
787-
a = convert(T, div(unsigned(diff), s))
788+
a = convert(ET, div(unsigned(diff), s))
788789
elseif s < -1
789790
diff = start - stop
790-
a = convert(T, div(unsigned(diff), -s))
791+
a = convert(ET, div(unsigned(diff), -s))
791792
elseif s > 0
792-
a = div(checked_sub(stop, start), s)
793+
a = convert(ET, div(checked_sub(stop, start), s))
793794
else
794-
a = div(checked_sub(start, stop), -s)
795+
a = convert(ET, div(checked_sub(start, stop), -s))
795796
end
796-
return checked_add(convert(T, a), oneunit(T))
797+
return checked_add(a, oneunit(a))
797798
end
798-
end
799+
firstindex(r::StepRange{<:bigints,<:bitints}) = one(last(r)-first(r))
799800

800-
# some special cases to favor default Int type
801-
let smallints = (Int === Int64 ?
802-
Union{Int8, UInt8, Int16, UInt16, Int32, UInt32} :
803-
Union{Int8, UInt8, Int16, UInt16})
804-
global length, checked_length
805-
# n.b. !(step isa T)
801+
# some special cases to favor default Int type
806802
function length(r::OrdinalRange{<:smallints})
807803
s = step(r)
808804
isempty(r) && return 0
805+
# n.b. !(step isa T)
809806
return Int(div(Int(last(r)) - Int(first(r)), s)) + 1
810807
end
811808
length(r::AbstractUnitRange{<:smallints}) = Int(last(r)) - Int(first(r)) + 1
812809
length(r::OneTo{<:smallints}) = Int(r.stop)
813810
checked_length(r::OrdinalRange{<:smallints}) = length(r)
814811
checked_length(r::AbstractUnitRange{<:smallints}) = length(r)
815812
checked_length(r::OneTo{<:smallints}) = length(r)
813+
firstindex(::StepRange{<:smallints,<:bitints}) = 1
816814
end
817815

818816
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
@@ -2037,6 +2037,12 @@ end
20372037
@test typeof(length(r1)) == typeof(checked_length(r1)) ==
20382038
typeof(length(r2)) == typeof(checked_length(r2))
20392039
end
2040+
SR = StepRange{Union{Int64,Int128},Int}
2041+
test_length(r, l) = length(r) === checked_length(r) === l
2042+
@test test_length(SR(Int64(1), 1, Int128(1)), Int128(1))
2043+
@test test_length(SR(Int64(1), 1, Int128(0)), Int128(0))
2044+
@test test_length(SR(Int64(1), 1, Int64(1)), Int64(1))
2045+
@test test_length(SR(Int64(1), 1, Int64(0)), Int64(0))
20402046
end
20412047

20422048
@testset "LinRange eltype for element types that wrap integers" begin
@@ -2351,10 +2357,12 @@ end
23512357

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

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

0 commit comments

Comments
 (0)