From 6337cf6a137f4a89e94661b8fcf9d33f1798bb96 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 20 Mar 2023 13:34:06 -0400 Subject: [PATCH] datatype: handle concrete type intersections that happen (#49017) This is actually very similar to the current might_intersect_concrete, but for subtyping and memoized. It replaces cached_by_hash, which was a confusingly-named incomplete prior work towards this. This gives NamedTuple{(:names)} hashes, which lets them go into the faster type lookup tables. This is a fairly common type for some packages to create, so we need this to avoid polluting our cache tables. Reverts efafd8388675d65096e0f088ddfe96f4e8077567, since these types have no intersection, the morespecific algorithm is no longer required to have any opinion on them. --- src/datatype.c | 2 +- src/jl_exported_funcs.inc | 1 - src/jltypes.c | 241 ++++++++++++++++++++++++++------------ src/julia.h | 3 +- src/julia_internal.h | 1 + src/subtype.c | 23 ++-- test/specificity.jl | 2 +- 7 files changed, 176 insertions(+), 97 deletions(-) diff --git a/src/datatype.c b/src/datatype.c index ae1e3029aa0e1..91fd24495f299 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -103,7 +103,7 @@ jl_datatype_t *jl_new_uninitialized_datatype(void) t->isprimitivetype = 0; t->zeroinit = 0; t->has_concrete_subtype = 1; - t->cached_by_hash = 0; + t->maybe_subtype_of_cache = 1; t->ismutationfree = 0; t->isidentityfree = 0; t->name = NULL; diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index d3acb7d2ad92a..863f5d5686fb7 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -487,7 +487,6 @@ XX(jl_typename_str) \ XX(jl_typeof_str) \ XX(jl_types_equal) \ - XX(jl_type_equality_is_identity) \ XX(jl_type_error) \ XX(jl_type_error_rt) \ XX(jl_type_intersection) \ diff --git a/src/jltypes.c b/src/jltypes.c index 0b7ef9424b6bf..1b602e58b215a 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -581,8 +581,8 @@ static int typekey_eq(jl_datatype_t *tt, jl_value_t **key, size_t n) if (tt->name == jl_type_typename) { // for Type{T}, require `typeof(T)` to match also, to avoid incorrect // dispatch from changing the type of something. - // this should work because `Type`s don't have uids, and aren't the - // direct tags of values so we don't rely on pointer equality. + // this should work because `Type`s don't need unique pointers, and aren't the + // direct tags of values (concrete) so we don't rely on pointer equality. jl_value_t *kj = key[0]; jl_value_t *tj = jl_tparam0(tt); return (kj == tj || (jl_typeof(tj) == jl_typeof(kj) && jl_types_equal(tj, kj))); @@ -591,11 +591,14 @@ static int typekey_eq(jl_datatype_t *tt, jl_value_t **key, size_t n) jl_value_t *kj = key[j]; jl_value_t *tj = jl_svecref(tt->parameters, j); if (tj != kj) { - // require exact same Type{T}. see e.g. issue #22842 - if (jl_is_type_type(tj) || jl_is_type_type(kj)) - return 0; - if ((jl_is_concrete_type(tj) || jl_is_concrete_type(kj)) && - jl_type_equality_is_identity(tj, kj)) + if (tt->name == jl_tuple_typename) { + // require exact same Type{T} in covariant context. see e.g. issue #22842 + // this should work because `Tuple{Type}`s don't need unique pointers, and aren't the + // direct tags of values (concrete) so we don't rely on pointer equality. + if (jl_is_type_type(tj) || jl_is_type_type(kj)) + return 0; + } + if (jl_type_equality_is_identity(tj, kj)) return 0; if (!jl_types_equal(tj, kj)) return 0; @@ -905,16 +908,88 @@ jl_datatype_t *jl_lookup_cache_type_(jl_datatype_t *type) return (jl_datatype_t*)lookup_type(type->name, key, n); } -JL_DLLEXPORT int jl_type_equality_is_identity(jl_value_t *t1, jl_value_t *t2) +// compute whether kj might actually be a subtype of something in the cache +// (which otherwise would normally be comparable with pointer-egal) +static int maybe_subtype_of_cache(jl_value_t *kj, int covariant) JL_NOTSAFEPOINT { - if (t1 == t2) + jl_value_t *uw = jl_is_unionall(kj) ? jl_unwrap_unionall(kj) : kj; + if (jl_is_datatype(uw)) { + jl_datatype_t *dt = (jl_datatype_t*)uw; + return dt->maybe_subtype_of_cache; + } + else if (jl_is_uniontype(uw)) { + int ca = maybe_subtype_of_cache(((jl_uniontype_t*)uw)->a, covariant); + int cb = maybe_subtype_of_cache(((jl_uniontype_t*)uw)->b, covariant); + return ca && cb; + } + else if (uw == jl_bottom_type) { return 1; - if (!jl_is_datatype(t1) || !jl_is_datatype(t2)) - return 0; - jl_datatype_t *dt1 = (jl_datatype_t *) t1; - jl_datatype_t *dt2 = (jl_datatype_t *) t2; + } + else if (jl_is_typevar(uw) && !covariant) { // assume Tuple's bounds are always degenerate + // TODO: improve this bound if we can prove that typeintersect(lb,ub) is a leaftype + jl_tvar_t *tv = (jl_tvar_t*)uw; + return tv->lb == tv->ub || + tv->lb != jl_bottom_type; + } + return 1; +} + +// compute whether kj might have a supertype which is actually concrete +static int has_concrete_supertype(jl_value_t *kj) JL_NOTSAFEPOINT +{ + jl_value_t *uw = jl_is_unionall(kj) ? jl_unwrap_unionall(kj) : kj; + if (jl_is_datatype(uw)) { + jl_datatype_t *dt = (jl_datatype_t*)uw; + if (dt->name->abstract && dt->name != jl_type_typename) + return 0; + if (!dt->maybe_subtype_of_cache) + return 0; + if (dt->name == jl_tuple_typename) { + // check tuple parameters recursively for has_concrete_supertype + size_t i, n = jl_nparams(dt); + for (i = 0; i < n; i++) { + jl_value_t *p = jl_tparam(dt, i); + if (jl_is_vararg(p)) + p = jl_unwrap_vararg(p); + if (!has_concrete_supertype(p)) + return 0; + } + } + return 1; + } + else if (jl_is_uniontype(uw)) { + int ca = has_concrete_supertype(((jl_uniontype_t*)uw)->a); + int cb = has_concrete_supertype(((jl_uniontype_t*)uw)->b); + return ca && cb; + } + else if (uw == jl_bottom_type) { + return 1; + } + else if (jl_is_typevar(uw)) { + jl_tvar_t *tv = (jl_tvar_t*)uw; + return has_concrete_supertype(tv->ub); + } + return 0; +} - return dt1->cached_by_hash == dt2->cached_by_hash; +int jl_type_equality_is_identity(jl_value_t *t1, jl_value_t *t2) JL_NOTSAFEPOINT +{ + int c1 = jl_is_concrete_type(t1); + int c2 = jl_is_concrete_type(t2); + if (c1 && c2) { + if (((jl_datatype_t*)t1)->name != jl_tuple_typename) + return 1; + if (((jl_datatype_t*)t2)->name != jl_tuple_typename) + return 1; + if (((jl_datatype_t*)t1)->has_concrete_subtype && ((jl_datatype_t*)t2)->has_concrete_subtype) + return 1; + // e.g. Tuple{Union{}} and Tuple{Int} are both concrete! + } + if (c1 && !has_concrete_supertype(t2)) + return 1; + if (c2 && !has_concrete_supertype(t1)) + return 1; + return 0; } // type instantiation @@ -1147,7 +1222,7 @@ static jl_value_t *lookup_type_stack(jl_typestack_t *stack, jl_datatype_t *tt, s } // stable numbering for types--starts with name->hash, then falls back to objectid -// sets failed if the hash value isn't stable (if not set on entry) +// sets *failed if the hash value isn't stable (if this param not set on entry) static unsigned type_hash(jl_value_t *kj, int *failed) JL_NOTSAFEPOINT { jl_value_t *uw = jl_is_unionall(kj) ? jl_unwrap_unionall(kj) : kj; @@ -1159,32 +1234,21 @@ static unsigned type_hash(jl_value_t *kj, int *failed) JL_NOTSAFEPOINT *failed = 1; return 0; } + // compute a hash now, only for the parent object we are putting in the cache hash = typekey_hash(dt->name, jl_svec_data(dt->parameters), jl_svec_len(dt->parameters), *failed); } return hash; } else if (jl_is_typevar(uw)) { - if (!*failed) { - *failed = 1; - return 0; - } // ignore var and lb, since those might get normalized out in equality testing return type_hash(((jl_tvar_t*)uw)->ub, failed); } - else if (jl_is_vararg(uw)) { - if (!*failed) { - *failed = 1; - return 0; - } - jl_vararg_t *vm = (jl_vararg_t *)uw; - // 0x064eeaab is just a randomly chosen constant - return bitmix(type_hash(vm->T ? vm->T : (jl_value_t*)jl_any_type, failed), vm->N ? type_hash(vm->N, failed) : 0x064eeaab); - } else if (jl_is_uniontype(uw)) { if (!*failed) { *failed = 1; return 0; } + // compute a hash now, only for the parent object we are putting in the cache unsigned hasha = type_hash(((jl_uniontype_t*)uw)->a, failed); unsigned hashb = type_hash(((jl_uniontype_t*)uw)->b, failed); // use a associative mixing function, with well-defined overflow @@ -1204,7 +1268,18 @@ static unsigned typekey_hash(jl_typename_t *tn, jl_value_t **key, size_t n, int unsigned hash = 3; int failed = nofail; for (j = 0; j < n; j++) { - hash = bitmix(type_hash(key[j], &failed), hash); + jl_value_t *p = key[j]; + if (jl_is_vararg(p)) { + jl_vararg_t *vm = (jl_vararg_t*)p; + if (!nofail && vm->N) + return 0; + // 0x064eeaab is just a randomly chosen constant + hash = bitmix(vm->N ? type_hash(vm->N, &failed) : 0x064eeaab, hash); + if (failed && !nofail) + return 0; + p = vm->T ? vm->T : (jl_value_t*)jl_any_type; + } + hash = bitmix(type_hash(p, &failed), hash); if (failed && !nofail) return 0; } @@ -1237,6 +1312,7 @@ void jl_precompute_memoized_dt(jl_datatype_t *dt, int cacheable) { int istuple = (dt->name == jl_tuple_typename); dt->hasfreetypevars = 0; + dt->maybe_subtype_of_cache = 1; dt->isconcretetype = !dt->name->abstract; dt->isdispatchtuple = istuple; size_t i, l = jl_nparams(dt); @@ -1247,30 +1323,38 @@ void jl_precompute_memoized_dt(jl_datatype_t *dt, int cacheable) if (dt->hasfreetypevars) dt->isconcretetype = 0; } - if (istuple && dt->isconcretetype) - dt->isconcretetype = (jl_is_datatype(p) && ((jl_datatype_t*)p)->isconcretetype) || p == jl_bottom_type; - if (dt->isdispatchtuple) { - dt->isdispatchtuple = jl_is_datatype(p) && - ((!jl_is_kind(p) && ((jl_datatype_t*)p)->isconcretetype) || - (p == (jl_value_t*)jl_typeofbottom_type) || // == Type{Union{}}, so needs to be consistent - (((jl_datatype_t*)p)->name == jl_type_typename && !((jl_datatype_t*)p)->hasfreetypevars)); + if (istuple) { + if (dt->isconcretetype) + dt->isconcretetype = (jl_is_datatype(p) && ((jl_datatype_t*)p)->isconcretetype) || p == jl_bottom_type; + if (dt->isdispatchtuple) { + dt->isdispatchtuple = jl_is_datatype(p) && + ((!jl_is_kind(p) && ((jl_datatype_t*)p)->isconcretetype) || + (p == (jl_value_t*)jl_typeofbottom_type) || // == Type{Union{}}, so needs to be consistent + (((jl_datatype_t*)p)->name == jl_type_typename && !((jl_datatype_t*)p)->hasfreetypevars)); + } } + if (jl_is_vararg(p)) + p = ((jl_vararg_t*)p)->T; if (istuple && dt->has_concrete_subtype) { - if (jl_is_vararg(p)) - p = ((jl_vararg_t*)p)->T; - // tuple types like Tuple{:x} cannot have instances + // tuple types like Tuple{:x} and Tuple{Union{}} cannot have instances if (p && !jl_is_type(p) && !jl_is_typevar(p)) dt->has_concrete_subtype = 0; + if (p == jl_bottom_type) + dt->has_concrete_subtype = 0; + } + if (dt->maybe_subtype_of_cache) { + dt->maybe_subtype_of_cache = !p || maybe_subtype_of_cache(p, istuple) || !jl_has_free_typevars(p); } } + assert(dt->isconcretetype || dt->isdispatchtuple ? dt->maybe_subtype_of_cache : 1); if (dt->name == jl_type_typename) { - cacheable = 0; // the cache for Type ignores parameter normalization, so it can't be used as a regular hash + cacheable = 0; // n.b. the cache for Type ignores parameter normalization, so it can't be used to make a stable hash value jl_value_t *p = jl_tparam(dt, 0); if (!jl_is_type(p) && !jl_is_typevar(p)) // Type{v} has no subtypes, if v is not a Type dt->has_concrete_subtype = 0; + dt->maybe_subtype_of_cache = 1; } dt->hash = typekey_hash(dt->name, jl_svec_data(dt->parameters), l, cacheable); - dt->cached_by_hash = cacheable ? (typekey_hash(dt->name, jl_svec_data(dt->parameters), l, 0) != 0) : (dt->hash != 0); } static void check_datatype_parameters(jl_typename_t *tn, jl_value_t **params, size_t np) @@ -2046,8 +2130,9 @@ void jl_init_types(void) JL_GC_DISABLED jl_nonfunction_mt = jl_any_type->name->mt; jl_any_type->name->mt = NULL; - jl_type_type = (jl_unionall_t*)jl_new_abstracttype((jl_value_t*)jl_symbol("Type"), core, jl_any_type, jl_emptysvec); - jl_type_typename = ((jl_datatype_t*)jl_type_type)->name; + jl_datatype_t *type_type = jl_new_abstracttype((jl_value_t*)jl_symbol("Type"), core, jl_any_type, jl_emptysvec); + jl_type_type = (jl_unionall_t*)type_type; + jl_type_typename = type_type->name; jl_type_type_mt = jl_new_method_table(jl_type_typename->name, core); jl_type_typename->mt = jl_type_type_mt; @@ -2055,7 +2140,7 @@ void jl_init_types(void) JL_GC_DISABLED // NOTE: types are not actually mutable, but we want to ensure they are heap-allocated with stable addresses jl_datatype_type->name = jl_new_typename_in(jl_symbol("DataType"), core, 0, 1); jl_datatype_type->name->wrapper = (jl_value_t*)jl_datatype_type; - jl_datatype_type->super = (jl_datatype_t*)jl_type_type; + jl_datatype_type->super = type_type; jl_datatype_type->parameters = jl_emptysvec; jl_datatype_type->name->n_uninitialized = 8 - 3; jl_datatype_type->name->names = jl_perm_symsvec(8, @@ -2066,7 +2151,7 @@ void jl_init_types(void) JL_GC_DISABLED "instance", "layout", "hash", - "flags"); // "hasfreetypevars", "isconcretetype", "isdispatchtuple", "isbitstype", "zeroinit", "has_concrete_subtype", "cached_by_hash" + "flags"); // "hasfreetypevars", "isconcretetype", "isdispatchtuple", "isbitstype", "zeroinit", "has_concrete_subtype", "maybe_subtype_of_cache" jl_datatype_type->types = jl_svec(8, jl_typename_type, jl_datatype_type, @@ -2095,6 +2180,11 @@ void jl_init_types(void) JL_GC_DISABLED "hash", "n_uninitialized", "flags", // "abstract", "mutable", "mayinlinealloc", "max_methods"); + const static uint32_t typename_constfields[1] = { 0x00003a3f }; // (1<<0)|(1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<5)|(1<<9)|(1<<11)|(1<<12)|(1<<13) + const static uint32_t typename_atomicfields[1] = { 0x00000180 }; // (1<<7)|(1<<8) + jl_typename_type->name->constfields = typename_constfields; + jl_typename_type->name->atomicfields = typename_atomicfields; + jl_precompute_memoized_dt(jl_typename_type, 1); jl_typename_type->types = jl_svec(15, jl_symbol_type, jl_any_type /*jl_module_type*/, jl_simplevector_type, jl_any_type/*jl_voidpointer_type*/, jl_any_type/*jl_voidpointer_type*/, jl_type_type, jl_type_type, jl_simplevector_type, jl_simplevector_type, @@ -2102,11 +2192,6 @@ void jl_init_types(void) JL_GC_DISABLED jl_any_type /*jl_long_type*/, jl_any_type /*jl_int32_type*/, jl_any_type /*jl_uint8_type*/, jl_any_type /*jl_uint8_type*/); - const static uint32_t typename_constfields[1] = { 0x00003a3f }; // (1<<0)|(1<<1)|(1<<2)|(1<<3)|(1<<4)|(1<<5)|(1<<9)|(1<<11)|(1<<12)|(1<<13) - const static uint32_t typename_atomicfields[1] = { 0x00000180 }; // (1<<7)|(1<<8) - jl_typename_type->name->constfields = typename_constfields; - jl_typename_type->name->atomicfields = typename_atomicfields; - jl_precompute_memoized_dt(jl_typename_type, 1); jl_methtable_type->name = jl_new_typename_in(jl_symbol("MethodTable"), core, 0, 1); jl_methtable_type->name->wrapper = (jl_value_t*)jl_methtable_type; @@ -2118,16 +2203,16 @@ void jl_init_types(void) JL_GC_DISABLED "leafcache", "cache", "max_args", "module", "backedges", "", "", "offs", ""); - jl_methtable_type->types = jl_svec(11, jl_symbol_type, jl_any_type, jl_any_type, - jl_any_type, jl_any_type/*jl_long*/, - jl_any_type/*module*/, jl_any_type/*any vector*/, - jl_any_type/*voidpointer*/, jl_any_type/*int32*/, - jl_any_type/*uint8*/, jl_any_type/*uint8*/); const static uint32_t methtable_constfields[1] = { 0x00000020 }; // (1<<5); const static uint32_t methtable_atomicfields[1] = { 0x0000001e }; // (1<<1)|(1<<2)|(1<<3)|(1<<4); jl_methtable_type->name->constfields = methtable_constfields; jl_methtable_type->name->atomicfields = methtable_atomicfields; jl_precompute_memoized_dt(jl_methtable_type, 1); + jl_methtable_type->types = jl_svec(11, jl_symbol_type, jl_any_type, jl_any_type, + jl_any_type, jl_any_type/*jl_long*/, + jl_any_type/*module*/, jl_any_type/*any vector*/, + jl_any_type/*voidpointer*/, jl_any_type/*int32*/, + jl_any_type/*uint8*/, jl_any_type/*uint8*/); jl_symbol_type->name = jl_new_typename_in(jl_symbol("Symbol"), core, 0, 1); jl_symbol_type->name->wrapper = (jl_value_t*)jl_symbol_type; @@ -2156,19 +2241,6 @@ void jl_init_types(void) JL_GC_DISABLED jl_astaggedvalue(jl_nothing)->header = ((uintptr_t)jl_nothing_type) | GC_OLD_MARKED; jl_nothing_type->instance = jl_nothing; - jl_datatype_t *type_type = (jl_datatype_t*)jl_type_type; - jl_typeofbottom_type = jl_new_datatype(jl_symbol("TypeofBottom"), core, type_type, jl_emptysvec, - jl_emptysvec, jl_emptysvec, jl_emptysvec, 0, 0, 0); - jl_bottom_type = jl_new_struct(jl_typeofbottom_type); - jl_typeofbottom_type->instance = jl_bottom_type; - - jl_uniontype_type = jl_new_datatype(jl_symbol("Union"), core, type_type, jl_emptysvec, - jl_perm_symsvec(2, "a", "b"), - jl_svec(2, jl_any_type, jl_any_type), - jl_emptysvec, 0, 0, 2); - // It seems like we probably usually end up needing the box for kinds (used in an Any context), so force it to exist - jl_uniontype_type->name->mayinlinealloc = 0; - jl_tvar_type = jl_new_datatype(jl_symbol("TypeVar"), core, jl_any_type, jl_emptysvec, jl_perm_symsvec(3, "name", "lb", "ub"), jl_svec(3, jl_symbol_type, jl_any_type, jl_any_type), @@ -2176,16 +2248,38 @@ void jl_init_types(void) JL_GC_DISABLED const static uint32_t tvar_constfields[1] = { 0x00000007 }; // all fields are constant, even though TypeVar itself has identity jl_tvar_type->name->constfields = tvar_constfields; + jl_typeofbottom_type = jl_new_datatype(jl_symbol("TypeofBottom"), core, type_type, jl_emptysvec, + jl_emptysvec, jl_emptysvec, jl_emptysvec, 0, 0, 0); + jl_bottom_type = jl_new_struct(jl_typeofbottom_type); + jl_typeofbottom_type->instance = jl_bottom_type; + jl_unionall_type = jl_new_datatype(jl_symbol("UnionAll"), core, type_type, jl_emptysvec, jl_perm_symsvec(2, "var", "body"), jl_svec(2, jl_tvar_type, jl_any_type), jl_emptysvec, 0, 0, 2); + // It seems like we probably usually end up needing the box for kinds (often used in an Any context), so force it to exist jl_unionall_type->name->mayinlinealloc = 0; + jl_uniontype_type = jl_new_datatype(jl_symbol("Union"), core, type_type, jl_emptysvec, + jl_perm_symsvec(2, "a", "b"), + jl_svec(2, jl_any_type, jl_any_type), + jl_emptysvec, 0, 0, 2); + // It seems like we probably usually end up needing the box for kinds (often used in an Any context), so force it to exist + jl_uniontype_type->name->mayinlinealloc = 0; + + jl_tvar_t *tttvar = tvar("T"); + type_type->parameters = jl_svec(1, tttvar); + jl_precompute_memoized_dt(type_type, 0); // update the hash value ASAP + type_type->hasfreetypevars = 1; + type_type->ismutationfree = 1; + jl_type_typename->wrapper = jl_new_struct(jl_unionall_type, tttvar, (jl_value_t*)jl_type_type); + jl_type_type = (jl_unionall_t*)jl_type_typename->wrapper; + jl_vararg_type = jl_new_datatype(jl_symbol("TypeofVararg"), core, jl_any_type, jl_emptysvec, jl_perm_symsvec(2, "T", "N"), jl_svec(2, jl_any_type, jl_any_type), jl_emptysvec, 0, 0, 0); + // It seems like we probably usually end up needing the box for kinds (often used in an Any context), so force it to exist jl_vararg_type->name->mayinlinealloc = 0; jl_svec_t *anytuple_params = jl_svec(1, jl_wrap_vararg((jl_value_t*)jl_any_type, (jl_value_t*)NULL)); @@ -2195,19 +2289,10 @@ void jl_init_types(void) JL_GC_DISABLED // fix some miscomputed values, since we didn't know this was going to be a Tuple in jl_precompute_memoized_dt jl_tuple_typename->wrapper = (jl_value_t*)jl_anytuple_type; // remove UnionAll wrappers jl_anytuple_type->isconcretetype = 0; + jl_anytuple_type->maybe_subtype_of_cache = 0; jl_anytuple_type->layout = NULL; - jl_anytuple_type->cached_by_hash = 0; - - jl_tvar_t *tttvar = tvar("T"); - ((jl_datatype_t*)jl_type_type)->parameters = jl_svec(1, tttvar); - ((jl_datatype_t*)jl_type_type)->hasfreetypevars = 1; - ((jl_datatype_t*)jl_type_type)->cached_by_hash = 0; - jl_type_typename->wrapper = jl_new_struct(jl_unionall_type, tttvar, (jl_value_t*)jl_type_type); - jl_type_type = (jl_unionall_t*)jl_type_typename->wrapper; - ((jl_datatype_t*)jl_type_type->body)->ismutationfree = 1; jl_typeofbottom_type->super = jl_wrap_Type(jl_bottom_type); - jl_emptytuple_type = jl_apply_tuple_type(jl_emptysvec); jl_emptytuple = jl_gc_permobj(0, jl_emptytuple_type); jl_emptytuple_type->instance = jl_emptytuple; diff --git a/src/julia.h b/src/julia.h index 6b0d6aec85cab..8a9e4ada487e2 100644 --- a/src/julia.h +++ b/src/julia.h @@ -548,7 +548,7 @@ typedef struct _jl_datatype_t { uint16_t isbitstype:1; // relevant query for C-api and type-parameters uint16_t zeroinit:1; // if one or more fields requires zero-initialization uint16_t has_concrete_subtype:1; // If clear, no value will have this datatype - uint16_t cached_by_hash:1; // stored in hash-based set cache (instead of linear cache) + uint16_t maybe_subtype_of_cache:1; // Computational bit for has_concrete_supertype. See description in jltypes.c. uint16_t isprimitivetype:1; // whether this is declared with 'primitive type' keyword (sized, no fields, and immutable) uint16_t ismutationfree:1; // whether any mutable memory is reachable through this type (in the type or via fields) uint16_t isidentityfree:1; // whether this type or any object reachable through its fields has non-content-based identity @@ -1414,7 +1414,6 @@ STATIC_INLINE int jl_egal_(const jl_value_t *a JL_MAYBE_UNROOTED, const jl_value #define jl_egal(a, b) jl_egal_((a), (b)) // type predicates and basic operations -JL_DLLEXPORT int jl_type_equality_is_identity(jl_value_t *t1, jl_value_t *t2) JL_NOTSAFEPOINT; JL_DLLEXPORT int jl_has_free_typevars(jl_value_t *v) JL_NOTSAFEPOINT; JL_DLLEXPORT int jl_has_typevar(jl_value_t *t, jl_tvar_t *v) JL_NOTSAFEPOINT; JL_DLLEXPORT int jl_has_typevar_from_unionall(jl_value_t *t, jl_unionall_t *ua); diff --git a/src/julia_internal.h b/src/julia_internal.h index b77de64732116..0e5e3b6cdd6ef 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -722,6 +722,7 @@ void jl_init_main_module(void); JL_DLLEXPORT int jl_is_submodule(jl_module_t *child, jl_module_t *parent) JL_NOTSAFEPOINT; jl_array_t *jl_get_loaded_modules(void); JL_DLLEXPORT int jl_datatype_isinlinealloc(jl_datatype_t *ty, int pointerfree); +int jl_type_equality_is_identity(jl_value_t *t1, jl_value_t *t2) JL_NOTSAFEPOINT; void jl_eval_global_expr(jl_module_t *m, jl_expr_t *ex, int set_type); jl_value_t *jl_toplevel_eval_flex(jl_module_t *m, jl_value_t *e, int fast, int expanded); diff --git a/src/subtype.c b/src/subtype.c index cb6c9531c33ca..10a9f9c924421 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -306,11 +306,8 @@ static int obviously_unequal(jl_value_t *a, jl_value_t *b) if (ad->name != bd->name) return 1; int istuple = (ad->name == jl_tuple_typename); - if ((jl_is_concrete_type(a) || jl_is_concrete_type(b)) && - jl_type_equality_is_identity(a, b)) { - if (!istuple && ad->name != jl_type_typename) // HACK: can't properly normalize Tuple{Float64} == Tuple{<:Float64} like types or Type{T} types - return 1; - } + if (jl_type_equality_is_identity(a, b)) + return 1; size_t i, np; if (istuple) { size_t na = jl_nparams(ad), nb = jl_nparams(bd); @@ -395,10 +392,7 @@ static int obviously_disjoint(jl_value_t *a, jl_value_t *b, int specificity) return 0; if (specificity && a == (jl_value_t*)jl_typeofbottom_type) return 0; - if (jl_is_concrete_type(a) && jl_is_concrete_type(b) && - jl_type_equality_is_identity(a, b) && - (((jl_datatype_t*)a)->name != jl_tuple_typename || - ((jl_datatype_t*)b)->name != jl_tuple_typename)) + if (jl_is_concrete_type(a) && jl_is_concrete_type(b) && jl_type_equality_is_identity(a, b)) return 1; if (jl_is_unionall(a)) a = jl_unwrap_unionall(a); if (jl_is_unionall(b)) b = jl_unwrap_unionall(b); @@ -1766,7 +1760,7 @@ static int obvious_subtype(jl_value_t *x, jl_value_t *y, jl_value_t *y0, int *su if (jl_is_datatype(y)) { int istuple = (((jl_datatype_t*)y)->name == jl_tuple_typename); int iscov = istuple; - // TODO: this would be a nice fast-path to have, unfortuanately, + // TODO: this would be a nice fast-path to have, unfortunately, // datatype allocation fails to correctly hash-cons them // and the subtyping tests include tests for this case //if (!iscov && ((jl_datatype_t*)y)->isconcretetype && !jl_is_type_type(x)) { @@ -2253,7 +2247,7 @@ JL_DLLEXPORT int jl_isa(jl_value_t *x, jl_value_t *t) return 0; } } - if (jl_is_concrete_type(t) && jl_type_equality_is_identity(jl_typeof(x), t)) + if (jl_is_concrete_type(t)) return 0; return jl_subtype(jl_typeof(x), t); } @@ -3735,6 +3729,7 @@ static jl_value_t *switch_union_tuple(jl_value_t *a, jl_value_t *b) // `a` might have a non-empty intersection with some concrete type b even if !(a<:b) and !(b<:a) // For example a=`Tuple{Type{<:Vector}}` and b=`Tuple{DataType}` +// TODO: this query is partly available memoized as jl_type_equality_is_identity static int might_intersect_concrete(jl_value_t *a) { if (jl_is_unionall(a)) @@ -3784,9 +3779,9 @@ jl_value_t *jl_type_intersection_env_s(jl_value_t *a, jl_value_t *b, jl_svec_t * *ans = a; sz = szb; if (issubty) *issubty = 1; } - else if (lta && ltb) { - goto bot; - } + // else if (lta && ltb) { // !jl_type_equality_is_identity known in this case because obviously_disjoint returned false + // goto bot; + // } else if (jl_subtype(b, a)) { *ans = b; } diff --git a/test/specificity.jl b/test/specificity.jl index 1a5c117ce5d9d..5808ac71fa54b 100644 --- a/test/specificity.jl +++ b/test/specificity.jl @@ -214,7 +214,7 @@ f27361(::M) where M <: Tuple{3} = nothing @test length(methods(f27361)) == 2 # specificity of TypeofBottom -@test args_morespecific(Tuple{Core.TypeofBottom}, Tuple{DataType}) +@test !args_morespecific(Tuple{DataType}, Tuple{Core.TypeofBottom}) @test args_morespecific(Tuple{Core.TypeofBottom}, Tuple{Type{<:Tuple}}) @test args_morespecific(Tuple{Type{Any}, Type}, Tuple{Type{T}, Type{T}} where T)