From 1c0e5b78c59e199edfc5212a9550d1873098c049 Mon Sep 17 00:00:00 2001 From: Milan Bouchet-Valat Date: Wed, 25 Oct 2017 19:46:55 +0200 Subject: [PATCH] Add special case for Void and Missing in typejoin() to return Union{T, Void/Missing} This allows for a more efficient code and array layout than using Any. --- base/promotion.jl | 23 ++++++++++++++++------- test/arrayops.jl | 3 +++ test/core.jl | 13 +++++++++++++ test/functional.jl | 38 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 70 insertions(+), 7 deletions(-) diff --git a/base/promotion.jl b/base/promotion.jl index 9c2aa4c9eb78f..89a479d8a5af7 100644 --- a/base/promotion.jl +++ b/base/promotion.jl @@ -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) @@ -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) @@ -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) @@ -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 @@ -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 diff --git a/test/arrayops.jl b/test/arrayops.jl index 7d6e178c73471..0d8ab42983271 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -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 x⊙y = sum([x[i]*y[i] for i=1:length(x)]) @test [1,2] ⊙ [3,4] == 11 diff --git a/test/core.jl b/test/core.jl index 254ccdd38afb8..7ccd05692043c 100644 --- a/test/core.jl +++ b/test/core.jl @@ -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 diff --git a/test/functional.jl b/test/functional.jl index 7b15585cbc59d..a0b4026049495 100644 --- a/test/functional.jl +++ b/test/functional.jl @@ -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 \ No newline at end of file