diff --git a/base/array.jl b/base/array.jl index 083e4a4323f7f6..22c6b272005efb 100644 --- a/base/array.jl +++ b/base/array.jl @@ -561,7 +561,7 @@ function collect_to!(dest::AbstractArray{T}, itr, offs, st) where T @inbounds dest[i] = el::T i += 1 else - R = typejoin(T, S) + R = promote_join(T, S) new = similar(dest, R) copy!(new,1, dest,1, i-1) @inbounds new[i] = el @@ -584,7 +584,7 @@ function grow_to!(dest, itr, st) if S === T || S <: T push!(dest, el::T) else - new = similar(dest, typejoin(T, S)) + new = similar(dest, promote_join(T, S)) copy!(new, dest) push!(new, el) return grow_to!(new, itr, st) diff --git a/base/broadcast.jl b/base/broadcast.jl index 7168971c2ac316..ddf51d4e523e09 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -487,7 +487,7 @@ end else # This element type doesn't fit in B. Allocate a new B with wider eltype, # copy over old values, and continue - newB = Base.similar(B, typejoin(eltype(B), S)) + newB = Base.similar(B, promote_join(eltype(B), S)) for II in Iterators.take(iter, count) newB[II] = B[II] end diff --git a/base/dict.jl b/base/dict.jl index bd3ac41287e550..ca66d780a182ff 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -179,7 +179,7 @@ function grow_to!(dest::Associative{K,V}, itr, st) where V where K if isa(k,K) && isa(v,V) dest[k] = v else - new = empty(dest, typejoin(K,typeof(k)), typejoin(V,typeof(v))) + new = empty(dest, promote_join(K,typeof(k)), promote_join(V,typeof(v))) copy!(new, dest) new[k] = v return grow_to!(new, itr, st) diff --git a/base/inference.jl b/base/inference.jl index 6b5f867eabf7f1..2df71055660aa9 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -1833,7 +1833,7 @@ function limit_tuple_type_n(@nospecialize(t), lim::Int) p = t.parameters n = length(p) if n > lim - tail = reduce(typejoin, Bottom, Any[p[lim:(n-1)]..., unwrapva(p[n])]) + tail = reduce(promote_join, Bottom, Any[p[lim:(n-1)]..., unwrapva(p[n])]) return Tuple{p[1:(lim-1)]..., Vararg{tail}} end return t diff --git a/base/promotion.jl b/base/promotion.jl index 89a479d8a5af70..64ceee529f285b 100644 --- a/base/promotion.jl +++ b/base/promotion.jl @@ -2,17 +2,11 @@ ## type join (closest common ancestor, or least upper bound) ## -# Note: no methods must be added to this function after `typejoin` has been defined, -# since it is declared as `@pure` -isspecial(::Type) = false -isspecial(::Type{Void}) = true -isspecial(::Type{Missing}) = true -isspecialpair(a, b) = isconcrete(a) && isconcrete(b) && (isspecial(a) || isspecial(b)) - """ typejoin(T, S) -Compute a type that contains both `T` and `S`. +Return the closest common ancestor of `T` and `S`, i.e. a type which +contains both of them. """ typejoin() = (@_pure_meta; Bottom) typejoin(@nospecialize(t)) = (@_pure_meta; t) @@ -31,9 +25,9 @@ function typejoin(@nospecialize(a), @nospecialize(b)) return typejoin(a.ub, b) elseif isa(b,TypeVar) return typejoin(a, b.ub) - elseif isa(a,Union) && !isspecialpair(a.a, a.b) + elseif isa(a,Union) return typejoin(typejoin(a.a,a.b), b) - elseif isa(b,Union) && !isspecialpair(b.a, b.b) + elseif isa(b,Union) return typejoin(a, typejoin(b.a,b.b)) elseif a <: Tuple if !(b <: Tuple) @@ -82,10 +76,9 @@ function typejoin(@nospecialize(a), @nospecialize(b)) elseif b <: Tuple return Any end - b2 = b - while b2 !== Any && a isa DataType && b2 isa DataType - if a <: b2.name.wrapper - while a.name !== b2.name + while b !== Any + if a <: b.name.wrapper + while a.name !== b.name a = supertype(a) end aprimary = unwrap_unionall(a.name.wrapper) @@ -96,7 +89,7 @@ function typejoin(@nospecialize(a), @nospecialize(b)) end p = Vector{Any}(uninitialized, n) for i = 1:n - ai, bi = a.parameters[i], b2.parameters[i] + ai, bi = a.parameters[i], b.parameters[i] if ai === bi || (isa(ai,Type) && isa(bi,Type) && typeseq(ai,bi)) p[i] = ai else @@ -105,12 +98,30 @@ function typejoin(@nospecialize(a), @nospecialize(b)) end return rewrap_unionall(a.name.wrapper{p...}, a.name.wrapper) end - b2 = supertype(b2) + b = supertype(b) end - isspecialpair(a, b) && return Union{a, b} return Any end +""" + promote_join(T, S) + +Compute a type that contains both `T` and `S`, which could be +either a parent of both types, or a `Union` if appropriate. +Falls back to [`typejoin`](@ref). +""" +promote_join(@nospecialize(a), @nospecialize(b)) = typejoin(a, b) +promote_join(::Type{Void}, ::Type{T}) where {T} = + isconcrete(T) ? Union{T, Void} : Any +promote_join(::Type{T}, ::Type{Void}) where {T} = + isconcrete(T) ? Union{T, Void} : Any +promote_join(::Type{Missing}, ::Type{T}) where {T} = + isconcrete(T) ? Union{T, Missing} : Any +promote_join(::Type{T}, ::Type{Missing}) where {T} = + isconcrete(T) ? Union{T, Missing} : Any +promote_join(::Type{Void}, ::Type{Missing}) = Union{Void, Missing} +promote_join(::Type{Missing}, ::Type{Void}) = Union{Void, Missing} + # Returns length, isfixed function full_va_len(p) isempty(p) && return 0, true diff --git a/base/range.jl b/base/range.jl index 3615a99a2d257f..2169f0b88f8aee 100644 --- a/base/range.jl +++ b/base/range.jl @@ -807,7 +807,7 @@ broadcast(::typeof(\), x::Number, r::LinSpace) = LinSpace(x \ r.start, x \ 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{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) = typejoin(a, b) +el_same(::Type, a, b) = promote_join(a, b) promote_rule(a::Type{UnitRange{T1}}, b::Type{UnitRange{T2}}) where {T1,T2} = el_same(promote_type(T1,T2), a, b) diff --git a/base/set.jl b/base/set.jl index dea43bc4b665f3..79381a18c8bb4d 100644 --- a/base/set.jl +++ b/base/set.jl @@ -128,7 +128,7 @@ function union!(s::Set{T}, xs) where T end join_eltype() = Bottom -join_eltype(v1, vs...) = typejoin(eltype(v1), join_eltype(vs...)) +join_eltype(v1, vs...) = promote_join(eltype(v1), join_eltype(vs...)) """ intersect(s1,s2...) @@ -278,7 +278,7 @@ _unique_from(itr, out, seen, i) = unique_from(itr, out, seen, i) x, i = next(itr, i) S = typeof(x) if !(S === T || S <: T) - R = typejoin(S, T) + R = promote_join(S, T) seenR = convert(Set{R}, seen) outR = convert(Vector{R}, out) if !in(x, seenR) diff --git a/base/tuple.jl b/base/tuple.jl index 2b94d1d946e44e..7bb22c0cf27912 100644 --- a/base/tuple.jl +++ b/base/tuple.jl @@ -78,11 +78,11 @@ end eltype(t::Type{<:Tuple}) = _compute_eltype(t) function _compute_eltype(t::Type{<:Tuple}) @_pure_meta - t isa Union && return typejoin(eltype(t.a), eltype(t.b)) + t isa Union && return promote_join(eltype(t.a), eltype(t.b)) t´ = unwrap_unionall(t) r = Union{} for ti in t´.parameters - r = typejoin(r, rewrap_unionall(unwrapva(ti), t)) + r = promote_join(r, rewrap_unionall(unwrapva(ti), t)) end return r end diff --git a/test/core.jl b/test/core.jl index 7ccd05692043cc..5f486d62d28658 100644 --- a/test/core.jl +++ b/test/core.jl @@ -123,17 +123,17 @@ end @test typejoin(Tuple{Vararg{Int,2}}, Tuple{Int,Int,Int}) === Tuple{Int,Int,Vararg{Int}} @test typejoin(Tuple{Vararg{Int,2}}, Tuple{Vararg{Int}}) === Tuple{Vararg{Int}} -# typejoin returns a Union only with Void/Missing combined with concrete types +# promote_join returns a Union only with Void/Missing combined with concrete types for T in (Void, Missing) - @test typejoin(Int, Float64) === Real - @test typejoin(Int, T) === Union{Int, T} - @test typejoin(T, String) === Union{T, String} - @test typejoin(Vector{Int}, T) === Union{Vector{Int}, T} - @test typejoin(Vector, T) === Any - @test typejoin(Real, T) === Any - @test typejoin(Int, String) === Any - @test typejoin(Int, Union{Float64, T}) === Any - @test typejoin(Int, Union{String, T}) === Any + @test Base.promote_join(Int, Float64) === Real + @test Base.promote_join(Int, T) === Union{Int, T} + @test Base.promote_join(T, String) === Union{T, String} + @test Base.promote_join(Vector{Int}, T) === Union{Vector{Int}, T} + @test Base.promote_join(Vector, T) === Any + @test Base.promote_join(Real, T) === Any + @test Base.promote_join(Int, String) === Any + @test Base.promote_join(Int, Union{Float64, T}) === Any + @test Base.promote_join(Int, Union{String, T}) === Any end @test promote_type(Bool,Bottom) === Bool