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 dfb90df06074f..24e3931ff0ae7 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); @@ -1756,7 +1750,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)) { @@ -2243,7 +2237,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); } @@ -3725,6 +3719,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)) @@ -3774,9 +3769,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)