Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

reland: add morespecific rule for Type{Union{}} #49470

Merged
merged 4 commits into from
Apr 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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