Skip to content

Commit

Permalink
morespecific: add rule for Type{Union{}}
Browse files Browse the repository at this point in the history
Make Type{Union{}} in method definitions always the most specific type
(normally these would end up being ambiguous). This ensures we do not
invalidate them, nor need to consider ambiguities that might arise from
intersections with them.
  • Loading branch information
vtjnash committed Apr 23, 2023
1 parent 7560dea commit 44b3d2c
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 7 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

This comment has been minimized.

Copy link
@PallHaraldsson

PallHaraldsson May 11, 2023

Contributor

Isn't it "more specific"? Or without the space some technical term?

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
9 changes: 2 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(MethodError(convert, (T, x)))

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
22 changes: 22 additions & 0 deletions src/subtype.c
Original file line number Diff line number Diff line change
Expand Up @@ -4460,6 +4460,21 @@ static int num_occurs(jl_tvar_t *v, jl_typeenv_t *env)
return 0;
}

int tuple_cmp_typeofbottom(jl_datatype_t *a, jl_datatype_t *b)
{
size_t i, la = jl_nparams(a), lb = jl_nparams(b);
for (i = 0; i < la || i < lb; i++) {
jl_value_t *pa = i < la ? jl_tparam(a, i) : NULL;
jl_value_t *pb = i < lb ? jl_tparam(b, i) : NULL;
int xa = pa == (jl_value_t*)jl_typeofbottom_type || pa == (jl_value_t*)jl_typeofbottom_type->super;
int xb = pb == (jl_value_t*)jl_typeofbottom_type || pb == (jl_value_t*)jl_typeofbottom_type->super;
if (xa != xb)
return xa - xb;
}
return 0;
}


#define HANDLE_UNIONALL_A \
jl_unionall_t *ua = (jl_unionall_t*)a; \
jl_typeenv_t newenv = { ua->var, 0x0, env }; \
Expand All @@ -4478,6 +4493,13 @@ static int type_morespecific_(jl_value_t *a, jl_value_t *b, jl_value_t *a0, jl_v
return 0;

if (jl_is_tuple_type(a) && jl_is_tuple_type(b)) {
// compare whether a and b have Type{Union{}} included,
// which makes them instantly the most specific, regardless of all else,
// for whichever is left most (the left-to-right behavior here ensures
// we do not need to keep track of conflicts with multiple methods).
int msp = tuple_cmp_typeofbottom((jl_datatype_t*)a, (jl_datatype_t*)b);
if (msp)
return msp > 0;
// When one is JL_VARARG_BOUND and the other has fixed length,
// allow the argument length to fix the tvar
jl_vararg_kind_t akind = jl_va_tuple_kind((jl_datatype_t*)a);
Expand Down
5 changes: 5 additions & 0 deletions test/specificity.jl
Original file line number Diff line number Diff line change
Expand Up @@ -311,3 +311,8 @@ let A = Tuple{Type{SubString{S}},AbstractString} where S<:AbstractString,
@test args_morespecific(B, C)
@test args_morespecific(A, C)
end

@test args_morespecific(Tuple{Type{Union{}}, Any}, Tuple{Any, Type{Union{}}})
@test args_morespecific(Tuple{typeof(Union{}), Any}, Tuple{Any, Type{Union{}}})
@test args_morespecific(Tuple{Type{Union{}}, Type{Union{}}, Any}, Tuple{Type{Union{}}, Any, Type{Union{}}})
@test args_morespecific(Tuple{Type{Union{}}, Type{Union{}}, Any, Type{Union{}}}, Tuple{Type{Union{}}, Any, Type{Union{}}, Type{Union{}}})

0 comments on commit 44b3d2c

Please sign in to comment.