From a8c8377cc43f024b0c02200f1cd7e0c1ab83caca Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 24 Apr 2019 14:29:07 -0400 Subject: [PATCH] types: lazy initialize the field-types when first needed For concrete datatypes, we still always initialize the fieldtypes immediately (so that we can immediately also compute the layout and other related properties). Note that this implies that constructing the fieldtype is no longer part of subtyping, (since construction no longer verifies these conditions), and so impossible constraints now lead to a computed value of `Union{}` being given for the type of that field, instead of throwing an error when trying to allocate the type. It might be possible to instead store this error, and rethrow it when someone tries to construct a (partially initialized) copy of the type and/or when the user (but not inference or introspection tools) tries to examine the fieldtype of the object. But I think this extra complexity (and additional failure cases to consider) isn't worthwhile to add. --- base/compiler/tfuncs.jl | 29 +++++++------- base/reflection.jl | 13 ++++-- base/show.jl | 2 +- base/summarysize.jl | 4 +- src/array.c | 7 ++-- src/builtins.c | 7 ++-- src/cgutils.cpp | 14 ++++--- src/datatype.c | 10 ++--- src/dump.c | 2 +- src/gf.c | 12 +++--- src/interpreter.c | 15 ++++--- src/jlapi.c | 6 +++ src/jltypes.c | 88 ++++++++++++++++++++++++++++------------- src/julia.h | 8 +++- src/subtype.c | 2 +- src/typemap.c | 36 ++++++++--------- test/core.jl | 36 +++++++++++------ test/precompile.jl | 15 +++---- 18 files changed, 190 insertions(+), 116 deletions(-) diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 00dce1b6a3ddd..1c56916cbaec6 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -338,7 +338,7 @@ function nfields_tfunc(@nospecialize(x)) x = widenconst(x) if isa(x, DataType) && !x.abstract && !(x.name === Tuple.name && isvatuple(x)) if !(x.name === _NAMEDTUPLE_NAME && !isconcretetype(x)) - return Const(length(x.types)) + return Const(isdefined(x, :types) ? length(x.types) : length(x.name.names)) end end return Int @@ -588,7 +588,7 @@ function fieldcount_noerror(@nospecialize t) if abstr return nothing end - return length(t.types) + return isdefined(t, :types) ? length(t.types) : length(t.name.names) end @@ -744,7 +744,8 @@ function getfield_tfunc(@nospecialize(s00), @nospecialize(name)) # TODO: better approximate inference return Any end - if isempty(s.types) + ftypes = datatype_fieldtypes(s) + if isempty(ftypes) return Bottom end if isa(name, Conditional) @@ -754,27 +755,27 @@ function getfield_tfunc(@nospecialize(s00), @nospecialize(name)) if !(Int <: name || Symbol <: name) return Bottom end - if length(s.types) == 1 - return rewrap_unionall(unwrapva(s.types[1]), s00) + if length(ftypes) == 1 + return rewrap_unionall(unwrapva(ftypes[1]), s00) end # union together types of all fields t = Bottom - for _ft in s.types + for _ft in ftypes t = tmerge(t, rewrap_unionall(unwrapva(_ft), s00)) t === Any && break end return t end fld = name.val - if isa(fld,Symbol) + if isa(fld, Symbol) fld = fieldindex(s, fld, false) end - if !isa(fld,Int) + if !isa(fld, Int) return Bottom end - nf = length(s.types) - if s <: Tuple && fld >= nf && isvarargtype(s.types[nf]) - return rewrap_unionall(unwrapva(s.types[nf]), s00) + nf = length(ftypes) + if s <: Tuple && fld >= nf && isvarargtype(ftypes[nf]) + return rewrap_unionall(unwrapva(ftypes[nf]), s00) end if fld < 1 || fld > nf return Bottom @@ -790,7 +791,7 @@ function getfield_tfunc(@nospecialize(s00), @nospecialize(name)) t = const_datatype_getfield_tfunc(sp, fld) t !== nothing && return t end - R = s.types[fld] + R = ftypes[fld] if isempty(s.parameters) return R end @@ -843,7 +844,7 @@ function _fieldtype_nothrow(@nospecialize(s), exact::Bool, name::Const) fld = fieldindex(u, fld, false) end isa(fld, Int) || return false - ftypes = u.types + ftypes = datatype_fieldtypes(u) nf = length(ftypes) (fld >= 1 && fld <= nf) || return false if u.name === Tuple.name && fld >= nf && isvarargtype(ftypes[nf]) @@ -893,7 +894,7 @@ function _fieldtype_tfunc(@nospecialize(s), exact::Bool, @nospecialize(name)) # TODO: better approximate inference return Type end - ftypes = u.types + ftypes = datatype_fieldtypes(u) if isempty(ftypes) return Bottom end diff --git a/base/reflection.jl b/base/reflection.jl index abf5268922933..acc24dba51dbc 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -313,6 +313,8 @@ objectid(@nospecialize(x)) = ccall(:jl_object_id, UInt, (Any,), x) # concrete datatype predicates +datatype_fieldtypes(x::DataType) = ccall(:jl_get_fieldtypes, Any, (Any,), x) + struct DataTypeLayout nfields::UInt32 alignment::UInt32 @@ -407,7 +409,8 @@ function isstructtype(@nospecialize(t::Type)) t = unwrap_unionall(t) # TODO: what to do for `Union`? isa(t, DataType) || return false - return length(t.types) != 0 || (t.size == 0 && !t.abstract) + hasfield = !isdefined(t, :types) || !isempty(t.types) + return hasfield || (t.size == 0 && !t.abstract) end """ @@ -421,7 +424,8 @@ function isprimitivetype(@nospecialize(t::Type)) t = unwrap_unionall(t) # TODO: what to do for `Union`? isa(t, DataType) || return false - return length(t.types) == 0 && t.size != 0 && !t.abstract + hasfield = !isdefined(t, :types) || !isempty(t.types) + return !hasfield && t.size != 0 && !t.abstract end """ @@ -674,7 +678,10 @@ function fieldcount(@nospecialize t) if abstr throw(ArgumentError("type does not have a definite number of fields")) end - return length(t.types) + if isdefined(t, :types) + return length(t.types) + end + return length(t.name.names) end """ diff --git a/base/show.jl b/base/show.jl index 29320479f7534..69ac6b2bb8d76 100644 --- a/base/show.jl +++ b/base/show.jl @@ -1729,7 +1729,7 @@ function dump(io::IOContext, x::DataType, n::Int, indent) end end fields = fieldnames(x) - fieldtypes = x.types + fieldtypes = datatype_fieldtypes(x) for idx in 1:length(fields) println(io) print(io, indent, " ", fields[idx], "::") diff --git a/base/summarysize.jl b/base/summarysize.jl index c6cac492b88c4..3ae3ee135d531 100644 --- a/base/summarysize.jl +++ b/base/summarysize.jl @@ -89,7 +89,9 @@ function (ss::SummarySize)(obj::DataType) size::Int = 7 * Core.sizeof(Int) + 6 * Core.sizeof(Int32) size += 4 * nfields(obj) + ifelse(Sys.WORD_SIZE == 64, 4, 0) size += ss(obj.parameters)::Int - size += ss(obj.types)::Int + if isdefined(obj, :types) + size += ss(obj.types)::Int + end return size end diff --git a/src/array.c b/src/array.c index 2744eafeab958..1afceb7054a56 100644 --- a/src/array.c +++ b/src/array.c @@ -171,9 +171,10 @@ static inline int is_ntuple_long(jl_value_t *v) { if (!jl_is_tuple(v)) return 0; - size_t nfields = jl_nfields(v); - for (size_t i = 0; i < nfields; i++) { - if (jl_field_type(jl_typeof(v), i) != (jl_value_t*)jl_long_type) { + jl_value_t *tt = jl_typeof(v); + size_t i, nfields = jl_nparams(tt); + for (i = 0; i < nfields; i++) { + if (jl_tparam(tt, i) != (jl_value_t*)jl_long_type) { return 0; } } diff --git a/src/builtins.c b/src/builtins.c index ba8eee130288e..a14167567ee3d 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -752,7 +752,8 @@ static jl_value_t *get_fieldtype(jl_value_t *t, jl_value_t *f, int dothrow) return (jl_value_t*)jl_any_type; return get_fieldtype(tt, f, dothrow); } - int nf = jl_field_count(st); + jl_svec_t *types = jl_get_fieldtypes(st); + int nf = jl_svec_len(types); if (nf > 0 && field_index >= nf-1 && st->name == jl_tuple_typename) { jl_value_t *ft = jl_field_type(st, nf-1); if (jl_is_vararg_type(ft)) @@ -790,8 +791,8 @@ JL_CALLABLE(jl_f_fieldtype) JL_CALLABLE(jl_f_nfields) { JL_NARGS(nfields, 1, 1); - jl_value_t *x = args[0]; - return jl_box_long(jl_field_count(jl_typeof(x))); + jl_datatype_t *xt = (jl_datatype_t*)jl_typeof(args[0]); + return jl_box_long(jl_datatype_nfields(xt)); } JL_CALLABLE(jl_f_isdefined) diff --git a/src/cgutils.cpp b/src/cgutils.cpp index ec1cb73da2ff8..9a2f452a30197 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -569,9 +569,10 @@ static bool julia_struct_has_layout(jl_datatype_t *dt, jl_unionall_t *ua) if (dt->layout || dt->struct_decl || jl_justbits((jl_value_t*)dt)) return true; if (ua) { - size_t i, ntypes = jl_svec_len(dt->types); + jl_svec_t *types = jl_get_fieldtypes(dt); + size_t i, ntypes = jl_svec_len(types); for (i = 0; i < ntypes; i++) { - jl_value_t *ty = jl_svecref(dt->types, i); + jl_value_t *ty = jl_svecref(types, i); if (jl_has_typevar_from_unionall(ty, ua)) return false; } @@ -603,7 +604,8 @@ static Type *julia_struct_to_llvm(jl_value_t *jt, jl_unionall_t *ua, bool *isbox if (jst->struct_decl != NULL) return (Type*)jst->struct_decl; if (jl_is_structtype(jt) && !(jst->layout && jl_is_layout_opaque(jst->layout))) { - size_t i, ntypes = jl_svec_len(jst->types); + jl_svec_t *ftypes = jl_get_fieldtypes(jst); + size_t i, ntypes = jl_svec_len(ftypes); if (ntypes == 0 || (jst->layout && jl_datatype_nbits(jst) == 0)) return T_void; if (!julia_struct_has_layout(jst, ua)) @@ -615,7 +617,7 @@ static Type *julia_struct_to_llvm(jl_value_t *jt, jl_unionall_t *ua, bool *isbox Type *lasttype = NULL; bool allghost = true; for (i = 0; i < ntypes; i++) { - jl_value_t *ty = jl_svecref(jst->types, i); + jl_value_t *ty = jl_svecref(ftypes, i); if (jlasttype != NULL && ty != jlasttype) isvector = false; jlasttype = ty; @@ -1496,7 +1498,7 @@ static bool emit_getfield_unknownidx(jl_codectx_t &ctx, } else if (is_tupletype_homogeneous(stt->types)) { assert(nfields > 0); // nf == 0 trapped by all_pointers case - jl_value_t *jt = jl_field_type(stt, 0); + jl_value_t *jt = jl_svecref(stt->types, 0); idx = emit_bounds_check(ctx, strct, (jl_value_t*)stt, idx, ConstantInt::get(T_size, nfields), inbounds); Value *ptr = maybe_decay_tracked(data_pointer(ctx, strct)); if (!stt->mutabl && !(maybe_null && jt == (jl_value_t*)jl_bool_type)) { @@ -1526,7 +1528,7 @@ static bool emit_getfield_unknownidx(jl_codectx_t &ctx, return true; } assert(!jl_field_isptr(stt, 0)); - jl_value_t *jt = jl_field_type(stt, 0); + jl_value_t *jt = jl_svecref(stt->types, 0); Value *idx0 = emit_bounds_check(ctx, strct, (jl_value_t*)stt, idx, ConstantInt::get(T_size, nfields), inbounds); if (strct.isghost) { *ret = ghostValue(jt); diff --git a/src/datatype.c b/src/datatype.c index dceba56f8ea92..dd2f60769a6de 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -202,7 +202,7 @@ unsigned jl_special_vector_alignment(size_t nfields, jl_value_t *t) if (mask) return 0; // nfields has more than two 1s assert(jl_datatype_nfields(t)==1); - jl_value_t *ty = jl_field_type(t, 0); + jl_value_t *ty = jl_field_type((jl_datatype_t*)t, 0); if (!jl_is_primitivetype(ty)) // LLVM requires that a vector element be a primitive type. // LLVM allows pointer types as vector elements, but until a @@ -301,9 +301,9 @@ void jl_compute_field_offsets(jl_datatype_t *st) // based on whether its definition is self-referential if (w->types != NULL) { st->isbitstype = st->isconcretetype && !st->mutabl; - size_t i, nf = jl_field_count(st); + size_t i, nf = jl_svec_len(st->types); for (i = 0; i < nf; i++) { - jl_value_t *fld = jl_field_type(st, i); + jl_value_t *fld = jl_svecref(st->types, i); if (st->isbitstype) st->isbitstype = jl_is_datatype(fld) && ((jl_datatype_t*)fld)->isbitstype; if (!st->zeroinit) @@ -311,9 +311,9 @@ void jl_compute_field_offsets(jl_datatype_t *st) } if (st->isbitstype) { st->isinlinealloc = 1; - size_t i, nf = jl_field_count(w); + size_t i, nf = jl_svec_len(w->types); for (i = 0; i < nf; i++) { - jl_value_t *fld = jl_field_type(w, i); + jl_value_t *fld = jl_svecref(w->types, i); if (references_name(fld, w->name)) { st->isinlinealloc = 0; st->isbitstype = 0; diff --git a/src/dump.c b/src/dump.c index 27c8524a2c0b4..3702f29c16613 100644 --- a/src/dump.c +++ b/src/dump.c @@ -1458,7 +1458,7 @@ static jl_value_t *jl_deserialize_datatype(jl_serializer_state *s, int pos, jl_v dt->super = (jl_datatype_t*)jl_deserialize_value(s, (jl_value_t**)&dt->super); jl_gc_wb(dt, dt->super); dt->types = (jl_svec_t*)jl_deserialize_value(s, (jl_value_t**)&dt->types); - jl_gc_wb(dt, dt->types); + if (dt->types) jl_gc_wb(dt, dt->types); return (jl_value_t*)dt; } diff --git a/src/gf.c b/src/gf.c index a50f9a29d9643..1620b5c7a455e 100644 --- a/src/gf.c +++ b/src/gf.c @@ -421,13 +421,13 @@ static int very_general_type(jl_value_t *t) jl_value_t *jl_nth_slot_type(jl_value_t *sig, size_t i) { sig = jl_unwrap_unionall(sig); - size_t len = jl_field_count(sig); + size_t len = jl_nparams(sig); if (len == 0) return NULL; if (i < len-1) return jl_tparam(sig, i); - if (jl_is_vararg_type(jl_tparam(sig,len-1))) - return jl_unwrap_vararg(jl_tparam(sig,len-1)); + if (jl_is_vararg_type(jl_tparam(sig, len-1))) + return jl_unwrap_vararg(jl_tparam(sig, len-1)); if (i == len-1) return jl_tparam(sig, i); return NULL; @@ -473,7 +473,7 @@ static void jl_compilation_sig( jl_value_t *decl = definition->sig; assert(jl_is_tuple_type(tt)); size_t i, np = jl_nparams(tt); - size_t nargs = definition->nargs; // == jl_field_count(jl_unwrap_unionall(decl)); + size_t nargs = definition->nargs; // == jl_nparams(jl_unwrap_unionall(decl)); for (i = 0; i < np; i++) { jl_value_t *elt = jl_tparam(tt, i); jl_value_t *decl_i = jl_nth_slot_type(decl, i); @@ -678,7 +678,7 @@ JL_DLLEXPORT int jl_isa_compileable_sig( return 0; size_t i, np = jl_nparams(type); - size_t nargs = definition->nargs; // == jl_field_count(jl_unwrap_unionall(decl)); + size_t nargs = definition->nargs; // == jl_nparams(jl_unwrap_unionall(decl)); if (np == 0) return nargs == 0; @@ -2565,7 +2565,7 @@ int jl_has_concrete_subtype(jl_value_t *typ) return 1; if (((jl_datatype_t*)typ)->name == jl_namedtuple_typename) return jl_has_concrete_subtype(jl_tparam1(typ)); - jl_svec_t *fields = ((jl_datatype_t*)typ)->types; + jl_svec_t *fields = jl_get_fieldtypes((jl_datatype_t*)typ); size_t i, l = jl_svec_len(fields); if (l != ((jl_datatype_t*)typ)->ninitialized) if (((jl_datatype_t*)typ)->name != jl_tuple_typename) diff --git a/src/interpreter.c b/src/interpreter.c index f4b93fe3ffa39..478527a98ee3d 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -47,11 +47,11 @@ SECT_INTERP static int equiv_type(jl_datatype_t *dta, jl_datatype_t *dtb) dta->ninitialized == dtb->ninitialized && jl_egal((jl_value_t*)jl_field_names(dta), (jl_value_t*)jl_field_names(dtb)) && jl_nparams(dta) == jl_nparams(dtb) && - jl_field_count(dta) == jl_field_count(dtb))) + jl_svec_len(dta->types) == jl_svec_len(dtb->types))) return 0; jl_value_t *a=NULL, *b=NULL; int ok = 1; - size_t i, nf = jl_field_count(dta); + size_t i, nf = jl_svec_len(dta->types); JL_GC_PUSH2(&a, &b); a = jl_rewrap_unionall((jl_value_t*)dta->super, dta->name->wrapper); b = jl_rewrap_unionall((jl_value_t*)dtb->super, dtb->name->wrapper); @@ -63,7 +63,8 @@ SECT_INTERP static int equiv_type(jl_datatype_t *dta, jl_datatype_t *dtb) JL_CATCH { ok = 0; } - if (!ok) goto no; + if (!ok) + goto no; assert(jl_is_datatype(a)); a = dta->name->wrapper; b = dtb->name->wrapper; @@ -77,9 +78,11 @@ SECT_INTERP static int equiv_type(jl_datatype_t *dta, jl_datatype_t *dtb) b = ub->body; } assert(jl_is_datatype(a) && jl_is_datatype(b)); - for (i=0; i < nf; i++) { - jl_value_t *ta = jl_svecref(((jl_datatype_t*)a)->types, i); - jl_value_t *tb = jl_svecref(((jl_datatype_t*)b)->types, i); + a = (jl_value_t*)jl_get_fieldtypes((jl_datatype_t*)a); + b = (jl_value_t*)jl_get_fieldtypes((jl_datatype_t*)b); + for (i = 0; i < nf; i++) { + jl_value_t *ta = jl_svecref(a, i); + jl_value_t *tb = jl_svecref(b, i); if (jl_has_free_typevars(ta)) { if (!jl_has_free_typevars(tb) || !jl_egal(ta, tb)) goto no; diff --git a/src/jlapi.c b/src/jlapi.c index 4bdb86e319919..c28fb9cf5aa27 100644 --- a/src/jlapi.c +++ b/src/jlapi.c @@ -394,6 +394,12 @@ JL_DLLEXPORT jl_value_t *(jl_typeof)(jl_value_t *v) return jl_typeof(v); } +JL_DLLEXPORT jl_value_t *(jl_get_fieldtypes)(jl_value_t *v) +{ + return (jl_value_t*)jl_get_fieldtypes((jl_datatype_t*)v); +} + + #ifndef __clang_analyzer__ JL_DLLEXPORT int8_t (jl_gc_unsafe_enter)(void) { diff --git a/src/jltypes.c b/src/jltypes.c index 0d59bdc3e659e..2e6e193480626 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -921,7 +921,7 @@ JL_EXTENSION struct _jl_typestack_t { }; static jl_value_t *inst_type_w_(jl_value_t *t, jl_typeenv_t *env, jl_typestack_t *stack, int check); -static jl_svec_t *inst_all(jl_svec_t *p, jl_typeenv_t *env, jl_typestack_t *stack, int check); +static jl_svec_t *inst_ftypes(jl_svec_t *p, jl_typeenv_t *env, jl_typestack_t *stack); JL_DLLEXPORT jl_value_t *jl_instantiate_unionall(jl_unionall_t *u, jl_value_t *p) { @@ -1251,6 +1251,9 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value ndt->super = (jl_datatype_t*)inst_type_w_((jl_value_t*)dt->super, env, stack, 1); jl_gc_wb(ndt, ndt->super); } + if (cacheable) { + jl_cache_type_(ndt); + } jl_svec_t *ftypes = dt->types; if (ftypes == NULL || dt->super == NULL) { // in the process of creating this type definition: @@ -1258,12 +1261,15 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value assert(inside_typedef && !istuple && !isnamedtuple); arraylist_push(&partial_inst, ndt); } - else { - assert(ftypes != jl_emptysvec || jl_field_names(ndt) == jl_emptysvec || isnamedtuple); + else if (!isnamedtuple && !istuple) { + assert(ftypes != jl_emptysvec || jl_field_names(ndt) == jl_emptysvec); assert(ftypes == jl_emptysvec || !ndt->abstract); - if (!istuple && !isnamedtuple) { + if (ftypes == jl_emptysvec) { + ndt->types = ftypes; + } + else if (cacheable) { // recursively instantiate the types of the fields - ndt->types = inst_all(ftypes, env, stack, 1); + ndt->types = inst_ftypes(ftypes, env, stack); jl_gc_wb(ndt, ndt->types); } } @@ -1284,7 +1290,6 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value ndt->ninitialized = dt->ninitialized; if (cacheable) { - jl_cache_type_(ndt); JL_UNLOCK(&typecache_lock); // Might GC } @@ -1344,14 +1349,21 @@ jl_datatype_t *jl_inst_concrete_tupletype_v(jl_value_t **p, size_t np) return (jl_datatype_t*)inst_datatype(jl_anytuple_type, NULL, p, np, 1, NULL); } -static jl_svec_t *inst_all(jl_svec_t *p, jl_typeenv_t *env, jl_typestack_t *stack, int check) +static jl_svec_t *inst_ftypes(jl_svec_t *p, jl_typeenv_t *env, jl_typestack_t *stack) { size_t i; size_t lp = jl_svec_len(p); jl_svec_t *np = jl_alloc_svec(lp); JL_GC_PUSH1(&np); - for(i=0; i < lp; i++) { - jl_svecset(np, i, (jl_value_t*)inst_type_w_(jl_svecref(p,i), env, stack, check)); + for (i = 0; i < lp; i++) { + jl_value_t *pi = jl_svecref(p, i); + JL_TRY { + pi = inst_type_w_(pi, env, stack, 1); + } + JL_CATCH { + pi = jl_bottom_type; + } + jl_svecset(np, i, pi); } JL_GC_POP(); return np; @@ -1401,7 +1413,7 @@ static jl_value_t *inst_tuple_w_(jl_value_t *t, jl_typeenv_t *env, jl_typestack_ int i; for (i = 0; i < ntp; i++) { jl_value_t *elt = jl_svecref(tp, i); - jl_value_t *pi = (jl_value_t*)inst_type_w_(elt, env, stack, 0); + jl_value_t *pi = inst_type_w_(elt, env, stack, 0); iparams[i] = pi; if (ip_heap) jl_gc_wb(ip_heap, pi); @@ -1492,7 +1504,7 @@ static jl_value_t *inst_type_w_(jl_value_t *t, jl_typeenv_t *env, jl_typestack_t int cacheable = 1, bound = 0; for(i=0; i < ntp; i++) { jl_value_t *elt = jl_svecref(tp, i); - iparams[i] = (jl_value_t*)inst_type_w_(elt, env, stack, check); + iparams[i] = inst_type_w_(elt, env, stack, check); bound |= (iparams[i] != elt); if (cacheable && jl_has_free_typevars(iparams[i])) cacheable = 0; @@ -1505,18 +1517,18 @@ static jl_value_t *inst_type_w_(jl_value_t *t, jl_typeenv_t *env, jl_typestack_t return result; } -jl_value_t *instantiate_with(jl_value_t *t, jl_value_t **env, size_t n, jl_typeenv_t *te, jl_typestack_t *stack) +static jl_value_t *instantiate_with(jl_value_t *t, jl_value_t **env, size_t n, jl_typeenv_t *te) { if (n > 0) { jl_typeenv_t en = { (jl_tvar_t*)env[0], env[1], te }; - return instantiate_with(t, &env[2], n-1, &en, stack); + return instantiate_with(t, &env[2], n-1, &en ); } - return inst_type_w_(t, te, stack, 1); + return inst_type_w_(t, te, NULL, 1); } jl_value_t *jl_instantiate_type_with(jl_value_t *t, jl_value_t **env, size_t n) { - return instantiate_with(t, env, n, NULL, NULL); + return instantiate_with(t, env, n, NULL); } static jl_value_t *_jl_instantiate_type_in_env(jl_value_t *ty, jl_unionall_t *env, jl_value_t **vals, jl_typeenv_t *prev) @@ -1562,6 +1574,30 @@ jl_value_t *jl_wrap_vararg(jl_value_t *t, jl_value_t *n) return vn; } +JL_DLLEXPORT jl_svec_t *jl_compute_fieldtypes(jl_datatype_t *st) +{ + assert(st->name != jl_namedtuple_typename && st->name != jl_tuple_typename); + jl_datatype_t *wt = (jl_datatype_t*)jl_unwrap_unionall(st->name->wrapper); + size_t i, n = jl_svec_len(wt->parameters); + assert(n > 0 && "expected empty case to be handled during construction"); + //if (n == 0) + // return ((st->types = jl_emptysvec)); + jl_typeenv_t *env = (jl_typeenv_t*)alloca(n * sizeof(jl_typeenv_t)); + for (i = 0; i < n; i++) { + env[i].var = (jl_tvar_t*)jl_svecref(wt->parameters, i); + env[i].val = jl_svecref(st->parameters, i); + env[i].prev = i == 0 ? NULL : &env[i - 1]; + } + jl_typestack_t top; + top.tt = st; + top.prev = NULL; + st->types = inst_ftypes(wt->types, &env[n - 1], &top); + jl_gc_wb(st, st->types); + JL_GC_POP(); + return st->types; +} + + void jl_reinstantiate_inner_types(jl_datatype_t *t) // can throw! { inside_typedef = 0; @@ -1575,19 +1611,20 @@ void jl_reinstantiate_inner_types(jl_datatype_t *t) // can throw! return; } - jl_value_t **env = (jl_value_t**)alloca(n * 2 * sizeof(void*)); + jl_typeenv_t *env = (jl_typeenv_t*)alloca(n * sizeof(jl_typeenv_t)); for (i = 0; i < n; i++) { - env[i * 2] = jl_svecref(t->parameters, i); - env[i * 2 + 1] = NULL; + env[i].var = (jl_tvar_t*)jl_svecref(t->parameters, i); + env[i].val = NULL; + env[i].prev = i == 0 ? NULL : &env[i - 1]; } for (j = 0; j < partial_inst.len; j++) { jl_datatype_t *ndt = (jl_datatype_t*)partial_inst.items[j]; assert(jl_unwrap_unionall(ndt->name->wrapper) == (jl_value_t*)t); for (i = 0; i < n; i++) - env[i * 2 + 1] = jl_svecref(ndt->parameters, i); + env[i].val = jl_svecref(ndt->parameters, i); - ndt->super = (jl_datatype_t*)instantiate_with((jl_value_t*)t->super, env, n, NULL, &top); + ndt->super = (jl_datatype_t*)inst_type_w_((jl_value_t*)t->super, env, &top, 1); jl_gc_wb(ndt, ndt->super); } @@ -1595,15 +1632,10 @@ void jl_reinstantiate_inner_types(jl_datatype_t *t) // can throw! for (j = 0; j < partial_inst.len; j++) { jl_datatype_t *ndt = (jl_datatype_t*)partial_inst.items[j]; for (i = 0; i < n; i++) - env[i * 2 + 1] = jl_svecref(ndt->parameters, i); - - int k; + env[i].val = jl_svecref(ndt->parameters, i); assert(ndt->types == NULL); - ndt->types = jl_alloc_svec(jl_svec_len(t->types)); + ndt->types = inst_ftypes(t->types, env, &top); jl_gc_wb(ndt, ndt->types); - for (k=0; k < jl_svec_len(t->types); k++) { - jl_svecset(ndt->types, k, instantiate_with(jl_svecref(t->types,k), env, n, NULL, &top)); - } if (ndt->uid) { // cacheable jl_compute_field_offsets(ndt); } @@ -1707,7 +1739,7 @@ void jl_init_types(void) JL_GC_DISABLED // NOTE: types should not really be mutable, but the instance and // struct_decl fields are basically caches, which are mutated. jl_datatype_type->mutabl = 1; - jl_datatype_type->ninitialized = 4; + jl_datatype_type->ninitialized = 3; jl_precompute_memoized_dt(jl_datatype_type); jl_typename_type->name = jl_new_typename_in(jl_symbol("TypeName"), core); diff --git a/src/julia.h b/src/julia.h index 0d4f3855c43e0..ba5348b77d2a9 100644 --- a/src/julia.h +++ b/src/julia.h @@ -896,6 +896,8 @@ STATIC_INLINE void jl_array_uint8_set(void *a, size_t i, uint8_t x) JL_NOTSAFEPO #define jl_gf_name(f) (jl_gf_mtable(f)->name) // struct type info +JL_DLLEXPORT jl_svec_t *jl_compute_fieldtypes(jl_datatype_t *st); +#define jl_get_fieldtypes(st) ((st)->types ? (st)->types : jl_compute_fieldtypes((st))) STATIC_INLINE jl_svec_t *jl_field_names(jl_datatype_t *st) JL_NOTSAFEPOINT { jl_svec_t *names = st->names; @@ -907,8 +909,10 @@ STATIC_INLINE jl_sym_t *jl_field_name(jl_datatype_t *st, size_t i) JL_NOTSAFEPOI { return (jl_sym_t*)jl_svecref(jl_field_names(st), i); } -#define jl_field_type(st,i) jl_svecref(((jl_datatype_t*)st)->types, (i)) -#define jl_field_count(st) jl_svec_len(((jl_datatype_t*)st)->types) +STATIC_INLINE jl_value_t *jl_field_type(jl_datatype_t *st, size_t i) +{ + return jl_svecref(jl_get_fieldtypes(st), i); +} #define jl_datatype_size(t) (((jl_datatype_t*)t)->size) #define jl_datatype_align(t) (((jl_datatype_t*)t)->layout->alignment) #define jl_datatype_nbits(t) ((((jl_datatype_t*)t)->size)*8) diff --git a/src/subtype.c b/src/subtype.c index 6cee9d9de2eb7..c257bcae89393 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -2791,7 +2791,7 @@ static int num_occurs(jl_tvar_t *v, jl_typeenv_t *env); static jl_value_t *nth_tuple_elt(jl_datatype_t *t JL_PROPAGATES_ROOT, size_t i) JL_NOTSAFEPOINT { - size_t len = jl_field_count(t); + size_t len = jl_nparams(t); if (len == 0) return NULL; if (i < len-1) diff --git a/src/typemap.c b/src/typemap.c index ef7aa50120204..593e401932ed0 100644 --- a/src/typemap.c +++ b/src/typemap.c @@ -30,7 +30,7 @@ static int sig_match_by_type_leaf(jl_value_t **types, jl_tupletype_t *sig, size_ { size_t i; for (i = 0; i < n; i++) { - jl_value_t *decl = jl_field_type(sig, i); + jl_value_t *decl = jl_tparam(sig, i); jl_value_t *a = types[i]; if (jl_is_type_type(a)) // decl is not Type, because it wouldn't be leafsig a = jl_typeof(jl_tparam0(a)); @@ -45,7 +45,7 @@ static int sig_match_by_type_simple(jl_value_t **types, size_t n, jl_tupletype_t size_t i; if (va) lensig -= 1; for (i = 0; i < lensig; i++) { - jl_value_t *decl = jl_field_type(sig, i); + jl_value_t *decl = jl_tparam(sig, i); jl_value_t *a = types[i]; jl_value_t *unw = jl_is_unionall(decl) ? ((jl_unionall_t*)decl)->body : decl; if (jl_is_type_type(unw)) { @@ -78,7 +78,7 @@ static int sig_match_by_type_simple(jl_value_t **types, size_t n, jl_tupletype_t } } if (va) { - jl_value_t *decl = jl_unwrap_unionall(jl_field_type(sig, i)); + jl_value_t *decl = jl_unwrap_unionall(jl_tparam(sig, i)); if (jl_vararg_kind(decl) == JL_VARARG_INT) { if (n - i != jl_unbox_long(jl_tparam1(decl))) return 0; @@ -258,7 +258,7 @@ jl_typemap_t *mtcache_hash_lookup(const struct jl_ordereddict_t *a JL_PROPAGATES t = ((jl_typemap_level_t*)ml)->key; } else { - t = jl_field_type(jl_unwrap_unionall(jl_typemap_entry_sig(ml)), offs); + t = jl_tparam(jl_unwrap_unionall(jl_typemap_entry_sig(ml)), offs); if (tparam) t = jl_tparam0(t); } @@ -281,7 +281,7 @@ static void mtcache_rehash(struct jl_ordereddict_t *pa, size_t newlen, jl_value_ t = ((jl_typemap_level_t*)ml)->key; } else { - t = jl_field_type(jl_unwrap_unionall(jl_typemap_entry_sig(ml)), offs); + t = jl_tparam(jl_unwrap_unionall(jl_typemap_entry_sig(ml)), offs); if (tparam) t = jl_tparam0(t); } @@ -359,7 +359,7 @@ static jl_typemap_t **mtcache_hash_bp(struct jl_ordereddict_t *pa JL_PROPAGATES_ t = ((jl_typemap_level_t*)*pml)->key; } else { - t = jl_field_type(jl_unwrap_unionall( + t = jl_tparam(jl_unwrap_unionall( jl_typemap_entry_sig(*pml)), offs); if (tparam) @@ -437,7 +437,7 @@ static int jl_typemap_intersection_array_visitor(struct jl_ordereddict_t *a, jl_ t = ((jl_typemap_level_t*)ml)->key; } else { - t = jl_field_type(jl_unwrap_unionall(jl_typemap_entry_sig(ml)), offs); + t = jl_tparam(jl_unwrap_unionall(jl_typemap_entry_sig(ml)), offs); if (tparam) t = jl_tparam0(t); } @@ -514,7 +514,7 @@ int jl_typemap_intersection_visitor(jl_typemap_t *map, int offs, if (jl_typeof(map) == (jl_value_t *)jl_typemap_level_type) { jl_typemap_level_t *cache = (jl_typemap_level_t*)map; jl_value_t *ty = NULL; - size_t l = jl_field_count(ttypes); + size_t l = jl_nparams(ttypes); if (closure->va && l <= offs + 1) { ty = closure->va; } @@ -590,16 +590,16 @@ static jl_typemap_entry_t *jl_typemap_assoc_by_type_(jl_typemap_entry_t *ml, jl_ { jl_value_t *unw = jl_unwrap_unionall((jl_value_t*)types); int isua = jl_is_unionall(types); - size_t n = jl_field_count(unw); + size_t n = jl_nparams(unw); int typesisva = n == 0 ? 0 : jl_is_vararg_type(jl_tparam(unw, n-1)); for (; ml != (void*)jl_nothing; ml = ml->next) { if (world < ml->min_world || world > (ml->max_world | max_world_mask)) continue; // ignore replaced methods - size_t lensig = jl_field_count(jl_unwrap_unionall((jl_value_t*)ml->sig)); + size_t lensig = jl_nparams(jl_unwrap_unionall((jl_value_t*)ml->sig)); if (lensig == n || (ml->va && lensig <= n+1)) { int resetenv = 0, ismatch = 1; if (ml->simplesig != (void*)jl_nothing && !isua) { - size_t lensimplesig = jl_field_count(ml->simplesig); + size_t lensimplesig = jl_nparams(ml->simplesig); int isva = lensimplesig > 0 && jl_is_vararg_type(jl_tparam(ml->simplesig, lensimplesig - 1)); if (lensig == n || (isva && lensimplesig <= n + 1)) ismatch = sig_match_by_type_simple(jl_svec_data(((jl_datatype_t*)types)->parameters), n, @@ -690,7 +690,7 @@ jl_typemap_entry_t *jl_typemap_assoc_by_type(jl_typemap_t *ml_or_cache, jl_value jl_value_t *ty = NULL; jl_value_t *ttypes = jl_unwrap_unionall((jl_value_t*)types); assert(jl_is_datatype(ttypes)); - size_t l = jl_field_count(ttypes); + size_t l = jl_nparams(ttypes); int isva = 0; // compute the type at offset `offs` into `types`, which may be a Vararg if (l <= offs + 1) { @@ -760,7 +760,7 @@ jl_typemap_entry_t *jl_typemap_entry_assoc_exact(jl_typemap_entry_t *ml, jl_valu while (ml->simplesig == (void*)jl_nothing && ml->guardsigs == jl_emptysvec && ml->isleafsig) { // use a tight loop for as long as possible if (world >= ml->min_world && world <= ml->max_world) { - if (n == jl_field_count(ml->sig) && jl_typeof(args[0]) == jl_tparam(ml->sig, 0)) { + if (n == jl_nparams(ml->sig) && jl_typeof(args[0]) == jl_tparam(ml->sig, 0)) { if (n == 1) return ml; if (n == 2) { @@ -786,10 +786,10 @@ jl_typemap_entry_t *jl_typemap_entry_assoc_exact(jl_typemap_entry_t *ml, jl_valu for (; ml != (void*)jl_nothing; ml = ml->next) { if (world < ml->min_world || world > ml->max_world) continue; // ignore replaced methods - size_t lensig = jl_field_count(ml->sig); + size_t lensig = jl_nparams(ml->sig); if (lensig == n || (ml->va && lensig <= n+1)) { if (ml->simplesig != (void*)jl_nothing) { - size_t lensimplesig = jl_field_count(ml->simplesig); + size_t lensimplesig = jl_nparams(ml->simplesig); int isva = lensimplesig > 0 && jl_is_vararg_type(jl_tparam(ml->simplesig, lensimplesig - 1)); if (lensig == n || (isva && lensimplesig <= n + 1)) { if (!sig_match_simple(args, n, jl_svec_data(ml->simplesig->parameters), isva, lensimplesig)) @@ -959,7 +959,7 @@ static void jl_typemap_level_insert_(jl_typemap_level_t *cache, jl_typemap_entry const struct jl_typemap_info *tparams) { jl_value_t *ttypes = jl_unwrap_unionall((jl_value_t*)newrec->sig); - size_t l = jl_field_count(ttypes); + size_t l = jl_nparams(ttypes); // compute the type at offset `offs` into `sig`, which may be a Vararg jl_value_t *t1 = NULL; int isva = 0; @@ -1047,8 +1047,8 @@ jl_typemap_entry_t *jl_typemap_insert(jl_typemap_t **cache, jl_value_t *parent, JL_GC_PUSH1(&newrec); assert(jl_is_tuple_type(ttype)); size_t i, l; - for (i = 0, l = jl_field_count(ttype); i < l && newrec->issimplesig; i++) { - jl_value_t *decl = jl_field_type(ttype, i); + for (i = 0, l = jl_nparams(ttype); i < l && newrec->issimplesig; i++) { + jl_value_t *decl = jl_tparam(ttype, i); if (jl_is_kind(decl)) newrec->isleafsig = 0; // Type{} may have a higher priority than a kind else if (jl_is_type_type(decl)) diff --git a/test/core.jl b/test/core.jl index 517bda1fe1812..0a126a8189317 100644 --- a/test/core.jl +++ b/test/core.jl @@ -219,13 +219,16 @@ struct D21923{T,N}; v::D21923{T}; end # issue #22624, more circular definitions struct T22624{A,B,C}; v::Vector{T22624{Int64,A}}; end -let elT = T22624.body.body.body.types[1].parameters[1] +let ft = Base.datatype_fieldtypes + elT = T22624.body.body.body.types[1].parameters[1] @test elT == T22624{Int64, T22624.var, C} where C - elT2 = elT.body.types[1].parameters[1] + elT2 = ft(elT.body)[1].parameters[1] @test elT2 == T22624{Int64, Int64, C} where C - @test elT2.body.types[1].parameters[1] === elT2 - @test Base.isconcretetype(elT2.body.types[1]) + @test ft(elT2.body)[1].parameters[1] === elT2 + @test Base.isconcretetype(ft(elT2.body)[1]) end +struct S22624{A,B,C} <: Ref{S22624{Int64,A}}; end +@test @isdefined S22624 # issue #3890 mutable struct A3890{T1} @@ -3459,15 +3462,22 @@ end mutable struct FooNTuple{N} z::Tuple{Integer, Vararg{Int, N}} end -@test_throws ErrorException FooNTuple{-1} -@test_throws ErrorException FooNTuple{typemin(Int)} -@test_throws TypeError FooNTuple{0x01} +for i in (-1, typemin(Int), 0x01) + T = FooNTuple{i} + @test T.parameters[1] == i + @test fieldtypes(T) == (Union{},) +end @test fieldtype(FooNTuple{0}, 1) == Tuple{Integer} mutable struct FooTupleT{T} z::Tuple{Int, T, Int} end -@test_throws TypeError FooTupleT{Vararg{Int, 2}} +let R = Vararg{Int, 2} + @test_throws TypeError Val{R} + @test_throws TypeError Ref{R} + @test_throws TypeError FooTupleT{R} + @test_throws TypeError Union{R} +end @test fieldtype(FooTupleT{Int}, 1) == NTuple{3, Int} @test Tuple{} === NTuple{0, Any} @@ -4619,7 +4629,9 @@ mutable struct B12238{T,S} end @test B12238.body.body.types[1] === A12238{B12238{Int}.body} @test isa(A12238{B12238{Int}}.instance, A12238{B12238{Int}}) -@test !isdefined(B12238.body.body.types[1], :instance) # has free type vars +let ft = Base.datatype_fieldtypes + @test !isdefined(ft(B12238.body.body)[1], :instance) # has free type vars +end # issue #16315 let a = Any[] @@ -4732,8 +4744,10 @@ end mutable struct C16767{T} b::A16767{C16767{:a}} end -@test B16767.body.types[1].types[1].parameters[1].types[1] === A16767{B16767.body} -@test C16767.body.types[1].types[1].parameters[1].types[1] === A16767{C16767{:a}} +let ft = Base.datatype_fieldtypes + @test ft(ft(B16767.body.types[1])[1].parameters[1])[1] === A16767{B16767.body} + @test ft(C16767.body.types[1].types[1].parameters[1])[1] === A16767{C16767{:a}} +end # issue #16340 function f16340(x::T) where T diff --git a/test/precompile.jl b/test/precompile.jl index b75ffed9e6667..836d73e9393ae 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -293,13 +293,14 @@ try some_method, Tuple{typeof(Base.include), String}, Core.svec(), typemax(UInt)) @test Foo.some_linfo::Core.MethodInstance === some_linfo - PV = Foo.Value18343{Some}.body.types[1] - VR = PV.types[1].parameters[1] - @test PV.types[1] === Array{VR,1} - @test pointer_from_objref(PV.types[1]) === - pointer_from_objref(PV.types[1].parameters[1].types[1].types[1]) - @test PV === PV.types[1].parameters[1].types[1] - @test pointer_from_objref(PV) === pointer_from_objref(PV.types[1].parameters[1].types[1]) + ft = Base.datatype_fieldtypes + PV = ft(Foo.Value18343{Some}.body)[1] + VR = ft(PV)[1].parameters[1] + @test ft(PV)[1] === Array{VR,1} + @test pointer_from_objref(ft(PV)[1]) === + pointer_from_objref(ft(ft(ft(PV)[1].parameters[1])[1])[1]) + @test PV === ft(ft(PV)[1].parameters[1])[1] + @test pointer_from_objref(PV) === pointer_from_objref(ft(ft(PV)[1].parameters[1])[1]) end Baz_file = joinpath(dir, "Baz.jl")