Skip to content

Commit

Permalink
implement promote for BitArray (#44096)
Browse files Browse the repository at this point in the history
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 <jakobsachs1999@gmail.com>
  • Loading branch information
vtjnash and JakobSachs committed Mar 1, 2022
1 parent c6e3146 commit 5cac5b9
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 18 deletions.
20 changes: 13 additions & 7 deletions base/indices.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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,)
Expand All @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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} =
Expand All @@ -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()
Expand Down
4 changes: 2 additions & 2 deletions base/promotion.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
8 changes: 6 additions & 2 deletions base/range.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 15 additions & 1 deletion test/bitarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)))
Expand Down Expand Up @@ -1767,4 +1781,4 @@ end
@test all(bitarray[rangeout, rangein] .== true)
@test all(bitarray[rangein, rangeout] .== true)
end
end
end
15 changes: 9 additions & 6 deletions test/broadcast.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 5cac5b9

Please sign in to comment.