Skip to content

Commit

Permalink
Merge pull request #49349 from JuliaLang/jn/morespecific-bottom
Browse files Browse the repository at this point in the history
add morespecific rule for Type{Union{}}
  • Loading branch information
vtjnash authored Apr 20, 2023
2 parents ca50706 + 285c770 commit d8fb5e7
Show file tree
Hide file tree
Showing 36 changed files with 369 additions and 157 deletions.
6 changes: 6 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ Language changes
----------------

* When a task forks a child, the parent task's task-local RNG (random number generator) is no longer affected. The seeding of child based on the parent task also takes a more disciplined approach to collision resistance, using a design based on the SplitMix and DotMix splittable RNG schemes ([#49110]).
* A new morespecific rule for methods resolves ambiguities containing Union{} in favor of
the method defined explicitly to handle the Union{} argument. This makes it possible to
define methods to explicitly handle Union{} without the ambiguities that commonly would
result previously. This also lets the runtime optimize certain method lookups in a way
that significantly improves load and inference times for heavily overloaded methods that
dispatch on Types (such as traits and constructors).

Compiler/Runtime improvements
-----------------------------
Expand Down
5 changes: 4 additions & 1 deletion base/abstractarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -183,11 +183,13 @@ CartesianIndex{2}
For arrays, this function requires at least Julia 1.2.
"""
keytype(a::AbstractArray) = keytype(typeof(a))
keytype(::Type{Union{}}, slurp...) = eltype(Union{})

keytype(A::Type{<:AbstractArray}) = CartesianIndex{ndims(A)}
keytype(A::Type{<:AbstractVector}) = Int

valtype(a::AbstractArray) = valtype(typeof(a))
valtype(::Type{Union{}}, slurp...) = eltype(Union{})

"""
valtype(T::Type{<:AbstractArray})
Expand Down Expand Up @@ -232,7 +234,7 @@ UInt8
```
"""
eltype(::Type) = Any
eltype(::Type{Bottom}) = throw(ArgumentError("Union{} does not have elements"))
eltype(::Type{Bottom}, slurp...) = throw(ArgumentError("Union{} does not have elements"))
eltype(x) = eltype(typeof(x))
eltype(::Type{<:AbstractArray{E}}) where {E} = @isdefined(E) ? E : Any

Expand Down Expand Up @@ -268,6 +270,7 @@ julia> ndims(A)
"""
ndims(::AbstractArray{T,N}) where {T,N} = N
ndims(::Type{<:AbstractArray{<:Any,N}}) where {N} = N
ndims(::Type{Union{}}, slurp...) = throw(ArgumentError("Union{} does not have elements"))

"""
length(collection) -> Integer
Expand Down
3 changes: 3 additions & 0 deletions base/array.jl
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,10 @@ function bitsunionsize(u::Union)
return sz
end

# Deprecate this, as it seems to have no documented meaning and is unused here,
# but is frequently accessed in packages
elsize(@nospecialize _::Type{A}) where {T,A<:Array{T}} = aligned_sizeof(T)
elsize(::Type{Union{}}, slurp...) = 0
sizeof(a::Array) = Core.sizeof(a)

function isassigned(a::Array, i::Int...)
Expand Down
2 changes: 2 additions & 0 deletions base/arrayshow.jl
Original file line number Diff line number Diff line change
Expand Up @@ -540,10 +540,12 @@ end
# returning Any, as this would cause incorrect printing in e.g. `Vector[Any[1]]`,
# because eltype(Vector) == Any so `Any` wouldn't be printed in `Any[1]`)
typeinfo_eltype(typeinfo) = nothing # element type not precisely known
typeinfo_eltype(typeinfo::Type{Union{}}, slurp...) = nothing
typeinfo_eltype(typeinfo::Type{<:AbstractArray{T}}) where {T} = eltype(typeinfo)
typeinfo_eltype(typeinfo::Type{<:AbstractDict{K,V}}) where {K,V} = eltype(typeinfo)
typeinfo_eltype(typeinfo::Type{<:AbstractSet{T}}) where {T} = eltype(typeinfo)


# types that can be parsed back accurately from their un-decorated representations
function typeinfo_implicit(@nospecialize(T))
if T === Float64 || T === Int || T === Char || T === String || T === Symbol ||
Expand Down
20 changes: 11 additions & 9 deletions base/boot.jl
Original file line number Diff line number Diff line change
Expand Up @@ -258,9 +258,17 @@ UnionAll(v::TypeVar, @nospecialize(t)) = ccall(:jl_type_unionall, Any, (Any, Any

const Vararg = ccall(:jl_toplevel_eval_in, Any, (Any, Any), Core, _expr(:new, TypeofVararg))

# let the compiler assume that calling Union{} as a constructor does not need
# to be considered ever (which comes up often as Type{<:T})
Union{}(a...) = throw(MethodError(Union{}, a))
# dispatch token indicating a kwarg (keyword sorter) call
function kwcall end
# deprecated internal functions:
kwfunc(@nospecialize(f)) = kwcall
kwftype(@nospecialize(t)) = typeof(kwcall)

# Let the compiler assume that calling Union{} as a constructor does not need
# to be considered ever (which comes up often as Type{<:T} inference, and
# occasionally in user code from eltype).
Union{}(a...) = throw(ArgumentError("cannot construct a value of type Union{} for return result"))
kwcall(kwargs, ::Type{Union{}}, a...) = Union{}(a...)

Expr(@nospecialize args...) = _expr(args...)

Expand Down Expand Up @@ -369,12 +377,6 @@ include(m::Module, fname::String) = ccall(:jl_load_, Any, (Any, Any), m, fname)

eval(m::Module, @nospecialize(e)) = ccall(:jl_toplevel_eval_in, Any, (Any, Any), m, e)

# dispatch token indicating a kwarg (keyword sorter) call
function kwcall end
# deprecated internal functions:
kwfunc(@nospecialize(f)) = kwcall
kwftype(@nospecialize(t)) = typeof(kwcall)

mutable struct Box
contents::Any
Box(@nospecialize(x)) = new(x)
Expand Down
6 changes: 3 additions & 3 deletions base/broadcast.jl
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ that you may be able to leverage; see the
"""
abstract type BroadcastStyle end

struct Unknown <: BroadcastStyle end
BroadcastStyle(::Type{Union{}}, slurp...) = Unknown() # ambiguity resolution

"""
`Broadcast.Style{C}()` defines a [`BroadcastStyle`](@ref) signaling through the type
parameter `C`. You can use this as an alternative to creating custom subtypes of `BroadcastStyle`,
Expand All @@ -45,9 +48,6 @@ struct Style{T} <: BroadcastStyle end

BroadcastStyle(::Type{<:Tuple}) = Style{Tuple}()

struct Unknown <: BroadcastStyle end
BroadcastStyle(::Type{Union{}}) = Unknown() # ambiguity resolution

"""
`Broadcast.AbstractArrayStyle{N} <: BroadcastStyle` is the abstract supertype for any style
associated with an `AbstractArray` type.
Expand Down
1 change: 1 addition & 0 deletions base/complex.jl
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ Float64
real(T::Type) = typeof(real(zero(T)))
real(::Type{T}) where {T<:Real} = T
real(C::Type{<:Complex}) = fieldtype(C, 1)
real(::Type{Union{}}, slurp...) = Union{}(im)

"""
isreal(x) -> Bool
Expand Down
10 changes: 3 additions & 7 deletions base/essentials.jl
Original file line number Diff line number Diff line change
Expand Up @@ -310,13 +310,8 @@ See also: [`round`](@ref), [`trunc`](@ref), [`oftype`](@ref), [`reinterpret`](@r
"""
function convert end

# make convert(::Type{<:Union{}}, x::T) intentionally ambiguous for all T
# so it will never get called or invalidated by loading packages
# with carefully chosen types that won't have any other convert methods defined
convert(T::Type{<:Core.IntrinsicFunction}, x) = throw(MethodError(convert, (T, x)))
convert(T::Type{<:Nothing}, x) = throw(MethodError(convert, (Nothing, x)))
convert(::Type{T}, x::T) where {T<:Core.IntrinsicFunction} = x
convert(::Type{T}, x::T) where {T<:Nothing} = x
# ensure this is never ambiguous, and therefore fast for lookup
convert(T::Type{Union{}}, x...) = throw(ArgumentError("cannot convert a value to Union{} for assignment"))

convert(::Type{Type}, x::Type) = x # the ssair optimizer is strongly dependent on this method existing to avoid over-specialization
# in the absence of inlining-enabled
Expand Down Expand Up @@ -540,6 +535,7 @@ Neither `convert` nor `cconvert` should take a Julia object and turn it into a `
function cconvert end

