Skip to content

Commit

Permalink
ensure promotion rules don't alter eltype values
Browse files Browse the repository at this point in the history
This also helps to re-synchronize `cat` and `map`

Closes #25924
  • Loading branch information
vtjnash committed Feb 19, 2018
1 parent 3a47cd4 commit 4a20236
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 31 deletions.
3 changes: 0 additions & 3 deletions base/namedtuple.jl
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,6 @@ indexed_next(t::NamedTuple, i::Int, state) = (getfield(t, i), i+1)
isempty(::NamedTuple{()}) = true
isempty(::NamedTuple) = false

promote_typejoin(::Type{NamedTuple{n, S}}, ::Type{NamedTuple{n, T}}) where {n, S, T} =
NamedTuple{n, promote_typejoin(S, T)}

convert(::Type{NamedTuple{names,T}}, nt::NamedTuple{names,T}) where {names,T<:Tuple} = nt
convert(::Type{NamedTuple{names}}, nt::NamedTuple{names}) where {names} = nt

Expand Down
44 changes: 21 additions & 23 deletions base/promotion.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,59 +12,58 @@ they both inherit.
typejoin() = (@_pure_meta; Bottom)
typejoin(@nospecialize(t)) = (@_pure_meta; t)
typejoin(@nospecialize(t), ts...) = (@_pure_meta; typejoin(t, typejoin(ts...)))
typejoin(@nospecialize(a), @nospecialize(b)) = (@_pure_meta; join_types(a, b, typejoin))

function join_types(@nospecialize(a), @nospecialize(b), f::Function)
function typejoin(@nospecialize(a), @nospecialize(b))
@_pure_meta
if a <: b
return b
elseif b <: a
return a
elseif isa(a,UnionAll)
return UnionAll(a.var, join_types(a.body, b, f))
return UnionAll(a.var, typejoin(a.body, b))
elseif isa(b,UnionAll)
return UnionAll(b.var, join_types(a, b.body, f))
return UnionAll(b.var, typejoin(a, b.body))
elseif isa(a,TypeVar)
return f(a.ub, b)
return typejoin(a.ub, b)
elseif isa(b,TypeVar)
return f(a, b.ub)
return typejoin(a, b.ub)
elseif isa(a,Union)
a′ = f(a.a,a.b)
return a′ === a ? typejoin(a, b) : f(a′, b)
a′ = typejoin(a.a, a.b)
return a′ === a ? typejoin(a, b) : typejoin(a′, b)
elseif isa(b,Union)
b′ = f(b.a,b.b)
return b′ === b ? typejoin(a, b) : f(a, b′)
b′ = typejoin(b.a, b.b)
return b′ === b ? typejoin(a, b) : typejoin(a, b′)
elseif a <: Tuple
if !(b <: Tuple)
return Any
end
ap, bp = a.parameters, b.parameters
lar = length(ap)::Int; lbr = length(bp)::Int
if lar == 0
return Tuple{Vararg{tailjoin(bp,1,f)}}
return Tuple{Vararg{tailjoin(bp, 1)}}
end
if lbr == 0
return Tuple{Vararg{tailjoin(ap,1,f)}}
return Tuple{Vararg{tailjoin(ap, 1)}}
end
laf, afixed = full_va_len(ap)
lbf, bfixed = full_va_len(bp)
if laf < lbf
if isvarargtype(ap[lar]) && !afixed
c = Vector{Any}(uninitialized, laf)
c[laf] = Vararg{f(unwrapva(ap[lar]), tailjoin(bp,laf,f))}
c[laf] = Vararg{typejoin(unwrapva(ap[lar]), tailjoin(bp, laf))}
n = laf-1
else
c = Vector{Any}(uninitialized, laf+1)
c[laf+1] = Vararg{tailjoin(bp,laf+1,f)}
c[laf+1] = Vararg{tailjoin(bp, laf+1)}
n = laf
end
elseif lbf < laf
if isvarargtype(bp[lbr]) && !bfixed
c = Vector{Any}(uninitialized, lbf)
c[lbf] = Vararg{f(unwrapva(bp[lbr]), tailjoin(ap,lbf,f))}
c[lbf] = Vararg{typejoin(unwrapva(bp[lbr]), tailjoin(ap, lbf))}
n = lbf-1
else
c = Vector{Any}(uninitialized, lbf+1)
c[lbf+1] = Vararg{tailjoin(ap,lbf+1,f)}
c[lbf+1] = Vararg{tailjoin(ap, lbf+1)}
n = lbf
end
else
Expand All @@ -73,7 +72,7 @@ function join_types(@nospecialize(a), @nospecialize(b), f::Function)
end
for i = 1:n
ai = ap[min(i,lar)]; bi = bp[min(i,lbr)]
ci = f(unwrapva(ai),unwrapva(bi))
ci = typejoin(unwrapva(ai), unwrapva(bi))
c[i] = i == length(c) && (isvarargtype(ai) || isvarargtype(bi)) ? Vararg{ci} : ci
end
return Tuple{c...}
Expand Down Expand Up @@ -114,8 +113,7 @@ 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_typejoin(@nospecialize(a), @nospecialize(b)) =
(@_pure_meta; join_types(a, b, promote_typejoin))
promote_typejoin(@nospecialize(a), @nospecialize(b)) = typejoin(a, b)
promote_typejoin(::Type{Nothing}, ::Type{T}) where {T} =
isconcretetype(T) || T === Union{} ? Union{T, Nothing} : Any
promote_typejoin(::Type{T}, ::Type{Nothing}) where {T} =
Expand Down Expand Up @@ -143,14 +141,14 @@ function full_va_len(p)
return length(p)::Int, true
end

