From 9094694b983a155f1cd9b65236127de5fa04a9df Mon Sep 17 00:00:00 2001 From: Milan Bouchet-Valat Date: Fri, 8 Dec 2017 19:16:13 +0100 Subject: [PATCH] Introduce promote_join() and use it instead of typejoin() where appropriate typejoin() is now used only for inference, and promote_join() everywhere else to choose the appropriate element type for a collection. --- base/array.jl | 4 ++-- base/broadcast.jl | 4 ++-- base/dict.jl | 2 +- base/inference.jl | 2 +- base/promotion.jl | 47 +++++++++++++++++++++++++++++----------------- base/range.jl | 2 +- base/set.jl | 4 ++-- base/tuple.jl | 4 ++-- test/core.jl | 20 ++++++++++---------- test/functional.jl | 10 ---------- 10 files changed, 51 insertions(+), 48 deletions(-) diff --git a/base/array.jl b/base/array.jl index 083e4a4323f7f..22c6b272005ef 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 7168971c2ac31..384a5bddc0954 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -5,7 +5,7 @@ module Broadcast using Base.Cartesian using Base: Indices, OneTo, linearindices, tail, to_shape, _msk_end, unsafe_bitgetindex, bitcache_chunks, bitcache_size, dumpbitcache, - nullable_returntype, null_safe_op, hasvalue, isoperator + nullable_returntype, null_safe_op, hasvalue, isoperator, promote_join import Base: broadcast, broadcast! export BroadcastStyle, broadcast_indices, broadcast_similar, broadcast_getindex, broadcast_setindex!, dotview, @__dot__ @@ -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 bd3ac41287e55..ca66d780a182f 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 6b5f867eabf7f..2df71055660aa 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 89a479d8a5af7..40881e72fd76a 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,32 @@ 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} +promote_join(::Type{Void}, ::Type{Void}) = Void +promote_join(::Type{Missing}, ::Type{Missing}) = 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 3615a99a2d257..2169f0b88f8ae 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 dea43bc4b665f..79381a18c8bb4 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 2b94d1d946e44..7bb22c0cf2791 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 7ccd05692043c..5f486d62d2865 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 diff --git a/test/functional.jl b/test/functional.jl index a0b4026049495..97076129f66b6 100644 --- a/test/functional.jl +++ b/test/functional.jl @@ -159,16 +159,6 @@ end @test res isa Vector{Bool} res = @inferred map(s -> s === v, x) @test res isa Vector{Bool} - - x = ["a", "b"] - res = @inferred collect(s for s in x) - @test res isa Vector{String} - res = @inferred map(identity, x) - @test res isa Vector{String} - res = @inferred collect(s === v for s in x) - @test res isa Vector{Bool} - res = @inferred map(s -> s === v, x) - @test res isa Vector{Bool} y = Union{String, T}["a", v] f(s::Union{Void, Missing}) = s f(s::String) = s == "a"