Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Array(::AbstractRange) should return an Array #50568

Merged
merged 3 commits into from
Jul 24, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions base/range.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1377,8 +1377,16 @@ function vcat(rs::AbstractRange{T}...) where T
return a
end

Array{T,1}(r::AbstractRange{T}) where {T} = vcat(r)
collect(r::AbstractRange) = vcat(r)
function Array{T,1}(r::AbstractRange{T}) where {T}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a minor note from the sidelines: there is a discussion in this PR why this method is needed, and why removing it would be possible, but would require changing code/behavior for which in turn it is unclear why it is the way it is.

After this PR, there'll be one more layer which when reviewed in a couple years, people will wonder "why is it this way?" and a future PR might also say "it is unclear why it is there" just referencing this method now?

Of course they can dig through old commits, then look up the corresponding PRs, read the discussions there, rinse and repeat ("code archaeology").

Live would be much easier for those poor future developers if there was a brief comment here summarizing this (and if there had been such a comment in the code you were wondering about, I am sure that would have been helpful, too... :-/).

Anyway, just my two cents as a bystander. You do you :-). For what it's worth, I'll appreciate your contribution either way :-)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, a comment here would make sense if we decide to retain this method

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the git blame, it seems support for non-conforming UnitRanges was added intentionally in #27302, and is pre-1.0 behavior(!). In light of this, I'll add a comment now to link to that discussion, but in the future, it might be better to return StepRanges automatically in such cases by checking the type of something like oneadditiveunit, as discussed on discourse.

a = Vector{T}(undef, length(r))
i = 1
for x in r
@inbounds a[i] = x
i += 1
end
return a
end
collect(r::AbstractRange) = Array(r)

_reverse(r::OrdinalRange, ::Colon) = (:)(last(r), negate(step(r)), first(r))
function _reverse(r::StepRangeLen, ::Colon)
Expand Down
20 changes: 20 additions & 0 deletions test/ranges.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1802,6 +1802,7 @@ Base.div(x::Displacement, y::Displacement) = Displacement(div(x.val, y.val))
# required for collect (summing lengths); alternatively, should length return Int by default?
Base.promote_rule(::Type{Displacement}, ::Type{Int}) = Int
Base.convert(::Type{Int}, x::Displacement) = x.val
Base.Int(x::Displacement) = x.val

# Unsigned complement, for testing checked_length
struct UPosition <: Unsigned
Expand Down Expand Up @@ -2498,3 +2499,22 @@ end
# a case that using mul_with_overflow & add_with_overflow might get wrong:
@test (-10:2:typemax(Int))[typemax(Int)÷2+2] == typemax(Int)-9
end

@testset "collect with specialized vcat" begin
struct OneToThree <: AbstractUnitRange{Int} end
Base.size(r::OneToThree) = (3,)
Base.first(r::OneToThree) = 1
Base.length(r::OneToThree) = 3
Base.last(r::OneToThree) = 3
function Base.getindex(r::OneToThree, i::Int)
checkbounds(r, i)
i
end
Base.vcat(r::OneToThree) = r
r = OneToThree()
a = Array(r)
@test a isa Vector{Int}
@test a == r
@test collect(r) isa Vector{Int}
@test collect(r) == r
end