Skip to content

Commit f59082e

Browse files
committed
promote ranges to the largest of the the start, step, or length (as applicable)
Fixes #35711 Fixes #10554
1 parent 5e2894b commit f59082e

File tree

2 files changed

+100
-65
lines changed

2 files changed

+100
-65
lines changed

base/range.jl

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

3-
(:)(a::Real, b::Real) = (:)(promote(a,b)...)
3+
(:)(a::Real, b::Real) = (:)(promote(a, b)...)
44

55
(:)(start::T, stop::T) where {T<:Real} = UnitRange{T}(start, stop)
66

7-
(:)(start::T, stop::T) where {T} = (:)(start, oftype(stop-start, 1), stop)
7+
(:)(start::T, stop::T) where {T} = (:)(start, oneunit(stop >= start ? stop - start : start - stop), stop)
88

99
# promote start and stop, leaving step alone
10-
(:)(start::A, step, stop::C) where {A<:Real,C<:Real} =
11-
(:)(convert(promote_type(A,C),start), step, convert(promote_type(A,C),stop))
10+
(:)(start::A, step, stop::C) where {A<:Real, C<:Real} =
11+
(:)(convert(promote_type(A, C), start), step, convert(promote_type(A, C), stop))
1212

1313
# AbstractFloat specializations
14-
(:)(a::T, b::T) where {T<:AbstractFloat} = (:)(a, T(1), b)
14+
(:)(a::T, b::T) where {T<:AbstractFloat} = (:)(a, oneunit(T), b)
1515

16-
(:)(a::T, b::AbstractFloat, c::T) where {T<:Real} = (:)(promote(a,b,c)...)
17-
(:)(a::T, b::AbstractFloat, c::T) where {T<:AbstractFloat} = (:)(promote(a,b,c)...)
18-
(:)(a::T, b::Real, c::T) where {T<:AbstractFloat} = (:)(promote(a,b,c)...)
16+
(:)(a::T, b::AbstractFloat, c::T) where {T<:Real} = (:)(promote(a, b, c)...)
17+
(:)(a::T, b::AbstractFloat, c::T) where {T<:AbstractFloat} = (:)(promote(a, b, c)...)
18+
(:)(a::T, b::Real, c::T) where {T<:AbstractFloat} = (:)(promote(a, b, c)...)
1919

2020
(:)(start::T, step::T, stop::T) where {T<:AbstractFloat} =
2121
_colon(OrderStyle(T), ArithmeticStyle(T), start, step, stop)
@@ -168,15 +168,15 @@ range_stop(stop) = range_start_stop(oneunit(stop), stop)
168168
range_stop(stop::Integer) = range_length(stop)
169169

170170
# Stop and length as the only argument
171-
range_stop_length(a::Real, len::Integer) = UnitRange{typeof(a)}(oftype(a, a-len+1), a)
172-
range_stop_length(a::AbstractFloat, len::Integer) = range_step_stop_length(oftype(a, 1), a, len)
173-
range_stop_length(a, len::Integer) = range_step_stop_length(oftype(a-a, 1), a, len)
171+
range_stop_length(a::Real, len::Integer) = UnitRange(promote(a - (len - oneunit(len)), a)...)
172+
range_stop_length(a::AbstractFloat, len::Integer) = range_step_stop_length(oneunit(a), a, len)
173+
range_stop_length(a, len::Integer) = range_step_stop_length(oneunit(a - a), a, len)
174174

175175
range_step_stop_length(step, stop, length) = reverse(range_start_step_length(stop, -step, length))
176176

177-
range_start_length(a::Real, len::Integer) = UnitRange{typeof(a)}(a, oftype(a, a+len-1))
178-
range_start_length(a::AbstractFloat, len::Integer) = range_start_step_length(a, oftype(a, 1), len)
179-
range_start_length(a, len::Integer) = range_start_step_length(a, oftype(a-a, 1), len)
177+
range_start_length(a::Real, len::Integer) = UnitRange(promote(a, a + (len - oneunit(len)))...)
178+
range_start_length(a::AbstractFloat, len::Integer) = range_start_step_length(a, oneunit(a), len)
179+
range_start_length(a, len::Integer) = range_start_step_length(a, oneunit(a - a), len)
180180

