diff --git a/Compiler/test/inference.jl b/Compiler/test/inference.jl index c94059f59f6e7..1d3dcdcccd581 100644 --- a/Compiler/test/inference.jl +++ b/Compiler/test/inference.jl @@ -184,6 +184,32 @@ Base.ndims(g::e43296) = ndims(typeof(g)) @test Compiler.unioncomplexity(Tuple{Vararg{Union{Symbol, Tuple{Vararg{Union{Symbol, Tuple{Vararg{Symbol}}}}}}}}) == 5 @test Compiler.unioncomplexity(Tuple{Vararg{Union{Symbol, Tuple{Vararg{Union{Symbol, Tuple{Vararg{Union{Symbol, Tuple{Vararg{Symbol}}}}}}}}}}}) == 7 +struct A57764{X} + x::X +end + +struct B57764{X,Y} + x::X + y::Y + B57764{X,Y}(x::X) where {X,Y} = new{X,Y}(x) +end + +@test !Compiler.valid_as_lattice(A57764{Union{}}, true) +@test !Compiler.valid_as_lattice(A57764{<:Union{}}, true) +@test Compiler.valid_as_lattice(A57764{Int}, true) +@test Compiler.valid_as_lattice(A57764{<:Int}, true) +@test !Compiler.valid_as_lattice(A57764{Type{1}}, true) +@test Compiler.valid_as_lattice(B57764{Int,Int}, true) +@test Compiler.valid_as_lattice(B57764{<:Int,Int}, true) +@test Compiler.valid_as_lattice(B57764{Int,Union{}}, true) +@test Compiler.valid_as_lattice(B57764{<:Int,Union{}}, true) +@test !Compiler.valid_as_lattice(B57764{Union{}, Int}, true) +@test !Compiler.valid_as_lattice(B57764{Union{}, <:Int}, true) +@test !Compiler.valid_as_lattice(B57764{<:Union{}, <:Int}, true) +@test !Compiler.valid_as_lattice(Array{1}, true) +@test !Compiler.valid_as_lattice(Memory{1}, true) +@test !Compiler.valid_as_lattice(Tuple{1}, true) +@test !Compiler.valid_as_lattice(Type{1}, true) # PR 22120 function tuplemerge_test(a, b, r, commutative=true) diff --git a/src/cgutils.cpp b/src/cgutils.cpp index d9b7b98e40ef4..a4dcad78d02f8 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -3009,7 +3009,8 @@ static MDNode *best_field_tbaa(jl_codectx_t &ctx, const jl_cgval_t &strct, jl_da { auto tbaa = strct.tbaa; if (tbaa == ctx.tbaa().tbaa_datatype) - if (byte_offset != offsetof(jl_datatype_t, types)) + if (byte_offset != offsetof(jl_datatype_t, types) && + byte_offset != offsetof(jl_datatype_t, hash) + 4) // flags return ctx.tbaa().tbaa_const; if (tbaa == ctx.tbaa().tbaa_array) { if (jl_is_genericmemory_type(jt)) { diff --git a/src/datatype.c b/src/datatype.c index f2f633adc3623..047ca32a6d837 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -504,7 +504,6 @@ void jl_get_genericmemory_layout(jl_datatype_t *st) // this is expected to have a layout, but since it is not constructable, we don't care too much what it is static const jl_datatype_layout_t opaque_ptr_layout = {0, 0, 1, -1, sizeof(void*), {0}}; st->layout = &opaque_ptr_layout; - st->has_concrete_subtype = 0; return; } @@ -573,7 +572,6 @@ void jl_get_genericmemory_layout(jl_datatype_t *st) assert(!st->layout); st->layout = jl_get_layout(elsz, nfields, npointers, al, haspadding, isbitsegal, arrayelem, NULL, pointers); st->zeroinit = zi; - //st->has_concrete_subtype = 1; //st->isbitstype = 0; //st->ismutationfree = 0; //st->isidentityfree = 0; @@ -602,7 +600,6 @@ void jl_compute_field_offsets(jl_datatype_t *st) // this check allows us to force re-computation of the layout for some types during init st->layout = NULL; st->zeroinit = 0; - st->has_concrete_subtype = 1; } if (st->name == jl_genericmemory_typename) { jl_get_genericmemory_layout(st); @@ -616,7 +613,6 @@ void jl_compute_field_offsets(jl_datatype_t *st) if (w->layout) { st->layout = w->layout; st->zeroinit = w->zeroinit; - st->has_concrete_subtype = w->has_concrete_subtype; if (!jl_is_layout_opaque(st->layout)) { // e.g. jl_simplevector_type st->isbitstype = isbitstype && st->layout->npointers == 0; jl_maybe_allocate_singleton_instance(st); @@ -645,14 +641,6 @@ void jl_compute_field_offsets(jl_datatype_t *st) } } else { - // compute a conservative estimate of whether there could exist an instance of a subtype of this - for (i = 0; st->has_concrete_subtype && i < nfields - st->name->n_uninitialized; i++) { - jl_value_t *fld = jl_svecref(st->types, i); - if (fld == jl_bottom_type) - st->has_concrete_subtype = 0; - else - st->has_concrete_subtype = !jl_is_datatype(fld) || ((jl_datatype_t *)fld)->has_concrete_subtype; - } // compute layout for the wrapper object if the field types have no free variables if (!st->isconcretetype && !jl_has_fixed_layout(st)) { assert(st == w); // otherwise caller should not have requested this layout diff --git a/src/gf.c b/src/gf.c index d43a2d32e2759..4119145e3b609 100644 --- a/src/gf.c +++ b/src/gf.c @@ -4599,7 +4599,7 @@ static jl_value_t *ml_matches(jl_methtable_t *mt, // that type will not be constructable, for example, tested recursively int jl_has_concrete_subtype(jl_value_t *typ) { - if (typ == jl_bottom_type) + if (typ == jl_bottom_type || (jl_is_typevar(typ) && ((jl_tvar_t*)typ)->ub == jl_bottom_type)) return 0; typ = jl_unwrap_unionall(typ); if (jl_is_vararg(typ)) diff --git a/src/jltypes.c b/src/jltypes.c index cda47118541dd..ec9af4f2dd104 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -1833,6 +1833,43 @@ static unsigned typekeyvalue_hash(jl_typename_t *tn, jl_value_t *key1, jl_value_ return hash ? hash : 1; } +int jl_is_type_valid_for_concrete_subtype(jl_value_t *dt) { + if (!dt) + return 0; + if (jl_is_typevar(dt)) + dt = ((jl_tvar_t*)dt)->ub; + if (jl_is_vararg(dt)) + dt = ((jl_vararg_t*)dt)->T; + if (dt == jl_bottom_type || (dt && !jl_is_type(dt))) + return 0; + return 1; +} + +// set the `has_concrete_subtype` flag of `dt` to zero for type parameters +// that are illegal for certain types +int jl_are_tparams_valid_for_concrete_subtype(jl_datatype_t *dt) { + if (dt->name == jl_type_typename) { + jl_value_t *t = jl_tparam0(dt); + if (t && !jl_is_type(t) && !jl_is_typevar(t)) + return 0; + } + if (dt->name == jl_genericmemory_typename) { + jl_value_t *t = jl_tparam1(dt); + if (t && !jl_is_type(t) && !jl_is_typevar(t)) + return 0; + } + if (dt->name == jl_tuple_typename) { + size_t i, l = jl_nparams(dt); + for (i = 0; i < l; i++) { + jl_value_t *t = jl_tparam(dt, i); + if (!jl_is_type_valid_for_concrete_subtype(t)) + return 0; + } + } + + return dt->has_concrete_subtype; +} + void jl_precompute_memoized_dt(jl_datatype_t *dt, int cacheable) { int istuple = (dt->name == jl_tuple_typename); @@ -1840,6 +1877,7 @@ void jl_precompute_memoized_dt(jl_datatype_t *dt, int cacheable) dt->maybe_subtype_of_cache = 1; dt->isconcretetype = !dt->name->abstract; dt->isdispatchtuple = istuple; + dt->has_concrete_subtype = jl_are_tparams_valid_for_concrete_subtype(dt); size_t i, l = jl_nparams(dt); for (i = 0; i < l; i++) { jl_value_t *p = jl_tparam(dt, i); @@ -1860,13 +1898,6 @@ void jl_precompute_memoized_dt(jl_datatype_t *dt, int cacheable) } if (jl_is_vararg(p)) p = ((jl_vararg_t*)p)->T; - if (istuple && dt->has_concrete_subtype) { - // 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); } @@ -1874,8 +1905,6 @@ void jl_precompute_memoized_dt(jl_datatype_t *dt, int cacheable) assert(dt->isconcretetype || dt->isdispatchtuple ? dt->maybe_subtype_of_cache : 1); if (dt->name == jl_type_typename) { 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; jl_value_t *uw = jl_unwrap_unionall(p); // n.b. the cache for Type ignores parameter normalization except for Typeofwrapper, so it can't be used to make a stable hash value @@ -2433,8 +2462,10 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value // recursively instantiate the types of the fields if (dt->types == NULL) ndt->types = jl_compute_fieldtypes(ndt, stack, cacheable); - else + else { ndt->types = inst_ftypes(ftypes, env, stack, cacheable); + jl_compute_has_concrete_subtype_from_fields(ndt); + } jl_gc_wb(ndt, ndt->types); } } @@ -2844,6 +2875,17 @@ jl_vararg_t *jl_wrap_vararg(jl_value_t *t, jl_value_t *n, int check, int nothrow return vm; } +// compute a conservative estimate of whether there could exist an instance of a subtype of this +void jl_compute_has_concrete_subtype_from_fields(jl_datatype_t *dt) { + size_t nfields = jl_svec_len(dt->types); + for (size_t i = 0; dt->has_concrete_subtype && i < nfields - dt->name->n_uninitialized; i++) { + jl_value_t *fld = jl_svecref(dt->types, i); + dt->has_concrete_subtype = jl_is_type_valid_for_concrete_subtype(fld); + if (jl_is_datatype(fld)) + dt->has_concrete_subtype &= jl_are_tparams_valid_for_concrete_subtype((jl_datatype_t*)fld); + } +} + JL_DLLEXPORT jl_svec_t *jl_compute_fieldtypes(jl_datatype_t *st JL_PROPAGATES_ROOT, void *stack, int cacheable) { assert(st->name != jl_namedtuple_typename && st->name != jl_tuple_typename); @@ -2866,10 +2908,10 @@ JL_DLLEXPORT jl_svec_t *jl_compute_fieldtypes(jl_datatype_t *st JL_PROPAGATES_RO top.prev = (jl_typestack_t*)stack; st->types = inst_ftypes(wt->types, &env[n - 1], &top, cacheable); jl_gc_wb(st, st->types); + jl_compute_has_concrete_subtype_from_fields(st); return st->types; } - void jl_reinstantiate_inner_types(jl_datatype_t *t) // can throw! { assert(jl_is_datatype(t)); @@ -2914,6 +2956,7 @@ void jl_reinstantiate_inner_types(jl_datatype_t *t) // can throw! assert(ndt->types == NULL); ndt->types = inst_ftypes(t->types, &env[n - 1], &top, 1); jl_gc_wb(ndt, ndt->types); + jl_compute_has_concrete_subtype_from_fields(ndt); if (ndt->isconcretetype) { // cacheable jl_compute_field_offsets(ndt); } diff --git a/src/julia_internal.h b/src/julia_internal.h index 0ecc243dcc493..7d376589191ac 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -931,6 +931,7 @@ JL_DLLEXPORT jl_methtable_t *jl_method_get_table( JL_DLLEXPORT int jl_pointer_egal(jl_value_t *t); JL_DLLEXPORT jl_value_t *jl_nth_slot_type(jl_value_t *sig JL_PROPAGATES_ROOT, size_t i) JL_NOTSAFEPOINT; void jl_compute_field_offsets(jl_datatype_t *st); +void jl_compute_has_concrete_subtype_from_fields(jl_datatype_t *dt); void jl_module_run_initializer(jl_module_t *m); JL_DLLEXPORT jl_binding_t *jl_get_module_binding(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var, int alloc); JL_DLLEXPORT void jl_binding_deprecation_warning(jl_binding_t *b);