Skip to content

Commit

Permalink
Add special case for Void and Missing in typejoin() to return Union{T…
Browse files Browse the repository at this point in the history
…, Void/Missing}

This allows for a more efficient code and array layout than using Any.
  • Loading branch information
nalimilan committed Dec 8, 2017
1 parent 6eec805 commit 1c0e5b7
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 7 deletions.
23 changes: 16 additions & 7 deletions base/promotion.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

## 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)
Expand All @@ -24,9 +31,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)
elseif isa(a,Union) && !isspecialpair(a.a, a.b)
return typejoin(typejoin(a.a,a.b), b)
elseif isa(b,Union)
elseif isa(b,Union) && !isspecialpair(b.a, b.b)
return typejoin(a, typejoin(b.a,b.b))
elseif a <: Tuple
if !(b <: Tuple)
Expand Down Expand Up @@ -75,9 +82,10 @@ function typejoin(@nospecialize(a), @nospecialize(b))
elseif b <: Tuple
return Any
end
while b !== Any
if a <: b.name.wrapper
while a.name !== b.name
b2 = b
while b2 !== Any && a isa DataType && b2 isa DataType
if a <: b2.name.wrapper
while a.name !== b2.name
a = supertype(a)
end
aprimary = unwrap_unionall(a.name.wrapper)
Expand All @@ -88,7 +96,7 @@ function typejoin(@nospecialize(a), @nospecialize(b))
end
p = Vector{Any}(uninitialized, n)
for i = 1:n
ai, bi = a.parameters[i], b.parameters[i]
ai, bi = a.parameters[i], b2.parameters[i]
if ai === bi || (isa(ai,Type) && isa(bi,Type) && typeseq(ai,bi))
p[i] = ai
else
Expand All @@ -97,8 +105,9 @@ function typejoin(@nospecialize(a), @nospecialize(b))
end
return rewrap_unionall(a.name.wrapper{p...}, a.name.wrapper)
end
b = supertype(b)
b2 = supertype(b2)
end
isspecialpair(a, b) && return Union{a, b}
return Any
end

Expand Down
3 changes: 3 additions & 0 deletions test/arrayops.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1212,6 +1212,9 @@ end
@test isequal([1,2,3], [a for (a,b) in enumerate(2:4)])
@test isequal([2,3,4], [b for (a,b) in enumerate(2:4)])

@test [s for s in Union{String, Void}["a", nothing]] isa Vector{Union{String, Void}}
@test [s for s in Union{String, Missing}["a", missing]] isa Vector{Union{String, Missing}}

@testset "comprehension in let-bound function" begin
let xy = sum([x[i]*y[i] for i=1:length(x)])
@test [1,2] [3,4] == 11
Expand Down
13 changes: 13 additions & 0 deletions test/core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,19 @@ 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
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
end

@test promote_type(Bool,Bottom) === Bool

# type declarations
Expand Down
38 changes: 38 additions & 0 deletions test/functional.jl
Original file line number Diff line number Diff line change
Expand Up @@ -147,3 +147,41 @@ end
for n = 0:5:100-q-d
for p = 100-q-d-n
if p < n < d < q] == [(50,30,15,5), (50,30,20,0), (50,40,10,0), (75,20,5,0)]

@testset "map/collect return type on generators with $T" for (T, v) in ((Void, nothing),
(Missing, missing))
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}

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"
res = collect(s for s in y)
@test res isa Vector{Union{String, T}}
res = map(identity, y)
@test res isa Vector{Union{String, T}}
res = @inferred collect(s === v for s in y)
@test res isa Vector{Bool}
res = @inferred map(s -> s === v, y)
@test res isa Vector{Bool}
res = collect(f(s) for s in y)
@test res isa Vector{Union{Bool, T}}
res = map(f, y)
@test res isa Vector{Union{Bool, T}}
end

0 comments on commit 1c0e5b7

Please sign in to comment.