181181
range_start_stop(start, stop) = start:stop
182182

@@ -200,10 +200,15 @@ function range_start_step_length(a::T, step, len::Integer) where {T}
200200
_rangestyle(OrderStyle(T), ArithmeticStyle(T), a, step, len)
201201
end
202202

203-
_rangestyle(::Ordered, ::ArithmeticWraps, a::T, step::S, len::Integer) where {T,S} =
204-
StepRange{typeof(a+zero(step)),S}(a, step, a+step*(len-1))
205-
_rangestyle(::Any, ::Any, a::T, step::S, len::Integer) where {T,S} =
206-
StepRangeLen{typeof(a+zero(step)),T,S}(a, step, len)
203+
function _rangestyle(::Ordered, ::ArithmeticWraps, a, step, len::Integer)
204+
stop = a + step * (len - oneunit(len))
205+
T = typeof(stop)
206+
return StepRange{T,typeof(step)}(convert(T, a), step, stop)
207+
end
208+
function _rangestyle(::Any, ::Any, a::T, step, len::Integer) where {T}
209+
stop = a + step * len - step
210+
return StepRangeLen{typeof(stop),T,typeof(step)}(a, step, len)
211+
end
207212

208213
range_start_step_stop(start, step, stop) = start:step:stop
209214

@@ -397,7 +402,7 @@ unitrange_last(start::T, stop::T) where {T<:Integer} =
397402
stop >= start ? stop : convert(T,start-oneunit(start-stop))
398403
unitrange_last(start::T, stop::T) where {T} =
399404
stop >= start ? convert(T,start+floor(stop-start)) :
400-
convert(T,start-oneunit(stop-start))
405+
convert(T,start-oneunit(start-stop))
401406

402407
unitrange(x) = UnitRange(x)
403408

@@ -556,7 +561,7 @@ function LinRange{T}(start, stop, len::Integer) where T
556561
end
557562

558563
function LinRange(start, stop, len::Integer)
559-
T = typeof((stop-start)/len)
564+
T = typeof((len >= 0 ? stop - start : start - stop) / oneunit(len))
560565
LinRange{T}(start, stop, len)
561566
end
562567

