From 69a0b62331bc0f9558e6ca4e6a5037fdbfb91a77 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 13 Apr 2023 11:49:51 -0400 Subject: [PATCH] add typemap filtering option for Union{} Based on the new morespecific rule for Union{} and method definitions of the specific form `f(..., Type{Union{}}, Vararg)`. If a method definition exists with that specific form, the intersection visitor will ignore all intersections that have that as their only result, saving significant effort when working with lookups involving `Type{<:T}` (which usually ended up mostly ambiguous anyways). Fixes: https://github.com/JuliaLang/julia/issues/33780 This pattern turns out to have still to been making package loading slow. We could keep adding methods following the ambiguity pattern https://github.com/JuliaLang/julia/pull/46000 for the couple specific functions that need it (constructor, eltype, IteratorEltype, IteratorSize, and maybe a couple others) so the internals can detect those and optimize functions that have that method pair. But it seems somewhat odd, convoluted, and non-obvious behavior there. Instead, this breaks all ambiguities in which Union{} is present explicitly in favor of the method with Union{}. This means that when computing method matches, as soon as we see one method definition with Union{}, we can record that the method is the only possible match for that slot. This, in essence, permits creating a rule for dispatch that a TypeVar lower bound must be strictly a supertype of Union{}, but this creates it at the function level, instead of expecting the user to add it to every TypeVar they use to define methods. This also lets us improve the error message for these cases (generally they should error to avoid polluting the inference result), since we can be assured this method will be called, and not result in an ambiguous MethodError instead! Reverts the functional change of #46000 --- base/abstractarray.jl | 2 +- base/boot.jl | 20 ++--- base/broadcast.jl | 6 +- base/essentials.jl | 2 +- base/generator.jl | 8 +- base/indices.jl | 2 +- base/missing.jl | 2 +- base/promotion.jl | 6 ++ base/some.jl | 1 + base/traits.jl | 4 +- src/gf.c | 12 ++- src/julia_internal.h | 2 + src/subtype.c | 26 +++++-- src/typemap.c | 177 +++++++++++++++++++++++++++--------------- test/abstractarray.jl | 3 - test/core.jl | 4 +- test/missing.jl | 4 +- 17 files changed, 182 insertions(+), 99 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 7be3f39d16def9..da09d589998592 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -232,7 +232,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 diff --git a/base/boot.jl b/base/boot.jl index ca6e6c81405e25..3a8abde4bce148 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -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...) @@ -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) diff --git a/base/broadcast.jl b/base/broadcast.jl index d86b5cd92e02fa..94413ae05c87a7 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -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`, @@ -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. diff --git a/base/essentials.jl b/base/essentials.jl index 1cf3be297edb7d..07208522c13a98 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -311,7 +311,7 @@ See also: [`round`](@ref), [`trunc`](@ref), [`oftype`](@ref), [`reinterpret`](@r function convert end # ensure this is never ambiguous, and therefore fast for lookup -convert(T::Type{Union{}}, x) = throw(MethodError(convert, (T, x))) +convert(T::Type{Union{}}, x...) = error(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 diff --git a/base/generator.jl b/base/generator.jl index d11742fe5b72f0..aa4b7f67cba95a 100644 --- a/base/generator.jl +++ b/base/generator.jl @@ -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 @@ -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() diff --git a/base/indices.jl b/base/indices.jl index 0584b329411320..497460413de10c 100644 --- a/base/indices.jl +++ b/base/indices.jl @@ -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() diff --git a/base/missing.jl b/base/missing.jl index e1988064aadc12..4544c2b38c4603 100644 --- a/base/missing.jl +++ b/base/missing.jl @@ -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 @@ -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 diff --git a/base/promotion.jl b/base/promotion.jl index 31f507d021e78c..df45835555293d 100644 --- a/base/promotion.jl +++ b/base/promotion.jl @@ -323,6 +323,12 @@ it for new types as appropriate. function promote_rule end promote_rule(::Type, ::Type) = Bottom +# avoid enumerating unrelated possibilities when presented with Type{<:T}, +# in general accordance with the result also given by promote_type +promote_rule(::Type{Bottom}, slurp...) = Bottom +promote_rule(::Type{Bottom}, ::Type{Bottom}, slurp...) = Bottom +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 diff --git a/base/some.jl b/base/some.jl index 08cb3c1648ba1a..0d538cbed6c236 100644 --- a/base/some.jl +++ b/base/some.jl @@ -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 diff --git a/base/traits.jl b/base/traits.jl index 53ae14b12c61e7..47ab8ddc0c7ac1 100644 --- a/base/traits.jl +++ b/base/traits.jl @@ -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 @@ -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 """ @@ -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)) diff --git a/src/gf.c b/src/gf.c index 82f8516e7a36d4..97f0a9f43fe270 100644 --- a/src/gf.c +++ b/src/gf.c @@ -1448,6 +1448,8 @@ static int get_intersect_visitor(jl_typemap_entry_t *oldentry, struct typemap_in // skip if no world has both active // also be careful not to try to scan something from the current dump-reload though return 1; + // don't need to consider other similar methods if this oldentry will always fully intersect with them and dominates all of them + typemap_slurp_search(oldentry, &closure->match); jl_method_t *oldmethod = oldentry->func.method; if (closure->match.issubty // e.g. jl_subtype(closure->newentry.sig, oldentry->sig) && jl_subtype(oldmethod->sig, (jl_value_t*)closure->newentry->sig)) { // e.g. jl_type_equal(closure->newentry->sig, oldentry->sig) @@ -1472,7 +1474,7 @@ static jl_value_t *get_intersect_matches(jl_typemap_t *defs, jl_typemap_entry_t else va = NULL; } - struct matches_env env = {{get_intersect_visitor, (jl_value_t*)type, va, + struct matches_env env = {{get_intersect_visitor, (jl_value_t*)type, va, /* .search_slurp = */ 0, /* .ti = */ NULL, /* .env = */ jl_emptysvec, /* .issubty = */ 0}, /* .newentry = */ newentry, /* .shadowed */ NULL, /* .replaced */ NULL}; JL_GC_PUSH3(&env.match.env, &env.match.ti, &env.shadowed); @@ -3149,6 +3151,7 @@ struct ml_matches_env { int intersections; size_t world; int lim; + int include_ambiguous; // results: jl_value_t *t; // array of method matches size_t min_valid; @@ -3204,6 +3207,9 @@ static int ml_matches_visitor(jl_typemap_entry_t *ml, struct typemap_intersectio return 0; closure->lim--; } + // don't need to consider other similar methods if this ml will always fully intersect with them and dominates all of them + if (!closure->include_ambiguous || closure->lim != -1) + typemap_slurp_search(ml, &closure->match); closure->matc = make_method_match((jl_tupletype_t*)closure->match.ti, closure->match.env, meth, closure->match.issubty ? FULLY_COVERS : NOT_FULLY_COVERS); @@ -3253,9 +3259,9 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, else va = NULL; } - struct ml_matches_env env = {{ml_matches_visitor, (jl_value_t*)type, va, + struct ml_matches_env env = {{ml_matches_visitor, (jl_value_t*)type, va, /* .search_slurp = */ 0, /* .ti = */ NULL, /* .env = */ jl_emptysvec, /* .issubty = */ 0}, - intersections, world, lim, /* .t = */ jl_an_empty_vec_any, + intersections, world, lim, include_ambiguous, /* .t = */ jl_an_empty_vec_any, /* .min_valid = */ *min_valid, /* .max_valid = */ *max_valid, /* .matc = */ NULL}; struct jl_typemap_assoc search = {(jl_value_t*)type, world, jl_emptysvec, 1, ~(size_t)0}; jl_value_t *isect2 = NULL; diff --git a/src/julia_internal.h b/src/julia_internal.h index 4f1a0b4513d8d7..cb7cd423769320 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -1459,12 +1459,14 @@ struct typemap_intersection_env { jl_typemap_intersection_visitor_fptr const fptr; // fptr to call on a match jl_value_t *const type; // type to match jl_value_t *const va; // the tparam0 for the vararg in type, if applicable (or NULL) + size_t search_slurp; // output values jl_value_t *ti; // intersection type jl_svec_t *env; // intersection env (initialize to null to perform intersection without an environment) int issubty; // if `a <: b` is true in `intersect(a,b)` }; int jl_typemap_intersection_visitor(jl_typemap_t *a, int offs, struct typemap_intersection_env *closure); +void typemap_slurp_search(jl_typemap_entry_t *ml, struct typemap_intersection_env *closure); // -- simplevector.c -- // diff --git a/src/subtype.c b/src/subtype.c index cd58f899322cd6..45800d0f890cba 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -2299,20 +2299,34 @@ int jl_has_intersect_type_not_kind(jl_value_t *t) t = jl_unwrap_unionall(t); if (t == (jl_value_t*)jl_any_type) return 1; - if (jl_is_uniontype(t)) { + assert(!jl_is_vararg(t)); + if (jl_is_uniontype(t)) return jl_has_intersect_type_not_kind(((jl_uniontype_t*)t)->a) || jl_has_intersect_type_not_kind(((jl_uniontype_t*)t)->b); - } - if (jl_is_typevar(t)) { + if (jl_is_typevar(t)) return jl_has_intersect_type_not_kind(((jl_tvar_t*)t)->ub); - } - if (jl_is_datatype(t)) { + if (jl_is_datatype(t)) if (((jl_datatype_t*)t)->name == jl_type_typename) return 1; - } return 0; } +// compute if DataType<:t || Union<:t || UnionAll<:t etc. +int jl_has_intersect_kind_not_type(jl_value_t *t) +{ + t = jl_unwrap_unionall(t); + if (t == (jl_value_t*)jl_any_type || jl_is_kind(t)) + return 1; + assert(!jl_is_vararg(t)); + if (jl_is_uniontype(t)) + return jl_has_intersect_kind_not_type(((jl_uniontype_t*)t)->a) || + jl_has_intersect_kind_not_type(((jl_uniontype_t*)t)->b); + if (jl_is_typevar(t)) + return jl_has_intersect_kind_not_type(((jl_tvar_t*)t)->ub); + return 0; +} + + JL_DLLEXPORT int jl_isa(jl_value_t *x, jl_value_t *t) { if (jl_typeis(x,t) || t == (jl_value_t*)jl_any_type) diff --git a/src/typemap.c b/src/typemap.c index dc2035cbcc5d97..79b8ac219f1b03 100644 --- a/src/typemap.c +++ b/src/typemap.c @@ -396,8 +396,10 @@ static unsigned jl_supertype_height(jl_datatype_t *dt) } // return true if a and b might intersect in the type domain (over just their type-names) -static int tname_intersection(jl_datatype_t *a, jl_typename_t *bname, unsigned ha) +static int tname_intersection_dt(jl_datatype_t *a, jl_typename_t *bname, unsigned ha) { + if (a == jl_any_type) + return 1; jl_datatype_t *b = (jl_datatype_t*)jl_unwrap_unionall(bname->wrapper); unsigned hb = 1; while (b != jl_any_type) { @@ -413,23 +415,42 @@ static int tname_intersection(jl_datatype_t *a, jl_typename_t *bname, unsigned h return a->name == bname; } +static int tname_intersection(jl_value_t *a, jl_typename_t *bname, int8_t tparam) +{ + if (a == (jl_value_t*)jl_any_type) + return 1; + a = jl_unwrap_unionall(a); + assert(!jl_is_vararg(a)); + if (jl_is_uniontype(a)) + return tname_intersection(((jl_uniontype_t*)a)->a, bname, tparam) || + tname_intersection(((jl_uniontype_t*)a)->b, bname, tparam); + if (jl_is_typevar(a)) + return tname_intersection(((jl_tvar_t*)a)->ub, bname, tparam); + if (jl_is_datatype(a)) { + if (tparam) { + if (!jl_is_type_type(a)) + return 0; + a = jl_unwrap_unionall(jl_tparam0(a)); + if (!jl_is_datatype(a)) + return tname_intersection(a, bname, 0); + } + return tname_intersection_dt((jl_datatype_t*)a, bname, jl_supertype_height((jl_datatype_t*)a)); + } + return 0; +} + static int concrete_intersects(jl_value_t *t, jl_value_t *ty, int8_t tparam) { if (ty == (jl_value_t*)jl_any_type) // easy case: Any always matches return 1; - if (tparam & 1) { - if (tparam & 4) - return jl_isa(t, ty); // (Type{t} <: ty), where is_leaf_type(t) => isa(t, ty) - return 1; - } - else { + if (tparam & 1) + return jl_isa(t, ty); // (Type{t} <: ty), where is_leaf_type(t) => isa(t, ty) + else return t == ty || jl_subtype(t, ty); - } } // tparam bit 0 is ::Type{T} (vs. T) // tparam bit 1 is typename(T) (vs. T) -// tparam bit 2 is exclude_typeofbottom (only if tparam bit 1 is set) static int jl_typemap_intersection_array_visitor(jl_array_t *a, jl_value_t *ty, int8_t tparam, int8_t offs, struct typemap_intersection_env *closure) { @@ -438,28 +459,24 @@ static int jl_typemap_intersection_array_visitor(jl_array_t *a, jl_value_t *ty, _Atomic(jl_typemap_t*) *data = (_Atomic(jl_typemap_t*)*)jl_array_data(a); unsigned height = 0; jl_datatype_t *tydt = jl_any_type; - if (jl_is_kind(ty)) - ty = (jl_value_t*)jl_any_type; if (tparam & 2) { // try to extract a description of ty for intersections, but since we - // know we got here because we already failed to find an exact match, - // we don't try too hard - if (tparam & 1) { - if (tparam & 4) { - // extract T from Type{T} (if possible) - jl_value_t *ttype = jl_unwrap_unionall(ty); - ttype = jl_is_type_type(ttype) ? jl_tparam0(ttype) : NULL; - ttype = ttype ? jl_type_extract_name(ttype) : NULL; - if (ttype) - tydt = (jl_datatype_t*)jl_unwrap_unionall(((jl_typename_t*)ttype)->wrapper); - } + jl_value_t *ttype = jl_unwrap_unionall(ty); + if (tparam & 1) + // extract T from Type{T} (if possible) + ttype = jl_is_type_type(ttype) ? jl_tparam0(ttype) : NULL; + if (ttype && jl_is_datatype(ttype)) { + tydt = (jl_datatype_t*)ttype; } - else { - tydt = (jl_datatype_t*)jl_unwrap_unionall(ty); - if (!jl_is_datatype(tydt)) - tydt = jl_any_type; + else if (ttype) { + ttype = jl_type_extract_name(ttype); + tydt = ttype ? (jl_datatype_t*)jl_unwrap_unionall(((jl_typename_t*)ttype)->wrapper) : NULL; } - if (tydt != jl_any_type) + if (tydt == jl_any_type) + ty = (jl_value_t*)jl_any_type; + else if (tydt == NULL) + tydt = jl_any_type; + else height = jl_supertype_height(tydt); } for (i = 0; i < l; i += 2) { @@ -470,8 +487,9 @@ static int jl_typemap_intersection_array_visitor(jl_array_t *a, jl_value_t *ty, if (tparam & 2) { jl_typemap_t *ml = jl_atomic_load_relaxed(&data[i + 1]); JL_GC_PROMISE_ROOTED(ml); - if (tydt == jl_any_type || // easy case: Any always matches - tname_intersection(tydt, (jl_typename_t*)t, height)) { + if (tydt == jl_any_type ? + tname_intersection(ty, (jl_typename_t*)t, tparam & 1) : + tname_intersection_dt(tydt, (jl_typename_t*)t, height)) { if ((tparam & 1) && t == (jl_value_t*)jl_typeofbottom_type->name) // skip Type{Union{}} and Type{typeof(Union{})}, since the caller should have already handled those continue; if (jl_is_array(ml)) { @@ -544,8 +562,25 @@ static int jl_typemap_intersection_node_visitor(jl_typemap_entry_t *ml, struct t return 1; } +int jl_has_intersect_type_not_kind(jl_value_t *t); +int jl_has_intersect_kind_not_type(jl_value_t *t); -jl_value_t *extract_wrapper(jl_value_t *t JL_PROPAGATES_ROOT) JL_GLOBALLY_ROOTED; +void typemap_slurp_search(jl_typemap_entry_t *ml, struct typemap_intersection_env *closure) +{ + if (closure->search_slurp && ml->va) { + jl_value_t *sig = jl_unwrap_unionall((jl_value_t*)ml->sig); + size_t nargs = jl_nparams(sig); + if (nargs > 1 && nargs - 1 == closure->search_slurp) { + jl_vararg_t *va = (jl_vararg_t*)jl_tparam(sig, nargs - 1); + assert(jl_is_vararg(va)); + if (va->T == (jl_value_t*)jl_any_type && va->N == NULL) { + // instruct typemap it can set exclude_typeofbottom on parameter nargs + // since we found the necessary slurp argument + closure->search_slurp = 0; + } + } + } +} int jl_typemap_intersection_visitor(jl_typemap_t *map, int offs, struct typemap_intersection_env *closure) @@ -583,28 +618,29 @@ int jl_typemap_intersection_visitor(jl_typemap_t *map, int offs, while (jl_is_typevar(ty)) ty = ((jl_tvar_t*)ty)->ub; // approxify the tparam until we have a valid type - if (jl_has_free_typevars(ty)) { - ty = extract_wrapper(ty); - if (ty == NULL) - ty = (jl_value_t*)jl_any_type; - } + if (jl_has_free_typevars(ty)) + ty = jl_rewrap_unionall(ty, ttypes); + JL_GC_PUSH1(&ty); jl_array_t *targ = jl_atomic_load_relaxed(&cache->targ); jl_array_t *tname = jl_atomic_load_relaxed(&cache->tname); - int maybe_Type = 0; + int maybe_type = 0; + int maybe_kind = 0; int exclude_typeofbottom = 0; jl_value_t *typetype = NULL; jl_value_t *name = NULL; - // pre-check: optimized pre-intersection test to see if `ty` could intersect with any Type + // pre-check: optimized pre-intersection test to see if `ty` could intersect with any Type or Kind if (targ != (jl_array_t*)jl_an_empty_vec_any || tname != (jl_array_t*)jl_an_empty_vec_any) { - typetype = jl_unwrap_unionall(ty); - typetype = jl_is_type_type(typetype) ? jl_tparam0(typetype) : NULL; - name = typetype ? jl_type_extract_name(typetype) : NULL; - maybe_Type = typetype || !jl_has_empty_intersection((jl_value_t*)jl_type_type, ty); - if (maybe_Type) + maybe_kind = jl_has_intersect_kind_not_type(ty); + maybe_type = maybe_kind || jl_has_intersect_type_not_kind(ty); + if (maybe_type && !maybe_kind) { + typetype = jl_unwrap_unionall(ty); + typetype = jl_is_type_type(typetype) ? jl_tparam0(typetype) : NULL; + name = typetype ? jl_type_extract_name(typetype) : NULL; exclude_typeofbottom = !(typetype ? jl_parameter_includes_bottom(typetype) : jl_subtype((jl_value_t*)jl_typeofbottom_type, ty)); + } } // First check for intersections with methods defined on Type{T}, where T was a concrete type - if (targ != (jl_array_t*)jl_an_empty_vec_any && maybe_Type && + if (targ != (jl_array_t*)jl_an_empty_vec_any && maybe_type && (!typetype || jl_has_free_typevars(typetype) || is_cache_leaf(typetype, 1))) { // otherwise cannot contain this particular kind, so don't bother with checking if (!exclude_typeofbottom) { // detect Type{Union{}}, Type{Type{Union{}}}, and Type{typeof(Union{}} and do those early here @@ -615,13 +651,21 @@ int jl_typemap_intersection_visitor(jl_typemap_t *map, int offs, targ = jl_atomic_load_relaxed(&cache->targ); // may be GC'd during type-intersection jl_value_t *ml = mtcache_hash_lookup(targ, (jl_value_t*)jl_typeofbottom_type->name); if (ml != jl_nothing) { - if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) return 0; - //exclude_typeofbottom = 1; + size_t search_slurp = closure->search_slurp; + closure->search_slurp = offs + 1; + if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) { + closure->search_slurp = search_slurp; + JL_GC_POP(); + return 0; + } + if (closure->search_slurp == 0) + exclude_typeofbottom = 1; + closure->search_slurp = search_slurp; } } if (name != (jl_value_t*)jl_typeofbottom_type->name) { targ = jl_atomic_load_relaxed(&cache->targ); // may be GC'd earlier - if (name && jl_type_extract_name_precise(typetype, 1)) { + if (exclude_typeofbottom && name && jl_type_extract_name_precise(typetype, 1)) { // attempt semi-direct lookup of types via their names // consider the type name first jl_value_t *ml = mtcache_hash_lookup(targ, (jl_value_t*)name); @@ -631,21 +675,21 @@ int jl_typemap_intersection_visitor(jl_typemap_t *map, int offs, if (is_cache_leaf(typetype, 1)) { ml = mtcache_hash_lookup((jl_array_t*)ml, typetype); if (ml != jl_nothing) { - if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) return 0; + if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) { JL_GC_POP(); return 0; } } } } else { - if (!jl_typemap_intersection_array_visitor((jl_array_t*)ml, ty, 1 | (exclude_typeofbottom << 2), offs, closure)) return 0; + if (!jl_typemap_intersection_array_visitor((jl_array_t*)ml, ty, 1, offs, closure)) { JL_GC_POP(); return 0; } } } else if (ml != jl_nothing) { - if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) return 0; + if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) { JL_GC_POP(); return 0; } } } else { // else an array scan is required to consider all the possible subtypes - if (!jl_typemap_intersection_array_visitor(targ, ty, 3 | (exclude_typeofbottom << 2), offs, closure)) return 0; + if (!jl_typemap_intersection_array_visitor(targ, exclude_typeofbottom && !maybe_kind ? ty : (jl_value_t*)jl_any_type, 3, offs, closure)) { JL_GC_POP(); return 0; } } } } @@ -658,7 +702,7 @@ int jl_typemap_intersection_visitor(jl_typemap_t *map, int offs, if (jl_is_array(ml)) ml = mtcache_hash_lookup((jl_array_t*)ml, ty); if (ml != jl_nothing) { - if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) return 0; + if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) { JL_GC_POP(); return 0; } } } else { @@ -667,20 +711,20 @@ int jl_typemap_intersection_visitor(jl_typemap_t *map, int offs, // direct lookup of leaf types jl_value_t *ml = mtcache_hash_lookup(cachearg1, name); if (jl_is_array(ml)) { - if (!jl_typemap_intersection_array_visitor((jl_array_t*)ml, ty, 0, offs, closure)) return 0; + if (!jl_typemap_intersection_array_visitor((jl_array_t*)ml, ty, 0, offs, closure)) { JL_GC_POP(); return 0; } } else { - if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) return 0; + if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) { JL_GC_POP(); return 0; } } } else { // else an array scan is required to check subtypes - if (!jl_typemap_intersection_array_visitor(cachearg1, ty, 2, offs, closure)) return 0; + if (!jl_typemap_intersection_array_visitor(cachearg1, ty, 2, offs, closure)) { JL_GC_POP(); return 0; } } } } // Next check for intersections with methods defined on Type{T}, where T was not concrete (it might even have been a TypeVar), but had an extractable TypeName - if (tname != (jl_array_t*)jl_an_empty_vec_any && maybe_Type) { + if (tname != (jl_array_t*)jl_an_empty_vec_any && maybe_type) { if (!exclude_typeofbottom || (!typetype && jl_isa((jl_value_t*)jl_typeofbottom_type, ty))) { // detect Type{Union{}}, Type{Type{Union{}}}, and Type{typeof(Union{}} and do those early here // otherwise the possibility of encountering `Type{Union{}}` in this intersection may @@ -690,11 +734,19 @@ int jl_typemap_intersection_visitor(jl_typemap_t *map, int offs, tname = jl_atomic_load_relaxed(&cache->tname); // may be GC'd earlier jl_value_t *ml = mtcache_hash_lookup(tname, (jl_value_t*)jl_typeofbottom_type->name); if (ml != jl_nothing) { - if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) return 0; - //exclude_typeofbottom = 1; + size_t search_slurp = closure->search_slurp; + closure->search_slurp = offs + 1; + if (!jl_typemap_intersection_visitor((jl_typemap_t*)ml, offs+1, closure)) { + closure->search_slurp = search_slurp; + JL_GC_POP(); + return 0; + } + if (closure->search_slurp == 0) + exclude_typeofbottom = 1; + closure->search_slurp = search_slurp; } } - if (name && jl_type_extract_name_precise(typetype, 1)) { + if (exclude_typeofbottom && name && jl_type_extract_name_precise(typetype, 1)) { // semi-direct lookup of types // just consider the type and its direct super types jl_datatype_t *super = (jl_datatype_t*)jl_unwrap_unionall(((jl_typename_t*)name)->wrapper); @@ -704,7 +756,7 @@ int jl_typemap_intersection_visitor(jl_typemap_t *map, int offs, tname = jl_atomic_load_relaxed(&cache->tname); // reload after callback jl_typemap_t *ml = mtcache_hash_lookup(tname, (jl_value_t*)super->name); if (ml != jl_nothing) { - if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) return 0; + if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) { JL_GC_POP(); return 0; } } if (super == jl_any_type) break; @@ -714,7 +766,7 @@ int jl_typemap_intersection_visitor(jl_typemap_t *map, int offs, else { // else an array scan is required to check subtypes of typetype too tname = jl_atomic_load_relaxed(&cache->tname); // may be GC'd earlier - if (!jl_typemap_intersection_array_visitor(tname, ty, 3 | (exclude_typeofbottom << 2), offs, closure)) return 0; + if (!jl_typemap_intersection_array_visitor(tname, exclude_typeofbottom && !maybe_kind ? ty : (jl_value_t*)jl_any_type, 3, offs, closure)) { JL_GC_POP(); return 0; } } } jl_array_t *name1 = jl_atomic_load_relaxed(&cache->name1); @@ -727,7 +779,7 @@ int jl_typemap_intersection_visitor(jl_typemap_t *map, int offs, name1 = jl_atomic_load_relaxed(&cache->name1); // reload after callback jl_typemap_t *ml = mtcache_hash_lookup(name1, (jl_value_t*)super->name); if (ml != jl_nothing) { - if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) return 0; + if (!jl_typemap_intersection_visitor(ml, offs+1, closure)) { JL_GC_POP(); return 0; } } if (super == jl_any_type) break; @@ -736,9 +788,10 @@ int jl_typemap_intersection_visitor(jl_typemap_t *map, int offs, } else { // else an array scan is required to check subtypes - if (!jl_typemap_intersection_array_visitor(name1, ty, 2, offs, closure)) return 0; + if (!jl_typemap_intersection_array_visitor(name1, ty, 2, offs, closure)) { JL_GC_POP(); return 0; } } } + JL_GC_POP(); } if (!jl_typemap_intersection_node_visitor(jl_atomic_load_relaxed(&cache->linear), closure)) return 0; diff --git a/test/abstractarray.jl b/test/abstractarray.jl index 070e5d7a7b2890..c5ff97deb6777e 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -520,9 +520,6 @@ function test_primitives(::Type{T}, shape, ::Type{TestAbstractArray}) where T @test convert(Matrix, Y) == Y @test convert(Matrix, view(Y, 1:2, 1:2)) == Y @test_throws MethodError convert(Matrix, X) - - # convert(::Type{Union{}}, A::AbstractMatrix) - @test_throws MethodError convert(Union{}, X) end mutable struct TestThrowNoGetindex{T} <: AbstractVector{T} end diff --git a/test/core.jl b/test/core.jl index a89d206182dbf1..e94e2e87ccba77 100644 --- a/test/core.jl +++ b/test/core.jl @@ -1694,7 +1694,7 @@ end # issue #3221 let x = fill(nothing, 1) - @test_throws MethodError x[1] = 1 + @test_throws ErrorException("cannot convert a value to Union{} for assignment") x[1] = 1 end # issue #3220 @@ -4916,7 +4916,7 @@ struct f47209 x::Int f47209()::Nothing = new(1) end -@test_throws MethodError f47209() +@test_throws ErrorException("cannot convert a value to nothing for assignment") f47209() # issue #12096 let a = Val{Val{TypeVar(:_, Int)}}, diff --git a/test/missing.jl b/test/missing.jl index 450b816ea3e57d..f06d1aad7a6b16 100644 --- a/test/missing.jl +++ b/test/missing.jl @@ -21,8 +21,8 @@ end @test convert(Union{Nothing, Missing}, nothing) === nothing @test convert(Union{Missing, Nothing, Float64}, 1) === 1.0 - @test_throws MethodError convert(Missing, 1) - @test_throws MethodError convert(Union{Nothing, Missing}, 1) + @test_throws ErrorException("cannot convert a value to missing for assignment") convert(Missing, 1) + @test_throws ErrorException("cannot convert a value to missing for assignment") convert(Union{Nothing, Missing}, 1) @test_throws MethodError convert(Union{Int, Missing}, "a") end