# reduce join_types over A[i:end]
function tailjoin(A, i, f::Function)
# reduce typejoin over A[i:end]
function tailjoin(A, i)
if i > length(A)
return unwrapva(A[end])
end
t = Bottom
for j = i:length(A)
t = f(t, unwrapva(A[j]))
t = typejoin(t, unwrapva(A[j]))
end
return t
end
Expand Down
4 changes: 2 additions & 2 deletions test/namedtuple.jl
Original file line number Diff line number Diff line change
Expand Up @@ -226,9 +226,9 @@ abstr_nt_22194_3()
for T in (Nothing, Missing)
x = [(a=1, b=T()), (a=1, b=2)]
y = map(v -> (a=v.a, b=v.b), [(a=1, b=T()), (a=1, b=2)])
@test y isa Vector{NamedTuple{(:a,:b),Tuple{Int,Union{T,Int}}}}
@test y isa Vector{NamedTuple{(:a,:b), T} where T<:Tuple}
@test isequal(x, y)
end
y = map(v -> (a=v.a, b=v.a + v.b), [(a=1, b=missing), (a=1, b=2)])
@test y isa Vector{NamedTuple{(:a,:b),Tuple{Int,Union{Missing,Int}}}}
@test y isa Vector{NamedTuple{(:a,:b), T} where T<:Tuple}
@test isequal(y, [(a=1, b=missing), (a=1, b=3)])
6 changes: 3 additions & 3 deletions test/tuple.jl
Original file line number Diff line number Diff line change
Expand Up @@ -189,11 +189,11 @@ end
for T in (Nothing, Missing)
x = [(1, T()), (1, 2)]
y = map(v -> (v[1], v[2]), [(1, T()), (1, 2)])
@test y isa Vector{Tuple{Int,Union{T,Int}}}
@test y isa Vector{Tuple{Int, Any}}
@test isequal(x, y)
end
y = map(v -> (v[1], v[1] + v[2]), [(1, missing), (1, 2)])
@test y isa Vector{Tuple{Int,Union{Missing,Int}}}
@test y isa Vector{Tuple{Int, Any}}
@test isequal(y, [(1, missing), (1, 3)])
end

Expand Down Expand Up @@ -425,4 +425,4 @@ end
@test findprev(equalto(1), (1, 1), 1) == 1
@test findnext(equalto(1), (2, 3), 1) === nothing
@test findprev(equalto(1), (2, 3), 2) === nothing
end
end

0 comments on commit 4a20236

Please sign in to comment.