@@ -962,29 +967,32 @@ function getindex(r::AbstractUnitRange, s::AbstractUnitRange{T}) where {T<:Integ
962967
@boundscheck checkbounds(r, s)
963968

964969
if T === Bool
965-
range(first(s) ? first(r) : last(r), length = Integer(last(s)))
970+
return range(first(s) ? first(r) : last(r), length = last(s))
966971
else
967972
f = first(r)
968-
st = oftype(f, f + first(s)-firstindex(r))
969-
return range(st, length=length(s))
973+
start = oftype(f, f + first(s)-firstindex(r))
974+
stop = oftype(f, start + (length(s) - 1))
975+
return range(start, stop)
970976
end
971977
end
972978

973979
function getindex(r::OneTo{T}, s::OneTo) where T
974980
@inline
975981
@boundscheck checkbounds(r, s)
976-
OneTo(T(s.stop))
982+
return OneTo(T(s.stop))
977983
end
978984

979985
function getindex(r::AbstractUnitRange, s::StepRange{T}) where {T<:Integer}
980986
@inline
981987
@boundscheck checkbounds(r, s)
982988

983989
if T === Bool
984-
range(first(s) ? first(r) : last(r), step=oneunit(eltype(r)), length = Integer(last(s)))
990+
return range(first(s) ? first(r) : last(r), step=oneunit(eltype(r)), length = last(s))
985991
else
986-
st = oftype(first(r), first(r) + s.start-firstindex(r))
987-
return range(st, step=step(s), length=length(s))
992+
f = first(r)
993+
start = oftype(f, f + s.start-firstindex(r))
994+
stop = oftype(f, start + (length(s) - 1) * step(s))
995+
return range(start, stop; step=step(s))
988996
end
989997
end
990998

@@ -994,19 +1002,21 @@ function getindex(r::StepRange, s::AbstractRange{T}) where {T<:Integer}
9941002

9951003
if T === Bool
9961004
if length(s) == 0
997-
return range(first(r), step=step(r), length=0)
1005+
start, len = first(r), 0
9981006
elseif length(s) == 1
9991007
if first(s)
1000-
return range(first(r), step=step(r), length=1)
1008+
start, len = first(r), 1
10011009
else
1002-
return range(first(r), step=step(r), length=0)
1010+
start, len = first(r), 0
10031011
end
10041012
else # length(s) == 2
1005-
return range(last(r), step=step(r), length=1)
1013+
start, len = last(r), 1
10061014
end
1015+
return range(start, step=step(r); length=len)
10071016
else
1008-
st = oftype(r.start, r.start + (first(s)-1)*step(r))
1009-
return range(st, step=step(r)*step(s), length=length(s))
1017+
f = r.start
1018+
start = oftype(f, f + (first(s)-1)*step(r))
1019+
return range(start; step=step(r)*step(s), length=length(s))
10101020
end
10111021
end
10121022

test/ranges.jl

Lines changed: 57 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1894,41 +1894,50 @@ end
18941894
end
18951895

18961896
@testset "eltype of range(::Integer; step::Rational, length) (#37295)" begin
1897-
@test range(1, step=1//2, length=3) == [1//1, 3//2, 2//1]
1898-
@test eltype(range(1, step=1//2, length=3)) === Rational{Int}
1899-
@test typeof(step(range(1, step=1//2, length=3))) === Rational{Int}
1900-
1901-
@test range(1//1, step=2, length=3) == [1, 3, 5]
1902-
@test eltype(range(1//1, step=2, length=3)) === Rational{Int}
1903-
@test typeof(step(range(1//1, step=2, length=3))) === Int
1904-
1905-
@test range(Int16(1), step=Rational{Int8}(1,2), length=3) == [1//1, 3//2, 2//1]
1906-
@test eltype(range(Int16(1), step=Rational{Int8}(1,2), length=3)) === Rational{Int16}
1907-
@test typeof(step(range(Int16(1), step=Rational{Int8}(1,2), length=3))) === Rational{Int8}
1908-
1909-
@test range(Rational{Int8}(1), step=Int16(2), length=3) == [1, 3, 5]
1910-
@test eltype(range(Rational{Int8}(1), step=Int16(2), length=3)) === Rational{Int16}
1911-
@test typeof(step(range(Rational{Int8}(1), step=Int16(2), length=3))) === Int16
1912-
1913-
@test range('a', step=2, length=3) == ['a', 'c', 'e']
1914-
@test eltype(range('a', step=2, length=3)) === Char
1915-
@test typeof(step(range('a', step=2, length=3))) === Int
1916-
1917-
@test isempty(range(typemax(Int)//1, step=1, length=0))
1918-
@test eltype(range(typemax(Int)//1, step=1, length=0)) === Rational{Int}
1919-
@test typeof(step(range(typemax(Int)//1, step=1, length=0))) === Int
1897+
r = range(1, step=1//2, length=3)
1898+
@test r == [1//1, 3//2, 2//1]
1899+
@test eltype(r) === Rational{Int}
1900+
@test typeof(step(r)) === Rational{Int}
1901+
1902+
r = range(1//1, step=2, length=3)
1903+
@test r == [1, 3, 5]
1904+
@test eltype(r) === Rational{Int}
1905+
@test typeof(step(r)) === Int
1906+
1907+
r = range(Int16(1), step=Rational{Int8}(1,2), length=Int16(3))
1908+
@test r == [1//1, 3//2, 2//1]
1909+
@test eltype(r) === Rational{Int16}
1910+
@test typeof(step(r)) === Rational{Int8}
1911+
1912+
r = range(Rational{Int8}(1), step=Int16(2), length=Int8(3))
1913+
@test r == [1, 3, 5]
1914+
@test eltype(r) === Rational{Int16}
1915+
@test typeof(step(r)) === Int16
1916+
1917+
r = range('a', step=2, length=3)
1918+
@test r == ['a', 'c', 'e']
1919+
@test eltype(r) === Char
1920+
@test typeof(step(r)) === Int
1921+
1922+
r = range(typemax(Int)//1, step=1, length=0)
1923+
@test isempty(r)
1924+
@test eltype(r) === Rational{Int}
1925+
@test typeof(step(r)) === Int
19201926

1921-
@test isempty(range(typemin(Int), step=-1//1, length=0))
1922-
@test eltype(range(typemin(Int), step=-1//1, length=0)) === Rational{Int}
1923-
@test typeof(step(range(typemin(Int), step=-1//1, length=0))) === Rational{Int}
1927+
r = range(typemin(Int), step=-1//1, length=0)
1928+
@test isempty(r)
1929+
@test eltype(r) === Rational{Int}
1930+
@test typeof(step(r)) === Rational{Int}
19241931

1925-
@test StepRangeLen(Int8(1), Int8(2), 3) == Int8[1, 3, 5]
1926-
@test eltype(StepRangeLen(Int8(1), Int8(2), 3)) === Int8
1927-
@test typeof(step(StepRangeLen(Int8(1), Int8(2), 3))) === Int8
1932+
r = StepRangeLen(Int8(1), Int8(2), 3)
1933+
@test r == Int8[1, 3, 5]
1934+
@test eltype(r) === Int8
1935+
@test typeof(step(r)) === Int8
19281936

1929-
@test StepRangeLen(Int8(1), Int8(2), 3, 2) == Int8[-1, 1, 3]
1930-
@test eltype(StepRangeLen(Int8(1), Int8(2), 3, 2)) === Int8
1931-
@test typeof(step(StepRangeLen(Int8(1), Int8(2), 3, 2))) === Int8
1937+
r = StepRangeLen(Int8(1), Int8(2), 3, 2)
1938+
@test r == Int8[-1, 1, 3]
1939+
@test eltype(r) === Int8
1940+
@test typeof(step(r)) === Int8
19321941
end
19331942

19341943
@testset "LinRange eltype for element types that wrap integers" begin
@@ -2226,3 +2235,19 @@ let r = Ptr{Cvoid}(20):-UInt(2):Ptr{Cvoid}(10)
22262235
@test step(r) === -UInt(2)
22272236
@test last(r) === Ptr{Cvoid}(10)
22282237
end
2238+
2239+
# test behavior of wrap-around and promotion of empty ranges (#35711)
2240+
@test length(range(0, length=UInt(0))) === UInt(0)
2241+
@test !isempty(range(0, length=UInt(0)))
2242+
@test length(range(typemax(Int), length=UInt(0))) === UInt(0)
2243+
@test isempty(range(typemax(Int), length=UInt(0)))
2244+
@test length(range(0, length=UInt(0), step=UInt(2))) == typemin(Int) % UInt
2245+
@test !isempty(range(0, length=UInt(0), step=UInt(2)))
2246+
@test length(range(typemax(Int), length=UInt(0), step=UInt(2))) === UInt(0)
2247+
@test isempty(range(typemax(Int), length=UInt(0), step=UInt(2)))
2248+
@test length(range(typemax(Int), length=UInt(0), step=2)) === UInt(0)
2249+
@test isempty(range(typemax(Int), length=UInt(0), step=2))
2250+
@test length(range(typemax(Int), length=0, step=UInt(2))) === UInt(0)
2251+
@test isempty(range(typemax(Int), length=0, step=UInt(2)))
2252+
2253+
@test length(range(1, length=typemax(Int128))) === typemax(Int128)

0 commit comments

Comments
 (0)