cconvert(T::Type, x) = x isa T ? x : convert(T, x) # do the conversion eagerly in most cases
cconvert(::Type{Union{}}, x...) = convert(Union{}, x...)
cconvert(::Type{<:Ptr}, x) = x # but defer the conversion to Ptr to unsafe_convert
unsafe_convert(::Type{T}, x::T) where {T} = x # unsafe_convert (like convert) defaults to assuming the convert occurred
unsafe_convert(::Type{T}, x::T) where {T<:Ptr} = x # to resolve ambiguity with the next method
Expand Down
1 change: 1 addition & 0 deletions base/float.jl
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,7 @@ Float64
"""
float(::Type{T}) where {T<:Number} = typeof(float(zero(T)))
float(::Type{T}) where {T<:AbstractFloat} = T
float(::Type{Union{}}, slurp...) = Union{}(0.0)

"""
unsafe_trunc(T, x)
Expand Down
8 changes: 4 additions & 4 deletions base/generator.jl
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,13 @@ Base.HasLength()
"""
IteratorSize(x) = IteratorSize(typeof(x))
IteratorSize(::Type) = HasLength() # HasLength is the default
IteratorSize(::Type{Union{}}, slurp...) = throw(ArgumentError("Union{} does not have elements"))
IteratorSize(::Type{Any}) = SizeUnknown()

IteratorSize(::Type{<:Tuple}) = HasLength()
IteratorSize(::Type{<:AbstractArray{<:Any,N}}) where {N} = HasShape{N}()
IteratorSize(::Type{Generator{I,F}}) where {I,F} = IteratorSize(I)

IteratorSize(::Type{Any}) = SizeUnknown()

haslength(iter) = IteratorSize(iter) isa Union{HasShape, HasLength}

abstract type IteratorEltype end
Expand Down Expand Up @@ -126,7 +126,7 @@ Base.HasEltype()
"""
IteratorEltype(x) = IteratorEltype(typeof(x))
IteratorEltype(::Type) = HasEltype() # HasEltype is the default
IteratorEltype(::Type{Union{}}, slurp...) = throw(ArgumentError("Union{} does not have elements"))
IteratorEltype(::Type{Any}) = EltypeUnknown()

