Skip to content

Commit 025ef93

Browse files
jishnubKristofferC
authored andcommitted
Array(::AbstractRange) should return an Array (#50568)
Currently, `Array(r::AbstractRange)` falls back to `vcat(r)`, but certain ranges may choose to specialize `vcat(r::AbstractRange)` to not return an `Array`. This PR ensures that `Array(r)` always returns an `Array`. At present, there's some code overlap with `vcat` (just above the `Array` method added in this PR). Perhaps some of these may be replaced by `unsafe_copyto!`, but the tests for ranges include some special cases that don't support `getindex`, which complicates things a bit. I've not done this for now. In any case, the common bit of code is pretty simple, so perhaps the duplication is harmless. (cherry picked from commit 3cc0590)
1 parent 88ece46 commit 025ef93

File tree

2 files changed

+35
-2
lines changed

2 files changed

+35
-2
lines changed

base/range.jl

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1363,8 +1363,21 @@ function vcat(rs::AbstractRange{T}...) where T
13631363
return a
13641364
end
13651365

1366-
Array{T,1}(r::AbstractRange{T}) where {T} = vcat(r)
1367-
collect(r::AbstractRange) = vcat(r)
1366+
# This method differs from that for AbstractArrays as it
1367+
# use iteration instead of indexing. This works even if certain
1368+
# non-standard ranges don't support indexing.
1369+
# See https://github.com/JuliaLang/julia/pull/27302
1370+
# Similarly, collect(r::AbstractRange) uses iteration
1371+
function Array{T,1}(r::AbstractRange{T}) where {T}
1372+
a = Vector{T}(undef, length(r))
1373+
i = 1
1374+
for x in r
1375+
@inbounds a[i] = x
1376+
i += 1
1377+
end
1378+
return a
1379+
end
1380+
collect(r::AbstractRange) = Array(r)
13681381

13691382
_reverse(r::OrdinalRange, ::Colon) = (:)(last(r), negate(step(r)), first(r))
13701383
function _reverse(r::StepRangeLen, ::Colon)

test/ranges.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1751,6 +1751,7 @@ Base.div(x::Displacement, y::Displacement) = Displacement(div(x.val, y.val))
17511751
# required for collect (summing lengths); alternatively, should length return Int by default?
17521752
Base.promote_rule(::Type{Displacement}, ::Type{Int}) = Int
17531753
Base.convert(::Type{Int}, x::Displacement) = x.val
1754+
Base.Int(x::Displacement) = x.val
17541755

17551756
# Unsigned complement, for testing checked_length
17561757
struct UPosition <: Unsigned
@@ -2405,3 +2406,22 @@ end
24052406
srl = StepRangeLen(PR49516(1), PR49516(2), 10)
24062407
@test sprint(show, srl) == "PR49516(1):PR49516(2):PR49516(19)"
24072408
end
2409+
2410+
@testset "collect with specialized vcat" begin
2411+
struct OneToThree <: AbstractUnitRange{Int} end
2412+
Base.size(r::OneToThree) = (3,)
2413+
Base.first(r::OneToThree) = 1
2414+
Base.length(r::OneToThree) = 3
2415+
Base.last(r::OneToThree) = 3
2416+
function Base.getindex(r::OneToThree, i::Int)
2417+
checkbounds(r, i)
2418+
i
2419+
end
2420+
Base.vcat(r::OneToThree) = r
2421+
r = OneToThree()
2422+
a = Array(r)
2423+
@test a isa Vector{Int}
2424+
@test a == r
2425+
@test collect(r) isa Vector{Int}
2426+
@test collect(r) == r
2427+
end

0 commit comments

Comments
 (0)