Skip to content

Commit 1fb2e92

Browse files
JeffBezansonvtjnash
andcommitted
fewer convert methods for Missing and Nothing
And use `typesubtract` instead of subtyping to improve usability by handling undef sparams. Co-Authored-By: Jameson Nash <[email protected]>
1 parent f31b6e5 commit 1fb2e92

File tree

4 files changed

+57
-52
lines changed

4 files changed

+57
-52
lines changed

base/essentials.jl

+1
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ true
163163
"""
164164
function convert end
165165

166+
convert(::Type{Union{}}, x) = throw(MethodError(convert, (Union{}, x)))
166167
convert(::Type{Any}, @nospecialize(x)) = x
167168
convert(::Type{T}, x::T) where {T} = x
168169
convert(::Type{Type}, x::Type) = x # the ssair optimizer is strongly dependent on this method existing to avoid over-specialization

base/missing.jl

+36-32
Original file line numberDiff line numberDiff line change
@@ -19,35 +19,39 @@ showerror(io::IO, ex::MissingException) =
1919
print(io, "MissingException: ", ex.msg)
2020

2121
nonmissingtype(::Type{T}) where {T} = Core.Compiler.typesubtract(T, Missing)
22+
function nonmissingtype_checked(T::Type)
23+
R = nonmissingtype(T)
24+
R >: T && throw(MissingException("could not compute non-missing type"))
25+
return R
26+
end
2227

23-
for U in (:Nothing, :Missing)
24-
@eval begin
25-
promote_rule(::Type{$U}, ::Type{T}) where {T} = Union{T, $U}
26-
promote_rule(::Type{Union{S,$U}}, ::Type{Any}) where {S} = Any
27-
promote_rule(::Type{Union{S,$U}}, ::Type{T}) where {T,S} = Union{promote_type(T, S), $U}
28-
promote_rule(::Type{Any}, ::Type{$U}) = Any
29-
promote_rule(::Type{$U}, ::Type{Any}) = Any
30-
# This definition is never actually used, but disambiguates the above definitions
31-
promote_rule(::Type{$U}, ::Type{$U}) = $U
32-
end
28+
promote_rule(T::Type{Missing}, S::Type) = Union{S, Missing}
29+
promote_rule(T::Type{Missing}, S::Type{Any}) = Any
30+
promote_rule(T::Type{Union{Nothing, Missing}}, S::Type{Any}) = Any
31+
promote_rule(T::Type{Union{Nothing, Missing}}, S::Type) = Union{S, Nothing, Missing}
32+
promote_rule(T::Type{>:Union{Nothing, Missing}}, S::Type{Any}) = Any
33+
function promote_rule(T::Type{>:Union{Nothing, Missing}}, S::Type)
34+
R = nonnothingtype(T)
35+
R >: T && return Any
36+
T = R
37+
R = nonmissingtype(T)
38+
R >: T && return Any
39+
T = R
40+
R = promote_type(T, S)
41+
return Union{R, Nothing, Missing}
3342
end
34-
promote_rule(::Type{Union{Nothing, Missing}}, ::Type{Any}) = Any
35-
promote_rule(::Type{Union{Nothing, Missing}}, ::Type{T}) where {T} =
36-
Union{Nothing, Missing, T}
37-
promote_rule(::Type{Union{Nothing, Missing, S}}, ::Type{Any}) where {S} = Any
38-
promote_rule(::Type{Union{Nothing, Missing, S}}, ::Type{T}) where {T,S} =
39-
Union{Nothing, Missing, promote_type(T, S)}
40-
41-
convert(::Type{Union{T, Missing}}, x::Union{T, Missing}) where {T} = x
42-
convert(::Type{Union{T, Missing}}, x) where {T} = convert(T, x)
43-
# To fix ambiguities
44-
convert(::Type{Missing}, ::Missing) = missing
45-
convert(::Type{Union{Nothing, Missing}}, x::Union{Nothing, Missing}) = x
46-
convert(::Type{Union{Nothing, Missing, T}}, x::Union{Nothing, Missing, T}) where {T} = x
47-
convert(::Type{Union{Nothing, Missing}}, x) =
48-
throw(MethodError(convert, (Union{Nothing, Missing}, x)))
49-
# To print more appropriate message than "T not defined"
50-
convert(::Type{Missing}, x) = throw(MethodError(convert, (Missing, x)))
43+
promote_rule(T::Type{>:Missing}, S::Type{Any}) = Any
44+
function promote_rule(T::Type{>:Missing}, S::Type)
45+
R = nonmissingtype(T)
46+
R >: T && return Any
47+
T = R
48+
R = promote_type(T, S)
49+
return Union{R, Missing}
50+
end
51+
52+
convert(T::Type{>:Union{Missing, Nothing}}, x) = convert(nonmissingtype_checked(nonnothingtype_checked(T)), x)
53+
convert(T::Type{>:Missing}, x) = convert(nonmissingtype_checked(T), x)
54+
5155

5256
# Comparison operators
5357
==(::Missing, ::Missing) = missing
@@ -108,10 +112,10 @@ round(::Missing, ::RoundingMode=RoundNearest; sigdigits::Integer=0, digits::Inte
108112
round(::Type{>:Missing}, ::Missing, ::RoundingMode=RoundNearest) = missing
109113
round(::Type{T}, ::Missing, ::RoundingMode=RoundNearest) where {T} =
110114
throw(MissingException("cannot convert a missing value to type $T: use Union{$T, Missing} instead"))
111-
round(::Type{T}, x::Any, r::RoundingMode=RoundNearest) where {T>:Missing} = round(nonmissingtype(T), x, r)
115+
round(::Type{T}, x::Any, r::RoundingMode=RoundNearest) where {T>:Missing} = round(nonmissingtype_checked(T), x, r)
112116
# to fix ambiguities
113-
round(::Type{T}, x::Rational, r::RoundingMode=RoundNearest) where {T>:Missing} = round(nonmissingtype(T), x, r)
114-
round(::Type{T}, x::Rational{Bool}, r::RoundingMode=RoundNearest) where {T>:Missing} = round(nonmissingtype(T), x, r)
117+
round(::Type{T}, x::Rational, r::RoundingMode=RoundNearest) where {T>:Missing} = round(nonmissingtype_checked(T), x, r)
118+
round(::Type{T}, x::Rational{Bool}, r::RoundingMode=RoundNearest) where {T>:Missing} = round(nonmissingtype_checked(T), x, r)
115119

116120
# Handle ceil, floor, and trunc separately as they have no RoundingMode argument
117121
for f in (:(ceil), :(floor), :(trunc))
@@ -120,9 +124,9 @@ for f in (:(ceil), :(floor), :(trunc))
120124
($f)(::Type{>:Missing}, ::Missing) = missing
121125
($f)(::Type{T}, ::Missing) where {T} =
122126
throw(MissingException("cannot convert a missing value to type $T: use Union{$T, Missing} instead"))
123-
($f)(::Type{T}, x::Any) where {T>:Missing} = $f(nonmissingtype(T), x)
127+
($f)(::Type{T}, x::Any) where {T>:Missing} = $f(nonmissingtype_checked(T), x)
124128
# to fix ambiguities
125-
($f)(::Type{T}, x::Rational) where {T>:Missing} = $f(nonmissingtype(T), x)
129+
($f)(::Type{T}, x::Rational) where {T>:Missing} = $f(nonmissingtype_checked(T), x)
126130
end
127131
end
128132

base/some.jl

+20-8
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,28 @@ struct Some{T}
1313
end
1414

1515
promote_rule(::Type{Some{T}}, ::Type{Some{S}}) where {T, S<:T} = Some{T}
16-
promote_rule(::Type{Some{T}}, ::Type{Nothing}) where {T} = Union{Some{T}, Nothing}
1716

18-
convert(::Type{Some{T}}, x::Some) where {T} = Some{T}(convert(T, x.value))
19-
convert(::Type{Some{T}}, x::Some{T}) where {T} = x
20-
convert(::Type{Union{Some{T}, Nothing}}, x::Some) where {T} = convert(Some{T}, x)
17+
nonnothingtype(::Type{T}) where {T} = Core.Compiler.typesubtract(T, Nothing)
18+
promote_rule(T::Type{Nothing}, S::Type) = Union{S, Nothing}
19+
promote_rule(T::Type{Nothing}, S::Type{Any}) = Any
20+
promote_rule(T::Type{>:Nothing}, S::Type{Any}) = Any
21+
function promote_rule(T::Type{>:Nothing}, S::Type)
22+
R = nonnothingtype(T)
23+
R >: T && return Any
24+
T = R
25+
R = promote_type(T, S)
26+
return Union{R, Nothing}
27+
end
2128

22-
convert(::Type{Union{T, Nothing}}, x::Union{T, Nothing}) where {T} = x
23-
convert(::Type{Union{T, Nothing}}, x::Any) where {T} = convert(T, x)
24-
convert(::Type{Nothing}, x::Nothing) = nothing
25-
convert(::Type{Nothing}, x::Any) = throw(MethodError(convert, (Nothing, x)))
29+
function nonnothingtype_checked(T::Type)
30+
R = nonnothingtype(T)
31+
R >: T && throw(MissingException("could not compute non-nullable type"))
32+
return R
33+
end
34+
35+
convert(T::Type{>:Nothing}, x) = convert(nonnothingtype_checked(T), x)
36+
convert(::Type{Some{T}}, x::Some{T}) where {T} = x
37+
convert(::Type{Some{T}}, x::Some) where {T} = Some{T}(convert(T, x.value))
2638

2739
function show(io::IO, x::Some)
2840
if get(io, :typeinfo, Any) == typeof(x)

test/ambiguous.jl

-12
Original file line numberDiff line numberDiff line change
@@ -283,23 +283,11 @@ end
283283
pop!(need_to_handle_undef_sparam, which(Base.byteenv, (Union{AbstractArray{Pair{T}, 1}, Tuple{Vararg{Pair{T}}}} where T<:AbstractString,)))
284284
pop!(need_to_handle_undef_sparam, which(Base._cat, (Any, SparseArrays._TypedDenseConcatGroup{T} where T)))
285285
pop!(need_to_handle_undef_sparam, which(Base.float, Tuple{AbstractArray{Union{Missing, T},N} where {T, N}}))
286-
pop!(need_to_handle_undef_sparam, which(Base.convert, Tuple{Type{Union{Missing, T}} where T, Any}))
287-
pop!(need_to_handle_undef_sparam, which(Base.promote_rule, Tuple{Type{Union{Nothing, S}} where S, Type{T} where T}))
288-
pop!(need_to_handle_undef_sparam, which(Base.promote_rule, Tuple{Type{Union{Missing, S}} where S, Type{T} where T}))
289-
pop!(need_to_handle_undef_sparam, which(Base.promote_rule, Tuple{Type{Union{Missing, Nothing, S}} where S, Type{T} where T}))
290286
pop!(need_to_handle_undef_sparam, which(Base.zero, Tuple{Type{Union{Missing, T}} where T}))
291287
pop!(need_to_handle_undef_sparam, which(Base.one, Tuple{Type{Union{Missing, T}} where T}))
292288
pop!(need_to_handle_undef_sparam, which(Base.oneunit, Tuple{Type{Union{Missing, T}} where T}))
293-
pop!(need_to_handle_undef_sparam, which(Base.convert, (Type{Union{Some{T}, Nothing}} where T, Some)))
294-
pop!(need_to_handle_undef_sparam, which(Base.convert, (Type{Union{T, Nothing}} where T, Some)))
295289
pop!(need_to_handle_undef_sparam, which(Base.convert, Tuple{Type{Tuple{Vararg{Int}}}, Tuple{}}))
296290
pop!(need_to_handle_undef_sparam, which(Base.convert, Tuple{Type{Tuple{Vararg{Int}}}, Tuple{Int8}}))
297-
pop!(need_to_handle_undef_sparam, which(Base.convert, Tuple{Type{Union{Nothing,T}},Union{Nothing,T}} where T))
298-
pop!(need_to_handle_undef_sparam, which(Base.convert, Tuple{Type{Union{Missing,T}},Union{Missing,T}} where T))
299-
pop!(need_to_handle_undef_sparam, which(Base.convert, Tuple{Type{Union{Missing,Nothing,T}},Union{Missing,Nothing,T}} where T))
300-
pop!(need_to_handle_undef_sparam, which(Base.promote_rule, Tuple{Type{Union{Nothing,T}},Type{Any}} where T))
301-
pop!(need_to_handle_undef_sparam, which(Base.promote_rule, Tuple{Type{Union{Missing,T}},Type{Any}} where T))
302-
pop!(need_to_handle_undef_sparam, which(Base.promote_rule, Tuple{Type{Union{Missing,Nothing,T}},Type{Any}} where T))
303291
@test need_to_handle_undef_sparam == Set()
304292
end
305293
end

0 commit comments

Comments
 (0)