diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 327815fde932c..9c88f1f96cc87 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -86,7 +86,7 @@ julia> axes(A) """ function axes(A) @_inline_meta - map(OneTo, size(A)) + map(oneto, size(A)) end """ @@ -107,10 +107,10 @@ require_one_based_indexing(A...) = !has_offset_axes(A...) || throw(ArgumentError # in other applications. axes1(A::AbstractArray{<:Any,0}) = OneTo(1) axes1(A::AbstractArray) = (@_inline_meta; axes(A)[1]) -axes1(iter) = OneTo(length(iter)) +axes1(iter) = oneto(length(iter)) unsafe_indices(A) = axes(A) -unsafe_indices(r::AbstractRange) = (OneTo(unsafe_length(r)),) # Ranges use checked_sub for size +unsafe_indices(r::AbstractRange) = (oneto(unsafe_length(r)),) # Ranges use checked_sub for size keys(a::AbstractArray) = CartesianIndices(axes(a)) keys(a::AbstractVector) = LinearIndices(a) @@ -308,7 +308,7 @@ function eachindex(A::AbstractArray, B::AbstractArray...) @_inline_meta eachindex(IndexStyle(A,B...), A, B...) end -eachindex(::IndexLinear, A::AbstractArray) = (@_inline_meta; OneTo(length(A))) +eachindex(::IndexLinear, A::AbstractArray) = (@_inline_meta; oneto(length(A))) eachindex(::IndexLinear, A::AbstractVector) = (@_inline_meta; axes1(A)) function eachindex(::IndexLinear, A::AbstractArray, B::AbstractArray...) @_inline_meta @@ -1483,12 +1483,11 @@ vcat(V::AbstractVector{T}...) where {T} = typed_vcat(T, V...) # but that solution currently fails (see #27188 and #27224) AbstractVecOrTuple{T} = Union{AbstractVector{<:T}, Tuple{Vararg{T}}} -function _typed_vcat(::Type{T}, V::AbstractVecOrTuple{AbstractVector}) where T - n = 0 - for Vk in V - n += Int(length(Vk))::Int - end - a = similar(V[1], T, n) +_typed_vcat_similar(V, T, n) = similar(V[1], T, n) +_typed_vcat(::Type{T}, V::AbstractVecOrTuple{AbstractVector}) where T = + _typed_vcat!(_typed_vcat_similar(V, T, mapreduce(length, +, V)), V) + +function _typed_vcat!(a::AbstractVector{T}, V::AbstractVecOrTuple{AbstractVector}) where T pos = 1 for k=1:Int(length(V))::Int Vk = V[k] @@ -1634,7 +1633,7 @@ _cat(dims, X...) = cat_t(promote_eltypeof(X...), X...; dims=dims) @inline cat_t(::Type{T}, X...; dims) where {T} = _cat_t(dims, T, X...) @inline function _cat_t(dims, ::Type{T}, X...) where {T} catdims = dims2cat(dims) - shape = cat_shape(catdims, map(cat_size, X)::Tuple{Vararg{Union{Int,Dims}}})::Dims + shape = cat_shape(catdims, map(cat_size, X)) A = cat_similar(X[1], T, shape) if count(!iszero, catdims)::Int > 1 fill!(A, zero(T)) @@ -1642,7 +1641,7 @@ _cat(dims, X...) = cat_t(promote_eltypeof(X...), X...; dims=dims) return __cat(A, shape, catdims, X...) end -function __cat(A, shape::NTuple{M,Int}, catdims, X...) where M +function __cat(A, shape::NTuple{M}, catdims, X...) where M N = M::Int offsets = zeros(Int, N) inds = Vector{UnitRange{Int}}(undef, N) diff --git a/base/arrayshow.jl b/base/arrayshow.jl index f355d9e2dd8b4..37adaf13f669e 100644 --- a/base/arrayshow.jl +++ b/base/arrayshow.jl @@ -58,9 +58,9 @@ Alignment is reported as a vector of (left,right) tuples, one for each column going across the screen. """ function alignment(io::IO, X::AbstractVecOrMat, - rows::AbstractVector, cols::AbstractVector, - cols_if_complete::Integer, cols_otherwise::Integer, sep::Integer) - a = Tuple{Int, Int}[] + rows::AbstractVector{T}, cols::AbstractVector{V}, + cols_if_complete::Integer, cols_otherwise::Integer, sep::Integer) where {T,V} + a = Tuple{T, V}[] for j in cols # need to go down each column one at a time l = r = 0 for i in rows # plumb down and see what largest element sizes are @@ -166,6 +166,11 @@ function print_matrix(io::IO, @nospecialize(X::AbstractVecOrMat), vdots::AbstractString = "\u22ee", ddots::AbstractString = " \u22f1 ", hmod::Integer = 5, vmod::Integer = 5) + # use invokelatest to avoid backtracing in type invalidation, ref #37741 + invokelatest(_print_matrix, io, X, pre, sep, post, hdots, vdots, ddots, hmod, vmod, unitrange(axes(X,1)), unitrange(axes(X,2))) +end + +function _print_matrix(io, @nospecialize(X::AbstractVecOrMat), pre, sep, post, hdots, vdots, ddots, hmod, vmod, rowsA, colsA) hmod, vmod = Int(hmod)::Int, Int(vmod)::Int if !(get(io, :limit, false)::Bool) screenheight = screenwidth = typemax(Int) @@ -178,7 +183,6 @@ function print_matrix(io::IO, @nospecialize(X::AbstractVecOrMat), postsp = "" @assert textwidth(hdots) == textwidth(ddots) sepsize = length(sep)::Int - rowsA, colsA = UnitRange{Int}(axes(X,1)), UnitRange{Int}(axes(X,2)) m, n = length(rowsA), length(colsA) # To figure out alignments, only need to look at as many rows as could # fit down screen. If screen has at least as many rows as A, look at A. diff --git a/base/range.jl b/base/range.jl index b275e95c7c266..63f4c1aed02e2 100644 --- a/base/range.jl +++ b/base/range.jl @@ -295,6 +295,8 @@ unitrange_last(start::T, stop::T) where {T} = ifelse(stop >= start, convert(T,start+floor(stop-start)), convert(T,start-oneunit(stop-start))) +unitrange(x) = UnitRange(x) + if isdefined(Main, :Base) # Constant-fold-able indexing into tuples to functionally expose Base.tail and Base.front function getindex(@nospecialize(t::Tuple), r::UnitRange) @@ -332,6 +334,7 @@ struct OneTo{T<:Integer} <: AbstractUnitRange{T} end OneTo(stop::T) where {T<:Integer} = OneTo{T}(stop) OneTo(r::AbstractRange{T}) where {T<:Integer} = OneTo{T}(r) +oneto(r) = OneTo(r) ## Step ranges parameterized by length diff --git a/base/reshapedarray.jl b/base/reshapedarray.jl index 3534049a7a10b..d9a9f4eafaa80 100644 --- a/base/reshapedarray.jl +++ b/base/reshapedarray.jl @@ -153,7 +153,7 @@ rdims(out::Tuple{}, inds::NTuple{M,Any}) where {M} = () rdims(out::Tuple{Any}, inds::Tuple{}) = out # N == 1, M == 0 rdims(out::NTuple{N,Any}, inds::Tuple{}) where {N} = out # N > 1, M == 0 rdims(out::Tuple{Any}, inds::Tuple{Any}) = inds # N == 1, M == 1 -rdims(out::Tuple{Any}, inds::NTuple{M,Any}) where {M} = (OneTo(rdims_trailing(inds...)),) # N == 1, M > 1 +rdims(out::Tuple{Any}, inds::NTuple{M,Any}) where {M} = (oneto(rdims_trailing(inds...)),) # N == 1, M > 1 rdims(out::NTuple{N,Any}, inds::NTuple{N,Any}) where {N} = inds # N > 1, M == N rdims(out::NTuple{N,Any}, inds::NTuple{M,Any}) where {N,M} = (first(inds), rdims(tail(out), tail(inds))...) # N > 1, M > 1, M != N @@ -207,7 +207,7 @@ size(A::ReshapedArray) = A.dims similar(A::ReshapedArray, eltype::Type, dims::Dims) = similar(parent(A), eltype, dims) IndexStyle(::Type{<:ReshapedArrayLF}) = IndexLinear() parent(A::ReshapedArray) = A.parent -parentindices(A::ReshapedArray) = map(OneTo, size(parent(A))) +parentindices(A::ReshapedArray) = map(oneto, size(parent(A))) reinterpret(::Type{T}, A::ReshapedArray, dims::Dims) where {T} = reinterpret(T, parent(A), dims) elsize(::Type{<:ReshapedArray{<:Any,<:Any,P}}) where {P} = elsize(P) diff --git a/base/subarray.jl b/base/subarray.jl index 3b232d896d3c0..d1207ec094037 100644 --- a/base/subarray.jl +++ b/base/subarray.jl @@ -60,7 +60,7 @@ viewindexing(I::Tuple{Vararg{Any}}) = IndexCartesian() viewindexing(I::Tuple{AbstractArray, Vararg{Any}}) = IndexCartesian() # Simple utilities -size(V::SubArray) = (@_inline_meta; map(n->Int(unsafe_length(n)), axes(V))) +size(V::SubArray) = (@_inline_meta; map(unsafe_length, axes(V))) similar(V::SubArray, T::Type, dims::Dims) = similar(V.parent, T, dims) @@ -90,7 +90,7 @@ julia> parentindices(V) (1, Base.Slice(Base.OneTo(2))) ``` """ -parentindices(a::AbstractArray) = map(OneTo, size(a)) +parentindices(a::AbstractArray) = map(oneto, size(a)) ## Aliasing detection dataids(A::SubArray) = (dataids(A.parent)..., _splatmap(dataids, A.indices)...) @@ -107,7 +107,7 @@ function unaliascopy(V::SubArray{T,N,A,I,LD}) where {T,N,A<:Array,I<:Tuple{Varar end # Transform indices to be "dense" _trimmedindex(i::Real) = oftype(i, 1) -_trimmedindex(i::AbstractUnitRange) = oftype(i, OneTo(length(i))) +_trimmedindex(i::AbstractUnitRange) = oftype(i, oneto(length(i))) _trimmedindex(i::AbstractArray) = oftype(i, reshape(eachindex(IndexLinear(), i), axes(i))) ## SubArray creation diff --git a/test/abstractarray.jl b/test/abstractarray.jl index 958e62e1ecdc1..6d052bfb9a2ae 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -1234,6 +1234,11 @@ end @test Base.rest(a, st) == [3, 2, 4] end +@testset "issue #37741, non-int cat" begin + @test [1; 1:BigInt(5)] == [1; 1:5] + @test [1:BigInt(5); 1] == [1:5; 1] +end + @testset "Base.isstored" begin a = rand(3, 4, 5) @test Base.isstored(a, 1, 2, 3) diff --git a/test/subarray.jl b/test/subarray.jl index 4fd189e2ae441..985b481438c5b 100644 --- a/test/subarray.jl +++ b/test/subarray.jl @@ -698,3 +698,17 @@ import InteractiveUtils @test M*v == copy(M)*v @test (InteractiveUtils.@which M*v) == (InteractiveUtils.@which copy(M)*v) end + + +isdefined(Main, :InfiniteArrays) || @eval Main include("testhelpers/InfiniteArrays.jl") +using .Main.InfiniteArrays, Base64 + +@testset "PR #37741: non-Int sizes" begin + r = BigInt(1):BigInt(100_000_000)^100 + v = SubArray(r, (r,)) + @test size(v) == (last(r),) + + v = SubArray(OneToInf(), (OneToInf(),)) + @test size(v) == (Infinity(),) + @test stringmime("text/plain", v; context=(:limit => true)) == "$(Infinity())-element view(::$(OneToInf{Int}), 1:1:$(Infinity())) with eltype $Int with indices 1:1:$(Infinity()):\n 1\n 2\n 3\n 4\n 5\n 6\n 7\n 8\n 9\n 10\n ⋮" +end diff --git a/test/testhelpers/InfiniteArrays.jl b/test/testhelpers/InfiniteArrays.jl new file mode 100644 index 0000000000000..bc6de1afc5503 --- /dev/null +++ b/test/testhelpers/InfiniteArrays.jl @@ -0,0 +1,51 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# InfiniteArrays (arrays with infinite size) + +# This test file is designed to exercise support for generic sizing, +# even though infinite arrays aren't implemented in Base. + +module InfiniteArrays + +export OneToInf, Infinity + +""" + Infinity() + +represents infinite cardinality. Note that `Infinity <: Integer` to support +being treated as an index. +""" +struct Infinity <: Integer end + +Base.:(==)(::Infinity, ::Int) = false +Base.:(==)(::Int, ::Infinity) = false +Base.:(<)(::Int, ::Infinity) = true +Base.:(≤)(::Int, ::Infinity) = true +Base.:(≤)(::Infinity, ::Int) = false +Base.:(≤)(::Infinity, ::Infinity) = true +Base.:(-)(::Infinity, ::Int) = Infinity() +Base.:(+)(::Infinity, ::Int) = Infinity() +Base.:(:)(::Infinity, ::Infinity) = 1:0 + +""" + OneToInf(n) + +Define an `AbstractInfUnitRange` that behaves like `1:∞`, with the added +distinction that the limits are guaranteed (by the type system) to +be 1 and ∞. +""" +struct OneToInf{T<:Integer} <: AbstractUnitRange{T} end + +OneToInf() = OneToInf{Int}() + +Base.axes(r::OneToInf) = (r,) +Base.unsafe_indices(r::OneToInf) = (r,) +Base.unsafe_length(r::OneToInf) = Infinity() +Base.size(r::OneToInf) = (Infinity(),) +Base.first(r::OneToInf{T}) where {T} = oneunit(T) +Base.length(r::OneToInf{T}) where {T} = Infinity() +Base.last(r::OneToInf{T}) where {T} = Infinity() +Base.unitrange(r::OneToInf) = r +Base.oneto(::Infinity) = OneToInf() + +end \ No newline at end of file