IteratorEltype(::Type{Generator{I,T}}) where {I,T} = EltypeUnknown()

IteratorEltype(::Type{Any}) = EltypeUnknown()
2 changes: 1 addition & 1 deletion base/indices.jl
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ particular, [`eachindex`](@ref) creates an iterator whose type depends
on the setting of this trait.
"""
IndexStyle(A::AbstractArray) = IndexStyle(typeof(A))
IndexStyle(::Type{Union{}}) = IndexLinear()
IndexStyle(::Type{Union{}}, slurp...) = IndexLinear()
IndexStyle(::Type{<:AbstractArray}) = IndexCartesian()
IndexStyle(::Type{<:Array}) = IndexLinear()
IndexStyle(::Type{<:AbstractRange}) = IndexLinear()
Expand Down
2 changes: 2 additions & 0 deletions base/io.jl
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,8 @@ julia> read(io, String)
```
"""
read(stream, t)
read(stream, ::Type{Union{}}, slurp...; kwargs...) = error("cannot read a value of type Union{}")


"""
write(io::IO, x)
Expand Down
2 changes: 2 additions & 0 deletions base/iterators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1170,6 +1170,7 @@ IteratorEltype(::Type{Flatten{Tuple{}}}) = IteratorEltype(Tuple{})
_flatteneltype(I, ::HasEltype) = IteratorEltype(eltype(I))
_flatteneltype(I, et) = EltypeUnknown()

flatten_iteratorsize(::Union{HasShape, HasLength}, ::Type{Union{}}, slurp...) = HasLength() # length==0
flatten_iteratorsize(::Union{HasShape, HasLength}, ::Type{<:NTuple{N,Any}}) where {N} = HasLength()
flatten_iteratorsize(::Union{HasShape, HasLength}, ::Type{<:Tuple}) = SizeUnknown()
flatten_iteratorsize(::Union{HasShape, HasLength}, ::Type{<:Number}) = HasLength()
Expand All @@ -1181,6 +1182,7 @@ _flatten_iteratorsize(sz, ::HasEltype, ::Type{Tuple{}}) = HasLength()

IteratorSize(::Type{Flatten{I}}) where {I} = _flatten_iteratorsize(IteratorSize(I), IteratorEltype(I), I)

flatten_length(f, T::Type{Union{}}, slurp...) = 0
function flatten_length(f, T::Type{<:NTuple{N,Any}}) where {N}
return N * length(f.it)
end
Expand Down
2 changes: 1 addition & 1 deletion base/missing.jl
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ nonmissingtype(::Type{T}) where {T} = typesplit(T, Missing)
function nonmissingtype_checked(T::Type)
R = nonmissingtype(T)
R >: T && error("could not compute non-missing type")
R <: Union{} && error("cannot convert a value to missing for assignment")
return R
end

Expand Down Expand Up @@ -69,7 +70,6 @@ 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)


# Comparison operators
==(::Missing, ::Missing) = missing
==(::Missing, ::Any) = missing
Expand Down
4 changes: 4 additions & 0 deletions base/number.jl
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ julia> zero(rand(2,2))
"""
zero(x::Number) = oftype(x,0)
zero(::Type{T}) where {T<:Number} = convert(T,0)
zero(::Type{Union{}}, slurp...) = Union{}(0)

