Skip to content

Commit 3cc0590

Browse files
authored
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.
1 parent 76a9772 commit 3cc0590

File tree

2 files changed

+36
-2
lines changed

2 files changed

+36
-2
lines changed

base/range.jl

+15-2
Original file line numberDiff line numberDiff line change
@@ -1380,8 +1380,21 @@ function vcat(rs::AbstractRange{T}...) where T
13801380
return a
13811381
end
13821382

1383-
Array{T,1}(r::AbstractRange{T}) where {T} = vcat(r)
1384-
collect(r::AbstractRange) = vcat(r)
1383+
# This method differs from that for AbstractArrays as it
1384+
# use iteration instead of indexing. This works even if certain
1385+
# non-standard ranges don't support indexing.
1386+
# See https://github.com/JuliaLang/julia/pull/27302
1387+
# Similarly, collect(r::AbstractRange) uses iteration
1388+
function Array{T,1}(r::AbstractRange{T}) where {T}
1389+
a = Vector{T}(undef, length(r))
1390+
i = 1
1391+
for x in r
1392+
@inbounds a[i] = x
1393+
i += 1
1394+
end
1395+
return a
1396+
end
1397+
collect(r::AbstractRange) = Array(r)
13851398

13861399
_reverse(r::OrdinalRange, ::Colon) = (:)(last(r), negate(step(r)), first(r))
13871400
function _reverse(r::StepRangeLen, ::Colon)

test/ranges.jl

+21
Original file line numberDiff line numberDiff line change
@@ -1802,6 +1802,7 @@ Base.div(x::Displacement, y::Displacement) = Displacement(div(x.val, y.val))
18021802
# required for collect (summing lengths); alternatively, should length return Int by default?
18031803
Base.promote_rule(::Type{Displacement}, ::Type{Int}) = Int
18041804
Base.convert(::Type{Int}, x::Displacement) = x.val
1805+
Base.Int(x::Displacement) = x.val
18051806

18061807
# Unsigned complement, for testing checked_length
18071808
struct UPosition <: Unsigned
@@ -2499,11 +2500,31 @@ end
24992500
@test (-10:2:typemax(Int))[typemax(Int)÷2+2] == typemax(Int)-9
25002501
end
25012502

2503+
@testset "collect with specialized vcat" begin
2504+
struct OneToThree <: AbstractUnitRange{Int} end
2505+
Base.size(r::OneToThree) = (3,)
2506+
Base.first(r::OneToThree) = 1
2507+
Base.length(r::OneToThree) = 3
2508+
Base.last(r::OneToThree) = 3
2509+
function Base.getindex(r::OneToThree, i::Int)
2510+
checkbounds(r, i)
2511+
i
2512+
end
2513+
Base.vcat(r::OneToThree) = r
2514+
r = OneToThree()
2515+
a = Array(r)
2516+
@test a isa Vector{Int}
2517+
@test a == r
2518+
@test collect(r) isa Vector{Int}
2519+
@test collect(r) == r
2520+
end
2521+
25022522
@testset "isassigned" begin
25032523
for (r, val) in ((1:3, 3), (1:big(2)^65, big(2)^65))
25042524
@test isassigned(r, lastindex(r))
25052525
# test that the indexing actually succeeds
25062526
@test r[end] == val
25072527
@test_throws ArgumentError isassigned(r, true)
25082528
end
2529+
25092530
end

0 commit comments

Comments
 (0)