Skip to content

Commit

Permalink
Add promote_rule for OneTo and AbstractUnitRange (JuliaLang#53795)
Browse files Browse the repository at this point in the history
We may assume that a `OneTo` behaves like a `UnitRange` on promotion.
With this, custom `AbstractUnitRange` types don't need to define
promotion rules with `OneTo`, and the following works out of the box:
```julia
julia> struct MyUnitRange{T} <: AbstractUnitRange{T}
               range::UnitRange{T}
       end

julia> Base.first(r::MyUnitRange) = first(r.range)

julia> Base.last(r::MyUnitRange) = last(r.range)

julia> Base.size(r::MyUnitRange) = size(r.range)

julia> Base.length(r::MyUnitRange) = length(r.range)

julia> Base.getindex(r::MyUnitRange, i::Int) = getindex(r.range, i)

julia> promote(MyUnitRange(3:4), Base.OneTo(4))
(3:4, 1:4)
```
There is some potential for ambiguity if a package defines
`promote_rule(::AbstractUnitRange, ::CustomUnitRange)`, in which case a
working `promote_rule(::OneTo, ::CustomUnitRange)` becomes ambiguous.
However, a similar problem exists for `UnitRange` as well, and I think
the benefits outweigh the risks.

Co-authored-by: N5N3 <2642243996@qq.com>
  • Loading branch information
jishnub and N5N3 authored Apr 29, 2024
1 parent 86ab75e commit 68da780
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 0 deletions.
3 changes: 3 additions & 0 deletions base/range.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1294,6 +1294,9 @@ promote_rule(a::Type{OneTo{T1}}, b::Type{OneTo{T2}}) where {T1,T2} =
OneTo{T}(r::OneTo{T}) where {T<:Integer} = r
OneTo{T}(r::OneTo) where {T<:Integer} = OneTo{T}(r.stop)

promote_rule(a::Type{OneTo{T1}}, ::Type{UR}) where {T1,UR<:AbstractUnitRange} =
promote_rule(UnitRange{T1}, UR)

promote_rule(a::Type{UnitRange{T1}}, ::Type{UR}) where {T1,UR<:AbstractUnitRange} =
promote_rule(a, UnitRange{eltype(UR)})
UnitRange{T}(r::AbstractUnitRange) where {T<:Real} = UnitRange{T}(first(r), last(r))
Expand Down
13 changes: 13 additions & 0 deletions test/ranges.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2726,3 +2726,16 @@ end
@test Base._log_twice64_unchecked(NaN).lo isa Float64
@test Base._log_twice64_unchecked(Inf).lo isa Float64
end

@testset "OneTo promotion" begin
struct MyUnitRange{T} <: AbstractUnitRange{T}
range::UnitRange{T}
end
Base.first(r::MyUnitRange) = first(r.range)
Base.last(r::MyUnitRange) = last(r.range)
Base.size(r::MyUnitRange) = size(r.range)
Base.length(r::MyUnitRange) = length(r.range)
Base.getindex(r::MyUnitRange, i::Int) = getindex(r.range, i)
@test promote(MyUnitRange(2:3), Base.OneTo(3)) == (2:3, 1:3)
@test promote(MyUnitRange(UnitRange(3.0, 4.0)), Base.OneTo(3)) == (3.0:4.0, 1.0:3.0)
end

0 comments on commit 68da780

Please sign in to comment.