diff --git a/base/missing.jl b/base/missing.jl index 403a1092a63fe..337e7c0da6fd3 100644 --- a/base/missing.jl +++ b/base/missing.jl @@ -69,6 +69,33 @@ convert(::Type{T}, x::T) where {T>:Union{Missing, Nothing}} = x convert(::Type{T}, x) where {T>:Missing} = convert(nonmissingtype_checked(T), x) convert(::Type{T}, x) where {T>:Union{Missing, Nothing}} = convert(nonmissingtype_checked(nonnothingtype_checked(T)), x) +for Cs in ((:Missing,), (:Nothing,), (:Missing, :Nothing)) + @eval function Base.reinterpret( + ::Type{<:T}, # Note this is effectively same as Type{T} since will error if T is abstract + xs::Array{Union{T, $(Cs...)},N} + ) where {T,N} + isbitstype(T) || throw(ArgumentError("cannot reinterpret `$(eltype(xs))`, type `$(T)` is not a bits type")) + return unsafe_wrap(Array{T,N}, Ptr{T}(pointer(xs)), length(xs)) + end + + #== + @eval function Base.convert( + ::Type{Array{Q,N}}, + xs::Array{Union{T, $(Cs...)},N} + ) where {N} where {T<:Q} where Q + + all(x->x isa T, xs) || throw(ArgumentError("cannot convert $(eltype(xs)) to $T")) + if isbitstype(T) && T===Q + reinterpret(T, xs) + else + Q[x for x in xs] + end + end + ==# +end +# Resolve ambiguity +#Base.convert(::Type{Array{Any,1}}, xs::Array{Any,1}) = xs + # Comparison operators ==(::Missing, ::Missing) = missing diff --git a/test/missing.jl b/test/missing.jl index 850482585b2cd..7cac897625f9b 100644 --- a/test/missing.jl +++ b/test/missing.jl @@ -23,6 +23,48 @@ end @test_throws MethodError convert(Union{Int, Missing}, "a") end +mutable struct NotABitsType a end +==(n1::NotABitsType,n2::NotABitsType) = n1.a == n2.a + +@testset "convert/reinterpret Arrays of Unions" begin + unions(T) = (Union{T, Missing}, Union{T, Nothing}, Union{T, Missing, Nothing}) + corrupters(::Type{T}) where T = (x for x in (missing, nothing) if x isa T) + + @testset "bits type ($U)" for U in unions(Int) + xo = U[1, 2, 3] + xr = reinterpret(Int, xo) + #xc = convert(Vector{Int}, xo) + #@test xc isa Vector{Int} + @test xr isa Vector{Int} + @test xr == xo + #@test xc == xo + # should not have allocated new memory + #@test pointer(xc) == pointer(xo) + @test pointer(xr) == pointer(xo) + + @test_throws ArgumentError reinterpret(Integer, xo) + + @testset "With non-target type values" begin + yo = U[1, 2, 3, corrupters(U)...] + #@test_throws ArgumentError convert(Vector{Int}, yo) + yr = reinterpret(Int, yo) + @test yr isa Vector{Int} + # No comment on what happens for the nonInt values, unspecified behavour + @test yr[1:3] == yo[1:3] + @test length(yo) == length(yr) + end + end + @testset "non-bits type ($U)" for U in unions(NotABitsType) + xo = U[NotABitsType(1), NotABitsType(2), NotABitsType(3)] + @test_throws ArgumentError reinterpret(Int, xo) + #xc = convert(Vector{NotABitsType}, x) + #@test xc isa Vector{Int} + #@test xc == xo + # should have allocated new memory + #@test pointer(xc) != pointer(xo) + end +end + @testset "promote rules" begin @test promote_type(Missing, Missing) == Missing @test promote_type(Missing, Int) == Union{Missing, Int}