"""
one(x)
Expand Down Expand Up @@ -345,6 +346,7 @@ julia> import Dates; one(Dates.Day(1))
"""
one(::Type{T}) where {T<:Number} = convert(T,1)
one(x::T) where {T<:Number} = one(T)
one(::Type{Union{}}, slurp...) = Union{}(1)
# note that convert(T, 1) should throw an error if T is dimensionful,
# so this fallback definition should be okay.

Expand All @@ -368,6 +370,7 @@ julia> import Dates; oneunit(Dates.Day)
"""
oneunit(x::T) where {T} = T(one(x))
oneunit(::Type{T}) where {T} = T(one(T))
oneunit(::Type{Union{}}, slurp...) = Union{}(1)

"""
big(T::Type)
Expand All @@ -388,3 +391,4 @@ Complex{BigInt}
```
"""
big(::Type{T}) where {T<:Number} = typeof(big(zero(T)))
big(::Type{Union{}}, slurp...) = Union{}(0)
1 change: 1 addition & 0 deletions base/operators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,7 @@ julia> widen(1.5f0)
"""
widen(x::T) where {T} = convert(widen(T), x)
widen(x::Type{T}) where {T} = throw(MethodError(widen, (T,)))
widen(x::Type{Union{}}, slurp...) = throw(MethodError(widen, (Union{},)))

# function pipelining

Expand Down
2 changes: 2 additions & 0 deletions base/parse.jl
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ julia> parse(Complex{Float64}, "3.2e-1 + 4.5im")
```
"""
parse(T::Type, str; base = Int)
parse(::Type{Union{}}, slurp...; kwargs...) = error("cannot parse a value as Union{}")

function parse(::Type{T}, c::AbstractChar; base::Integer = 10) where T<:Integer
a::Int = (base <= 36 ? 10 : 36)
Expand Down Expand Up @@ -251,6 +252,7 @@ function parse(::Type{T}, s::AbstractString; base::Union{Nothing,Integer} = noth
convert(T, tryparse_internal(T, s, firstindex(s), lastindex(s),
base===nothing ? 0 : check_valid_base(base), true))
end
tryparse(::Type{Union{}}, slurp...; kwargs...) = error("cannot parse a value as Union{}")

## string to float functions ##

Expand Down
6 changes: 6 additions & 0 deletions base/promotion.jl
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,12 @@ it for new types as appropriate.
function promote_rule end

promote_rule(::Type, ::Type) = Bottom
# Define some methods to avoid needing to enumerate unrelated possibilities when presented
# with Type{<:T}, and return a value in general accordance with the result given by promote_type
promote_rule(::Type{Bottom}, slurp...) = Bottom
promote_rule(::Type{Bottom}, ::Type{Bottom}, slurp...) = Bottom # not strictly necessary, since the next method would match unambiguously anyways
promote_rule(::Type{Bottom}, ::Type{T}, slurp...) where {T} = T
promote_rule(::Type{T}, ::Type{Bottom}, slurp...) where {T} = T

promote_result(::Type,::Type,::Type{T},::Type{S}) where {T,S} = (@inline; promote_type(T,S))
# If no promote_rule is defined, both directions give Bottom. In that
Expand Down
1 change: 1 addition & 0 deletions base/some.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ end
function nonnothingtype_checked(T::Type)
R = nonnothingtype(T)
R >: T && error("could not compute non-nothing type")
R <: Union{} && error("cannot convert a value to nothing for assignment")
return R
end

Expand Down
4 changes: 3 additions & 1 deletion base/traits.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ OrderStyle(::Type{<:Real}) = Ordered()
OrderStyle(::Type{<:AbstractString}) = Ordered()
OrderStyle(::Type{Symbol}) = Ordered()
OrderStyle(::Type{<:Any}) = Unordered()
OrderStyle(::Type{Union{}}) = Ordered()
OrderStyle(::Type{Union{}}, slurp...) = Ordered()

# trait for objects that support arithmetic
abstract type ArithmeticStyle end
Expand All @@ -23,6 +23,7 @@ ArithmeticStyle(instance) = ArithmeticStyle(typeof(instance))
ArithmeticStyle(::Type{<:AbstractFloat}) = ArithmeticRounds()
ArithmeticStyle(::Type{<:Integer}) = ArithmeticWraps()
ArithmeticStyle(::Type{<:Any}) = ArithmeticUnknown()
ArithmeticStyle(::Type{Union{}}, slurp...) = ArithmeticUnknown()

# trait for objects that support ranges with regular step
"""
Expand Down Expand Up @@ -58,5 +59,6 @@ ranges with an element type which is a subtype of `Integer`.
abstract type RangeStepStyle end
struct RangeStepRegular <: RangeStepStyle end # range with regular step
struct RangeStepIrregular <: RangeStepStyle end # range with rounding error
RangeStepStyle(::Type{Union{}}, slurp...) = RangeStepIrregular()

RangeStepStyle(instance) = RangeStepStyle(typeof(instance))
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
6564297a5f5971231809bf9940f68b98
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
22d14c82a30f3ec7af09028423cc823808abf86918d5707fd1fcf6ca20dea7871589da9b22e462d194e86fcee380f549aeb65f585048f00bf23281786b17e040

This file was deleted.

This file was deleted.

Loading

0 comments on commit d8fb5e7

Please sign in to comment.