Skip to content

Commit

Permalink
Introduce promote_join() and use it instead of typejoin() where appro…
Browse files Browse the repository at this point in the history
…priate

typejoin() is now used only for inference, and promote_join() everywhere else
to choose the appropriate element type for a collection.
  • Loading branch information
nalimilan committed Dec 8, 2017
1 parent 1c0e5b7 commit 9094694
Show file tree
Hide file tree
Showing 10 changed files with 51 additions and 48 deletions.
4 changes: 2 additions & 2 deletions base/array.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions base/broadcast.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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__
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion base/dict.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion base/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
47 changes: 30 additions & 17 deletions base/promotion.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion base/range.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions base/set.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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...)
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions base/tuple.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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))
= unwrap_unionall(t)
r = Union{}
for ti in.parameters
r = typejoin(r, rewrap_unionall(unwrapva(ti), t))
r = promote_join(r, rewrap_unionall(unwrapva(ti), t))
end
return r
end
Expand Down
20 changes: 10 additions & 10 deletions test/core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 0 additions & 10 deletions test/functional.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down

0 comments on commit 9094694

Please sign in to comment.