From c6e31464819c7f3c389a2e16e3f4ae0c4ac5104f Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 22 Feb 2022 12:41:58 -0500 Subject: [PATCH 1/2] fix reduced_indices type bug (#44096) The result is type-asserted to be equal to the input, so we need to use a non-promoting function. --- base/reducedim.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/reducedim.jl b/base/reducedim.jl index d55db2768e62b..4ccf826df5865 100644 --- a/base/reducedim.jl +++ b/base/reducedim.jl @@ -44,7 +44,7 @@ function reduced_indices0(inds::Indices{N}, d::Int) where N end function reduced_indices(inds::Indices{N}, region) where N - rinds = [inds...] + rinds = collect(inds) for i in region isa(i, Integer) || throw(ArgumentError("reduced dimension(s) must be integers")) d = Int(i) @@ -58,7 +58,7 @@ function reduced_indices(inds::Indices{N}, region) where N end function reduced_indices0(inds::Indices{N}, region) where N - rinds = [inds...] + rinds = collect(inds) for i in region isa(i, Integer) || throw(ArgumentError("reduced dimension(s) must be integers")) d = Int(i) From 5cac5b92980d67e688e6cfafd6e7b22b55d6e9f5 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 9 Feb 2022 14:21:14 -0500 Subject: [PATCH 2/2] implement promote for BitArray (#44096) Defines a fallback promote_result for any AbstractArray, for the case when specific promote_rules are not defined for the specific array type. Add a few good promote_rules too. Fix #43551 Co-authored-by: Jakob Sachs --- base/indices.jl | 20 +++++++++++++------- base/promotion.jl | 4 ++-- base/range.jl | 8 ++++++-- test/bitarray.jl | 16 +++++++++++++++- test/broadcast.jl | 15 +++++++++------ 5 files changed, 45 insertions(+), 18 deletions(-) diff --git a/base/indices.jl b/base/indices.jl index 28028f23c72a3..6f3be4f8b0eed 100644 --- a/base/indices.jl +++ b/base/indices.jl @@ -351,6 +351,8 @@ struct Slice{T<:AbstractUnitRange} <: AbstractUnitRange{Int} indices::T end Slice(S::Slice) = S +Slice{T}(S::Slice) where {T<:AbstractUnitRange} = Slice{T}(T(S.indices)) + axes(S::Slice) = (IdentityUnitRange(S.indices),) axes1(S::Slice) = IdentityUnitRange(S.indices) axes(S::Slice{<:OneTo}) = (S.indices,) @@ -366,7 +368,6 @@ getindex(S::Slice, i::StepRange{<:Integer}) = (@inline; @boundscheck checkbounds show(io::IO, r::Slice) = print(io, "Base.Slice(", r.indices, ")") iterate(S::Slice, s...) = iterate(S.indices, s...) - """ IdentityUnitRange(range::AbstractUnitRange) @@ -378,6 +379,8 @@ struct IdentityUnitRange{T<:AbstractUnitRange} <: AbstractUnitRange{Int} indices::T end IdentityUnitRange(S::IdentityUnitRange) = S +IdentityUnitRange{T}(S::IdentityUnitRange) where {T<:AbstractUnitRange} = IdentityUnitRange{T}(T(S.indices)) + # IdentityUnitRanges are offset and thus have offset axes, so they are their own axes axes(S::IdentityUnitRange) = (S,) axes1(S::IdentityUnitRange) = S @@ -448,6 +451,8 @@ julia> linear[1,2] struct LinearIndices{N,R<:NTuple{N,AbstractUnitRange{Int}}} <: AbstractArray{Int,N} indices::R end +convert(::Type{LinearIndices{N,R}}, inds::LinearIndices{N}) where {N,R<:NTuple{N,AbstractUnitRange{Int}}} = + LinearIndices{N,R}(convert(R, inds.indices)) LinearIndices(::Tuple{}) = LinearIndices{0,typeof(())}(()) LinearIndices(inds::NTuple{N,AbstractUnitRange{<:Integer}}) where {N} = @@ -459,16 +464,17 @@ LinearIndices(A::Union{AbstractArray,SimpleVector}) = LinearIndices(axes(A)) _convert2ind(i::Integer) = Base.OneTo(i) _convert2ind(ind::AbstractUnitRange) = first(ind):last(ind) -promote_rule(::Type{LinearIndices{N,R1}}, ::Type{LinearIndices{N,R2}}) where {N,R1,R2} = - LinearIndices{N,indices_promote_type(R1,R2)} - function indices_promote_type(::Type{Tuple{R1,Vararg{R1,N}}}, ::Type{Tuple{R2,Vararg{R2,N}}}) where {R1,R2,N} R = promote_type(R1, R2) - Tuple{R,Vararg{R,N}} + return Tuple{R, Vararg{R, N}} end -convert(::Type{LinearIndices{N,R}}, inds::LinearIndices{N}) where {N,R} = - LinearIndices(convert(R, inds.indices)) +promote_rule(::Type{LinearIndices{N,R1}}, ::Type{LinearIndices{N,R2}}) where {N,R1,R2} = + LinearIndices{N,indices_promote_type(R1,R2)} +promote_rule(a::Type{Slice{T1}}, b::Type{Slice{T2}}) where {T1,T2} = + el_same(promote_type(T1, T2), a, b) +promote_rule(a::Type{IdentityUnitRange{T1}}, b::Type{IdentityUnitRange{T2}}) where {T1,T2} = + el_same(promote_type(T1, T2), a, b) # AbstractArray implementation IndexStyle(::Type{<:LinearIndices}) = IndexLinear() diff --git a/base/promotion.jl b/base/promotion.jl index 845e16ca499d3..c252c16b3c12a 100644 --- a/base/promotion.jl +++ b/base/promotion.jl @@ -303,9 +303,9 @@ it for new types as appropriate. """ function promote_rule end -promote_rule(::Type{<:Any}, ::Type{<:Any}) = Bottom +promote_rule(::Type, ::Type) = Bottom -promote_result(::Type{<:Any},::Type{<:Any},::Type{T},::Type{S}) where {T,S} = (@inline; promote_type(T,S)) +promote_result(::Type,::Type,::Type{T},::Type{S}) where {T,S} = (@inline; promote_type(T,S)) # If no promote_rule is defined, both directions give Bottom. In that # case use typejoin on the original types instead. promote_result(::Type{T},::Type{S},::Type{Bottom},::Type{Bottom}) where {T,S} = (@inline; typejoin(T, S)) diff --git a/base/range.jl b/base/range.jl index f3def4549a13a..84ea19d65feb3 100644 --- a/base/range.jl +++ b/base/range.jl @@ -1263,13 +1263,17 @@ function -(r::LinRange) LinRange{typeof(start)}(start, -r.stop, length(r)) end - # promote eltype if at least one container wouldn't change, otherwise join container types. -el_same(::Type{T}, a::Type{<:AbstractArray{T,n}}, b::Type{<:AbstractArray{T,n}}) where {T,n} = a +el_same(::Type{T}, a::Type{<:AbstractArray{T,n}}, b::Type{<:AbstractArray{T,n}}) where {T,n} = a # we assume a === b el_same(::Type{T}, a::Type{<:AbstractArray{T,n}}, b::Type{<:AbstractArray{S,n}}) where {T,S,n} = a el_same(::Type{T}, a::Type{<:AbstractArray{S,n}}, b::Type{<:AbstractArray{T,n}}) where {T,S,n} = b el_same(::Type, a, b) = promote_typejoin(a, b) +promote_result(::Type{<:AbstractArray}, ::Type{<:AbstractArray}, ::Type{T}, ::Type{S}) where {T,S} = (@inline; promote_type(T,S)) +promote_result(::Type{T}, ::Type{S}, ::Type{Bottom}, ::Type{Bottom}) where {T<:AbstractArray,S<:AbstractArray} = (@inline; promote_typejoin(T,S)) +# If no promote_rule is defined, both directions give Bottom. In that case use typejoin on the eltypes instead and give Array as the container. +promote_result(::Type{<:AbstractArray{T,n}}, ::Type{<:AbstractArray{S,n}}, ::Type{Bottom}, ::Type{Bottom}) where {T,S,n} = (@inline; Array{promote_type(T,S),n}) + promote_rule(a::Type{UnitRange{T1}}, b::Type{UnitRange{T2}}) where {T1,T2} = el_same(promote_type(T1, T2), a, b) UnitRange{T}(r::UnitRange{T}) where {T<:Real} = r diff --git a/test/bitarray.jl b/test/bitarray.jl index 75a6389815336..9ce3775a5d409 100644 --- a/test/bitarray.jl +++ b/test/bitarray.jl @@ -98,6 +98,20 @@ end timesofar("conversions") +@testset "Promotions for size $sz" for (sz, T) in allsizes + @test isequal(promote(falses(sz...), zeros(sz...)), + (zeros(sz...), zeros(sz...))) + @test isequal(promote(trues(sz...), ones(sz...)), + (ones(sz...), ones(sz...))) + ae = falses(1, sz...) + ex = (@test_throws ErrorException promote(ae, ones(sz...))).value + @test startswith(ex.msg, "promotion of types Bit") + ex = (@test_throws ErrorException promote(ae, falses(sz...))).value + @test startswith(ex.msg, "promotion of types Bit") +end + +timesofar("promotions") + @testset "utility functions" begin b1 = bitrand(v1) @test isequal(fill!(b1, true), trues(size(b1))) @@ -1767,4 +1781,4 @@ end @test all(bitarray[rangeout, rangein] .== true) @test all(bitarray[rangein, rangeout] .== true) end -end \ No newline at end of file +end diff --git a/test/broadcast.jl b/test/broadcast.jl index 5cddd0cb174f8..4b8217ea618ec 100644 --- a/test/broadcast.jl +++ b/test/broadcast.jl @@ -691,16 +691,19 @@ end @test a == [1 1; 2 2; 3 3] end -@testset "scalar .=" begin - A = [[1,2,3],4:5,6] +@testset "scalar .= and promotion" begin + A = [[1, 2, 3], 4:5, 6] + @test A isa Vector{Any} A[1] .= 0 - @test A[1] == [0,0,0] + @test A[1] == [0, 0, 0] @test_throws Base.CanonicalIndexError A[2] .= 0 @test_throws MethodError A[3] .= 0 - A = [[1,2,3],4:5] + A = [[1, 2, 3], 4:5] + @test A isa Vector{Vector{Int}} A[1] .= 0 - @test A[1] == [0,0,0] - @test_throws Base.CanonicalIndexError A[2] .= 0 + A[2] .= 0 + @test A[1] == [0, 0, 0] + @test A[2] == [0, 0] end # Issue #22180