From b7c2647e7591cd7250e9e1149fe082d7df90c25f Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Sat, 20 Jul 2019 17:52:39 -0400 Subject: [PATCH 1/4] Add deferred field to jl_module_t --- src/julia.h | 2 ++ src/staticdata.c | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/src/julia.h b/src/julia.h index 3c10c398ab1d2..16abdc6417ad7 100644 --- a/src/julia.h +++ b/src/julia.h @@ -487,6 +487,8 @@ typedef struct _jl_module_t { int32_t nospecialize; // global bit flags: initialization for new methods uint8_t istopmod; jl_mutex_t lock; + // Any method definitions that are deferred to due to incomplete types + arraylist_t deferred; } jl_module_t; // one Type-to-Value entry diff --git a/src/staticdata.c b/src/staticdata.c index 7a2f62c3d9274..634ed6321b84b 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -504,6 +504,14 @@ static void jl_write_module(jl_serializer_state *s, uintptr_t item, jl_module_t newm->bindings.table = NULL; memset(&newm->bindings._space, 0, sizeof(newm->bindings._space)); + // The deferred list should be empty + memset(&newm->deferred._space, 0, sizeof(newm->deferred._space)); + newm->deferred.len = 1; + newm->deferred.max = AL_N_INLINE; + newm->deferred.items = (void**)offsetof(jl_module_t, usings._space); + arraylist_push(&s->relocs_list, (void*)(reloc_offset + offsetof(jl_module_t, deferred.items))); + arraylist_push(&s->relocs_list, (void*)(((uintptr_t)DataRef << RELOC_TAG_OFFSET) + item)); + // write out the usings list memset(&newm->usings._space, 0, sizeof(newm->usings._space)); if (m == jl_main_module) { From 4cf53184d96dd6a0fe62fe81e5afe200d6e2ff4d Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Sat, 20 Jul 2019 21:50:07 -0400 Subject: [PATCH 2/4] Check number of arguments passed to jl_svec* functions When modifying Julia's builtin types, it's really easy to make mistakes here. Now the compiler will check for you. --- src/julia.h | 10 ++++++++++ src/julia_internal.h | 11 +++++++++++ src/simplevector.c | 4 ++-- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/julia.h b/src/julia.h index 16abdc6417ad7..ae72416a332e7 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1198,6 +1198,16 @@ JL_DLLEXPORT jl_value_t *jl_new_structt(jl_datatype_t *type, jl_value_t *tup); JL_DLLEXPORT jl_value_t *jl_new_struct_uninit(jl_datatype_t *type); JL_DLLEXPORT jl_method_instance_t *jl_new_method_instance_uninit(void); JL_DLLEXPORT jl_svec_t *jl_svec(size_t n, ...) JL_MAYBE_UNROOTED; +#ifdef __GNUC__ +#define jl_svec(n, ...) \ + (jl_svec)( \ + __extension__({ \ + void *names[] = { __VA_ARGS__ }; \ + _Static_assert(n == sizeof(names)/sizeof(names[0]), \ + "Number of passed arguments does not match expected number"); \ + n; \ + }), __VA_ARGS__) +#endif JL_DLLEXPORT jl_svec_t *jl_svec1(void *a); JL_DLLEXPORT jl_svec_t *jl_svec2(void *a, void *b); JL_DLLEXPORT jl_svec_t *jl_alloc_svec(size_t n); diff --git a/src/julia_internal.h b/src/julia_internal.h index b5b006029ad97..2d14849d2c457 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -277,6 +277,17 @@ jl_value_t *jl_permbox32(jl_datatype_t *t, int32_t x); jl_value_t *jl_permbox64(jl_datatype_t *t, int64_t x); jl_svec_t *jl_perm_symsvec(size_t n, ...); +#ifdef __GNUC__ +#define jl_perm_symsvec(n, ...) \ + (jl_perm_symsvec)( \ + __extension__({ \ + const char *names[] = { __VA_ARGS__ }; \ + _Static_assert(n == sizeof(names)/sizeof(names[0]), \ + "Number of passed arguments does not match expected number"); \ + n; \ + }), __VA_ARGS__) +#endif + // Returns a int32 where the high 16 bits are a lower bound of the number of non-pointer fields // at the beginning of the type and the low 16 bits are a lower bound on the number of non-pointer // fields at the end of the type. This field only exists for a layout that has at least one diff --git a/src/simplevector.c b/src/simplevector.c index c3d0dde54e43f..566c2e7100be8 100644 --- a/src/simplevector.c +++ b/src/simplevector.c @@ -7,7 +7,7 @@ #include "julia_internal.h" #include "julia_assert.h" -JL_DLLEXPORT jl_svec_t *jl_svec(size_t n, ...) +JL_DLLEXPORT jl_svec_t *(jl_svec)(size_t n, ...) { va_list args; if (n == 0) return jl_emptysvec; @@ -19,7 +19,7 @@ JL_DLLEXPORT jl_svec_t *jl_svec(size_t n, ...) return jv; } -jl_svec_t *jl_perm_symsvec(size_t n, ...) +jl_svec_t *(jl_perm_symsvec)(size_t n, ...) { if (n == 0) return jl_emptysvec; jl_svec_t *jv = (jl_svec_t*)jl_gc_permobj((n + 1) * sizeof(void*), jl_simplevector_type); From 8d4a91ee8d8763dafc08f853a2d8c9799057f9b2 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Sun, 21 Jul 2019 18:45:38 -0400 Subject: [PATCH 3/4] Placeholder WIP --- src/builtins.c | 1 + src/datatype.c | 1 + src/interpreter.c | 161 +++++++++++++++++++++++++++++++++++++++++++++- src/jltypes.c | 22 +++++-- src/julia.h | 34 +++++++++- src/module.c | 1 + src/staticdata.c | 2 +- 7 files changed, 213 insertions(+), 9 deletions(-) diff --git a/src/builtins.c b/src/builtins.c index 647872fcb6662..3de8770d77886 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -1308,6 +1308,7 @@ void jl_init_primitives(void) JL_GC_DISABLED add_builtin("TypeName", (jl_value_t*)jl_typename_type); add_builtin("DataType", (jl_value_t*)jl_datatype_type); add_builtin("TypeVar", (jl_value_t*)jl_tvar_type); + add_builtin("Placeholder", (jl_value_t*)jl_placeholder_type); add_builtin("UnionAll", (jl_value_t*)jl_unionall_type); add_builtin("Union", (jl_value_t*)jl_uniontype_type); add_builtin("TypeofBottom", (jl_value_t*)jl_typeofbottom_type); diff --git a/src/datatype.c b/src/datatype.c index ed132c8ace059..3c8f63f55b043 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -84,6 +84,7 @@ jl_datatype_t *jl_new_uninitialized_datatype(void) t->isdispatchtuple = 0; t->isbitstype = 0; t->zeroinit = 0; + t->incomplete = 0; t->isinlinealloc = 0; t->has_concrete_subtype = 1; t->layout = NULL; diff --git a/src/interpreter.c b/src/interpreter.c index 396b590beed2b..2b43e917f01b1 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -220,6 +220,159 @@ static void eval_primitivetype(jl_expr_t *ex, interpreter_state *s) JL_GC_POP(); } +/* + * We maintain a DAG of dependencies between incomplete datatypes. Each SCC in + * this graph is assigned a unique representative that represents the SCC to + * other SCCs. Obtain this representative for a given dt. + */ +static jl_datatype_t *get_scc_representative(jl_datatype_t *dt) { + if (dt->scc == dt) + return dt; + // For efficiency we only update the SCC representative when merging SCCs. + // This update here folds updating the members of the SCC into any + // subsequent access. + dt->scc = get_scc_representative(dt->scc); + return dt->scc; +} + + /* + * Check if adding a new dependency edge from scc_a to scc_b would add a cycle. + * If so, return the cycle. + * + * Note: We assume the SCC dag to be fairly small such that the search here is + * not too expensive. Should that not be the case in real world code, investigate + * using and incremental SCC maintenance algorithm (e.g. + * https://arxiv.org/pdf/1105.2397.pdf) instead. + */ +static jl_array_t *find_new_cycle(jl_datatype_t *scc_a, jl_datatype_t *scc_b) JL_NOTSAFEPOINT { + assert(get_scc_representative(scc_a) == scc_a); + assert(get_scc_representative(scc_b) == scc_b); + if (scc_a == scc_b) { + jl_array_t *cycle = jl_alloc_vec_any(0); + jl_array_ptr_1d_push(cycle, (jl_value_t*)scc_a); + return cycle; + } + for (int i = 0; i < jl_array_len(scc_a->depends); ++i) { + jl_array_t *cycle = find_new_cycle( + (jl_datatype_t*)jl_array_ptr_ref(scc_a->depends, i), scc_b); + if (cycle) { + jl_array_ptr_1d_push(cycle, (jl_value_t*)scc_a); + return cycle; + } + } + return NULL; +} + + static void filter_scc(jl_array_t *array, jl_datatype_t *scc_rep) +{ + size_t insert_point = 0; + for (int i = 0; i < jl_array_len(array); ++i) { + jl_datatype_t *connected_scc = (jl_datatype_t*)jl_array_ptr_ref(array, i); + if (get_scc_representative(connected_scc) == scc_rep) { + // If this SCC is now part of our new SCC, skip it in this list + continue; + } + if (insert_point != i) { + jl_array_ptr_set(array, insert_point, + jl_array_ptr_ref(array, i)); + } + insert_point++; + } + jl_array_del_end(array, jl_array_len(array) - insert_point); +} + + static void filter_cycle_and_append(jl_array_t *newa, jl_array_t *olda, jl_datatype_t *scc_rep) +{ + for (int i = 0; i < jl_array_len(olda); ++i) { + jl_datatype_t *connected_scc = (jl_datatype_t*)jl_array_ptr_ref(olda, i); + if (get_scc_representative(connected_scc) != scc_rep) + jl_array_ptr_1d_push(newa, (jl_value_t*)connected_scc); + } +} + + static void incomplete_add_dep(jl_datatype_t *from, jl_datatype_t *to) { + assert(from->incomplete); + assert(to->incomplete); + // If these datatypes are part of the same SCC, there's nothing + // to do - they are contracted in the condensation. + if (get_scc_representative(from) == get_scc_representative(to)) { + // Nothing to do + return; + } + // See if adding this edge would result in a cycle. + jl_array_t *cycle = find_new_cycle(get_scc_representative(from), + get_scc_representative(to)); + if (cycle) { + // If so, we need to contract the cycle by merging all SCCs that + // are part of the cycle. Arbitrarily pick the first SCC as the new + // SCC representative. + jl_datatype_t *new_scc_rep = (jl_datatype_t*)jl_array_ptr_ref(cycle, 0); + // First go through and set the elements to be members of the + // new cycle. + for (int i = 1; i < jl_array_len(cycle); ++i) { + jl_datatype_t *old_scc = (jl_datatype_t*)jl_array_ptr_ref(cycle, i); + old_scc->scc = new_scc_rep; + } + // Now go through and merge the various array lists + filter_scc(new_scc_rep->depends, new_scc_rep); + filter_scc(new_scc_rep->dependents, new_scc_rep); + for (int i = 1; i < jl_array_len(cycle); ++i) { + jl_datatype_t *old_scc = (jl_datatype_t*)jl_array_ptr_ref(cycle, 1); + filter_cycle_and_append(new_scc_rep->depends, old_scc->depends, new_scc_rep); + filter_cycle_and_append(new_scc_rep->dependents, old_scc->dependents, new_scc_rep); + } + return; + } + // Check if we need to merge cycle + jl_array_ptr_1d_push(get_scc_representative(to)->depends, + (jl_value_t*)get_scc_representative(from)); + jl_array_ptr_1d_push(get_scc_representative(from)->dependents, + (jl_value_t*)get_scc_representative(to)); +} + + static void finish_scc(jl_module_t *mod, jl_datatype_t *scc) { + assert(jl_array_len(scc->depends) == 0); + arraylist_t worklist; + arraylist_new(&worklist, 0); + arraylist_push(&worklist, scc); + while (worklist.len != 0) { + jl_datatype_t *dt = (jl_datatype_t*)arraylist_pop(&worklist); + if (!dt->incomplete) + continue; + dt->incomplete = 0; + // We don't store the members of the SCC. Instead, do a search through + // the various things that generate dependencies. + for (int i = 0; i < jl_svec_len(scc->types); ++i) { + jl_datatype_t *fdt = (jl_datatype_t*)jl_svecref(scc->types, i); + if (fdt->incomplete && get_scc_representative(fdt) == scc) + arraylist_push(&worklist, fdt); + } + jl_compute_field_offsets(dt); + } + scc->depends = NULL; + scc->dependents = NULL; + // See if there were any delayed method instantiations that we now need to + // add to the method table. + for (size_t i = 0; i < mod->deferred.len; ++i) { + jl_svec_t *item = (jl_svec_t*)mod->deferred.items[i]; + jl_method_t *m = (jl_method_t*)jl_svecref(item, 1); + if (((jl_datatype_t*)jl_unwrap_unionall(m->sig))->scc == scc) { + jl_method_table_insert( + (jl_methtable_t*)jl_svecref(item, 0), m, NULL); + } + } +} + +static void dt_mark_incomplete(jl_datatype_t *dt) +{ + if (dt->incomplete == 0) { + dt->scc = dt; + dt->depends = jl_alloc_vec_any(0); + dt->dependents = jl_alloc_vec_any(0); + } + dt->incomplete = 1; +} + static void eval_structtype(jl_expr_t *ex, interpreter_state *s) { jl_value_t **args = jl_array_ptr_data(ex->args); @@ -261,7 +414,13 @@ static void eval_structtype(jl_expr_t *ex, interpreter_state *s) jl_gc_wb(dt, dt->types); for (size_t i = 0; i < jl_svec_len(dt->types); i++) { jl_value_t *elt = jl_svecref(dt->types, i); - if ((!jl_is_type(elt) && !jl_is_typevar(elt)) || jl_is_vararg_type(elt)) { + if (jl_typeis(elt, jl_placeholder_type)) { + jl_array_ptr_1d_push( + ((jl_placeholder_t*)elt)->dependents, + (jl_value_t*)dt); + dt_mark_incomplete(dt); + jl_array_ptr_1d_push(dt->depends, elt); + } else if ((!jl_is_type(elt) && !jl_is_typevar(elt)) || jl_is_vararg_type(elt)) { jl_type_error_rt(jl_symbol_name(dt->name->name), "type definition", (jl_value_t*)jl_type_type, elt); diff --git a/src/jltypes.c b/src/jltypes.c index 422fcc7af2f37..617155438ac5e 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -39,6 +39,7 @@ jl_typename_t *jl_vecelement_typename; jl_unionall_t *jl_vararg_type; jl_typename_t *jl_vararg_typename; jl_datatype_t *jl_tvar_type; +jl_datatype_t *jl_placeholder_type; jl_datatype_t *jl_uniontype_type; jl_datatype_t *jl_unionall_type; jl_datatype_t *jl_datatype_type; @@ -1707,7 +1708,7 @@ void jl_init_types(void) JL_GC_DISABLED jl_datatype_type->name->wrapper = (jl_value_t*)jl_datatype_type; jl_datatype_type->super = (jl_datatype_t*)jl_type_type; jl_datatype_type->parameters = jl_emptysvec; - jl_datatype_type->name->names = jl_perm_symsvec(21, + jl_datatype_type->name->names = jl_perm_symsvec(23, "name", "super", "parameters", @@ -1720,6 +1721,7 @@ void jl_init_types(void) JL_GC_DISABLED "uid", "abstract", "mutable", + "incomplete", "hasfreetypevars", "isconcretetype", "isdispatchtuple", @@ -1728,8 +1730,10 @@ void jl_init_types(void) JL_GC_DISABLED "isinlinealloc", "has_concrete_subtype", "llvm::StructType", - "llvm::DIType"); - jl_datatype_type->types = jl_svec(21, + "llvm::DIType", + "padding"); + + jl_datatype_type->types = jl_svec(23, jl_typename_type, jl_datatype_type, jl_simplevector_type, @@ -1738,7 +1742,8 @@ void jl_init_types(void) JL_GC_DISABLED jl_any_type, jl_any_type, jl_any_type, jl_any_type, jl_any_type, jl_any_type, jl_any_type, jl_any_type, jl_any_type, jl_any_type, jl_any_type, jl_any_type, - jl_any_type, jl_any_type, jl_any_type); + jl_any_type, jl_any_type, jl_any_type, jl_any_type, + jl_any_type); jl_datatype_type->instance = NULL; jl_datatype_type->uid = jl_assign_type_uid(); jl_datatype_type->struct_decl = NULL; @@ -2005,6 +2010,11 @@ void jl_init_types(void) JL_GC_DISABLED jl_array_uint8_type = jl_apply_type2((jl_value_t*)jl_array_type, (jl_value_t*)jl_uint8_type, jl_box_long(1)); jl_array_int32_type = jl_apply_type2((jl_value_t*)jl_array_type, (jl_value_t*)jl_int32_type, jl_box_long(1)); + jl_placeholder_type = jl_new_datatype(jl_symbol("Placeholder"), core, jl_any_type, jl_emptysvec, + jl_perm_symsvec(1, "dependents"), + jl_svec(1, jl_array_any_type), + 0, 1, 1); + jl_expr_type = jl_new_datatype(jl_symbol("Expr"), core, jl_any_type, jl_emptysvec, @@ -2297,8 +2307,10 @@ void jl_init_types(void) JL_GC_DISABLED jl_svecset(jl_datatype_type->types, 16, jl_bool_type); jl_svecset(jl_datatype_type->types, 17, jl_bool_type); jl_svecset(jl_datatype_type->types, 18, jl_bool_type); - jl_svecset(jl_datatype_type->types, 19, jl_voidpointer_type); + jl_svecset(jl_datatype_type->types, 19, jl_bool_type); jl_svecset(jl_datatype_type->types, 20, jl_voidpointer_type); + jl_svecset(jl_datatype_type->types, 21, jl_voidpointer_type); + jl_svecset(jl_datatype_type->types, 22, jl_voidpointer_type); jl_svecset(jl_typename_type->types, 1, jl_module_type); jl_svecset(jl_typename_type->types, 6, jl_long_type); jl_svecset(jl_typename_type->types, 3, jl_type_type); diff --git a/src/julia.h b/src/julia.h index ae72416a332e7..a48bcf198af07 100644 --- a/src/julia.h +++ b/src/julia.h @@ -355,6 +355,13 @@ typedef struct _jl_code_instance_t { // all values are callable as Functions typedef jl_value_t jl_function_t; +typedef struct { + JL_DATA_TYPE + // Dependency SCCs that depend on this + // Placeholder + jl_array_t *dependents; +} jl_placeholder_t; + typedef struct { JL_DATA_TYPE jl_sym_t *name; @@ -439,6 +446,7 @@ typedef struct _jl_datatype_t { uint32_t uid; uint8_t abstract; uint8_t mutabl; + uint8_t incomplete; // memoized properties uint8_t hasfreetypevars; // majority part of isconcrete computation uint8_t isconcretetype; // whether this type can have instances @@ -447,8 +455,29 @@ typedef struct _jl_datatype_t { uint8_t zeroinit; // if one or more fields requires zero-initialization uint8_t isinlinealloc; // if this is allocated inline uint8_t has_concrete_subtype; // If clear, no value will have this datatype - void *struct_decl; //llvm::Type* - void *ditype; // llvm::MDNode* to be used as llvm::DIType(ditype) + union { + // of incomplete types. This is a DAG, in which one datatype is chosen + // as the representative of the SCC. Once the representative of the SCC + // is no longer incompletely specified and `depends` is empty, we may + // complete the entire cycle. + struct { + // Pointer to the SCC representative. Note that for efficiency, + // these may be chained, i.e. this pointer may have to be + // dereferenced multiple times to reach the SCC representative. + struct _jl_datatype_t *scc; + // Stores other SCCs that this SCC depends on (i.e. represents an + // edge in the SCC codensation) + jl_array_t *depends; + // The inverse array of the above, from SCCs to SCCs that depend on + // them. + jl_array_t *dependents; + }; + struct { + void *struct_decl; //llvm::Type* + void *ditype; // llvm::MDNode* to be used as llvm::DIType(ditype) + void *padding; + }; + }; } jl_datatype_t; typedef struct { @@ -555,6 +584,7 @@ extern JL_DLLEXPORT jl_datatype_t *jl_datatype_type JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_datatype_t *jl_uniontype_type JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_datatype_t *jl_unionall_type JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_datatype_t *jl_tvar_type JL_GLOBALLY_ROOTED; +extern JL_DLLEXPORT jl_datatype_t *jl_placeholder_type JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_datatype_t *jl_any_type JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_unionall_t *jl_type_type JL_GLOBALLY_ROOTED; diff --git a/src/module.c b/src/module.c index 33e7afedc1696..c338f1944821a 100644 --- a/src/module.c +++ b/src/module.c @@ -38,6 +38,7 @@ JL_DLLEXPORT jl_module_t *jl_new_module(jl_sym_t *name) JL_MUTEX_INIT(&m->lock); htable_new(&m->bindings, 0); arraylist_new(&m->usings, 0); + arraylist_new(&m->deferred, 0); if (jl_core_module) { jl_module_using(m, jl_core_module); } diff --git a/src/staticdata.c b/src/staticdata.c index 634ed6321b84b..75615d3bab2e9 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -55,7 +55,7 @@ static void *const _tags[] = { &jl_uint32_type, &jl_uint64_type, &jl_char_type, &jl_weakref_type, &jl_int8_type, &jl_int16_type, &jl_uint16_type, &jl_float16_type, &jl_float32_type, &jl_float64_type, &jl_floatingpoint_type, - &jl_number_type, &jl_signed_type, + &jl_number_type, &jl_signed_type, &jl_placeholder_type, // special typenames &jl_tuple_typename, &jl_pointer_typename, &jl_array_typename, &jl_type_typename, &jl_vararg_typename, &jl_namedtuple_typename, From 2dda80d6874104221af476d767a0bf70c450165f Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Mon, 22 Jul 2019 13:15:47 -0400 Subject: [PATCH 4/4] More placeholder WIP --- src/builtins.c | 4 ++- src/datatype.c | 7 +++++ src/interpreter.c | 30 +++++++++++++++----- src/jltypes.c | 18 ++++++++++++ src/method.c | 15 ++++++++-- src/module.c | 70 +++++++++++++++++++++++++++++++++++++++++++++- src/staticdata.c | 4 +-- test/incomplete.jl | 33 ++++++++++++++++++++++ 8 files changed, 168 insertions(+), 13 deletions(-) create mode 100644 test/incomplete.jl diff --git a/src/builtins.c b/src/builtins.c index 3de8770d77886..124fc897d82ef 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -907,7 +907,9 @@ static int valid_type_param(jl_value_t *v) if (jl_is_vararg_type(v)) return 0; // TODO: maybe more things - return jl_is_type(v) || jl_is_typevar(v) || jl_is_symbol(v) || jl_isbits(jl_typeof(v)); + return jl_is_type(v) || jl_is_typevar(v) || + jl_is_symbol(v) || jl_isbits(jl_typeof(v)) || + jl_typeis(v, jl_placeholder_type); } JL_CALLABLE(jl_f_apply_type) diff --git a/src/datatype.c b/src/datatype.c index 3c8f63f55b043..dfec60b1411e3 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -504,6 +504,13 @@ JL_DLLEXPORT jl_datatype_t *jl_new_datatype( t->name->names = fnames; jl_gc_wb(t->name, t->name->names); + for (int i = 0; i < jl_svec_len(t->parameters); ++i) { + jl_value_t *p = jl_svecref(t->parameters, i); + if (jl_typeis(p, jl_placeholder_type)) { + dt_mark_incomplete(t, (jl_placeholder_t*)p); + } + } + if (t->name->wrapper == NULL) { t->name->wrapper = (jl_value_t*)t; jl_gc_wb(t->name, t); diff --git a/src/interpreter.c b/src/interpreter.c index 2b43e917f01b1..48d965064fa19 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -101,7 +101,8 @@ SECT_INTERP static int equiv_type(jl_datatype_t *dta, jl_datatype_t *dtb) SECT_INTERP static void check_can_assign_type(jl_binding_t *b, jl_value_t *rhs) { - if (b->constp && b->value != NULL && jl_typeof(b->value) != jl_typeof(rhs)) + if (b->constp && b->value != NULL && jl_typeof(b->value) != jl_typeof(rhs) && + !jl_typeis(b->value, jl_placeholder_type)) jl_errorf("invalid redefinition of constant %s", jl_symbol_name(b->name)); } @@ -363,16 +364,35 @@ static jl_array_t *find_new_cycle(jl_datatype_t *scc_a, jl_datatype_t *scc_b) JL } } -static void dt_mark_incomplete(jl_datatype_t *dt) +extern jl_array_t *jl_all_methods; +void dt_mark_incomplete(jl_datatype_t *dt, jl_value_t *v) { + // While we don't have GC hooked up, just push these into a global root; + if (jl_all_methods == NULL) + jl_all_methods = jl_alloc_vec_any(0); if (dt->incomplete == 0) { dt->scc = dt; dt->depends = jl_alloc_vec_any(0); + jl_array_ptr_1d_push(jl_all_methods, (jl_value_t*)dt->depends); dt->dependents = jl_alloc_vec_any(0); + jl_array_ptr_1d_push(jl_all_methods, (jl_value_t*)dt->dependents); + } + if (jl_typeis(v, jl_placeholder_type)) { + jl_array_ptr_1d_push( + ((jl_placeholder_t*)v)->dependents, + (jl_value_t*)dt); + jl_array_ptr_1d_push(dt->depends, v); + } else if (jl_is_datatype(v)) { + assert(((jl_datatype_t*)v)->incomplete); + jl_array_ptr_1d_push( + ((jl_datatype_t*)v)->dependents, + (jl_value_t*)dt); + jl_array_ptr_1d_push(dt->depends, v); } dt->incomplete = 1; } + static void eval_structtype(jl_expr_t *ex, interpreter_state *s) { jl_value_t **args = jl_array_ptr_data(ex->args); @@ -415,11 +435,7 @@ static void eval_structtype(jl_expr_t *ex, interpreter_state *s) for (size_t i = 0; i < jl_svec_len(dt->types); i++) { jl_value_t *elt = jl_svecref(dt->types, i); if (jl_typeis(elt, jl_placeholder_type)) { - jl_array_ptr_1d_push( - ((jl_placeholder_t*)elt)->dependents, - (jl_value_t*)dt); - dt_mark_incomplete(dt); - jl_array_ptr_1d_push(dt->depends, elt); + dt_mark_incomplete(dt, elt); } else if ((!jl_is_type(elt) && !jl_is_typevar(elt)) || jl_is_vararg_type(elt)) { jl_type_error_rt(jl_symbol_name(dt->name->name), "type definition", diff --git a/src/jltypes.c b/src/jltypes.c index 617155438ac5e..b7de75960b092 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -1323,6 +1323,24 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value jl_compute_field_offsets(ndt); } + for (int i = 0; ndt->types && i < jl_svec_len(ndt->types); ++i) { + jl_value_t *v = jl_svecref(ndt->types, i); + if (jl_typeis(v, jl_placeholder_type)) { + dt_mark_incomplete(ndt, v); + } else if (jl_is_datatype(v) && ((jl_datatype_t*)v)->incomplete) { + dt_mark_incomplete(ndt, v); + } + } + + for (int i = 0; ndt->parameters && i < jl_svec_len(ndt->parameters); ++i) { + jl_value_t *p = jl_svecref(ndt->parameters, i); + if (jl_typeis(p, jl_placeholder_type)) { + dt_mark_incomplete(ndt, p); + } else if (jl_is_datatype(p) && ((jl_datatype_t*)p)->incomplete) { + dt_mark_incomplete(ndt, p); + } + } + if (istuple) ndt->ninitialized = ntp - isvatuple; else if (isnamedtuple) diff --git a/src/method.c b/src/method.c index 7f9189be0d152..6d93f0cd33c11 100644 --- a/src/method.c +++ b/src/method.c @@ -739,9 +739,13 @@ JL_DLLEXPORT void jl_method_def(jl_svec_t *argdata, m->line); } + int any_argtypes_incomplete = 0; for (i = 0; i < na; i++) { jl_value_t *elt = jl_svecref(atypes, i); - if (!jl_is_type(elt) && !jl_is_typevar(elt)) { + if (jl_typeis(elt, jl_placeholder_type) || + (jl_is_datatype(elt) && ((jl_datatype_t*)elt)->incomplete)) { + any_argtypes_incomplete = 1; + } else if (!jl_is_type(elt) && !jl_is_typevar(elt)) { jl_sym_t *argname = (jl_sym_t*)jl_array_ptr_ref(f->slotnames, i); if (argname == unused_sym) jl_exceptionf(jl_argumenterror_type, @@ -776,7 +780,14 @@ JL_DLLEXPORT void jl_method_def(jl_svec_t *argdata, jl_array_ptr_1d_push(jl_all_methods, (jl_value_t*)m); } - jl_method_table_insert(mt, m, NULL); + if (any_argtypes_incomplete) { + if (jl_all_methods == NULL) + jl_all_methods = jl_alloc_vec_any(0); + jl_svec_t *d = jl_svec(2, mt, m); + arraylist_push(&module->deferred, d); + jl_array_ptr_1d_push(jl_all_methods, (jl_value_t*)d); + } else + jl_method_table_insert(mt, m, NULL); if (jl_newmeth_tracer) jl_call_tracer(jl_newmeth_tracer, (jl_value_t*)m); JL_GC_POP(); diff --git a/src/module.c b/src/module.c index c338f1944821a..fc9f1a930f3cc 100644 --- a/src/module.c +++ b/src/module.c @@ -683,10 +683,74 @@ void jl_binding_deprecation_warning(jl_module_t *m, jl_binding_t *b) } } +static int is_any_incomplete(jl_value_t *v) +{ + return jl_typeis(v, jl_placeholder_type) || + (jl_is_datatype(v) && ((jl_datatype_t*)v)->incomplete); +} + +static void complete_dt(jl_datatype_t *dt); +static void fixup_dependent_dt(jl_datatype_t *dt, jl_value_t *to_replace, jl_value_t *replacement) +{ + assert(dt->incomplete); + int incomplete_remains = is_any_incomplete(replacement); + for (int j = 0; j < jl_svec_len(dt->types); ++j) { + jl_value_t *t = jl_svecref(dt->types, j); + if (t == (jl_value_t*)to_replace) + jl_svecset(dt->types, j, replacement); + else if (!incomplete_remains && is_any_incomplete(t)) + incomplete_remains = 1; + } + for (int j = 0; j < jl_svec_len(dt->parameters); ++j) { + jl_value_t *t = jl_svecref(dt->parameters, j); + if (t == (jl_value_t*)to_replace) + jl_svecset(dt->parameters, j, replacement); + else if (!incomplete_remains && is_any_incomplete(t)) + incomplete_remains = 1; + } + if (!incomplete_remains) { + complete_dt(dt); + } +} + +static void complete_dt(jl_datatype_t *dt) { + dt->incomplete = 0; + jl_precompute_memoized_dt(dt); + jl_compute_field_offsets(dt); + for (int i = 0; i < jl_array_len(dt->dependents); ++i) { + jl_value_t *v = jl_array_ptr_ref(dt->dependents, i); + if (jl_is_datatype(v) && ((jl_datatype_t*)v)->incomplete) { + fixup_dependent_dt((jl_datatype_t*)v, (jl_value_t*)dt, (jl_value_t*)dt); + } + } + if (jl_is_tuple_type(dt)) { + jl_module_t *mod = jl_main_module; + for (size_t i = 0; i < mod->deferred.len; ++i) { + jl_svec_t *item = (jl_svec_t*)mod->deferred.items[i]; + jl_method_t *m = (jl_method_t*)jl_svecref(item, 1); + if ((jl_datatype_t*)jl_unwrap_unionall(m->sig) == dt) { + jl_method_table_insert( + (jl_methtable_t*)jl_svecref(item, 0), m, NULL); + } + } + } +} + +static void resolve_placeholder(jl_placeholder_t *ph, jl_value_t *val) +{ + for (int i = 0; i < jl_array_len(ph->dependents); ++i) { + jl_value_t *v = jl_array_ptr_ref(ph->dependents, i); + if (jl_is_datatype(v) && ((jl_datatype_t*)v)->incomplete) { + fixup_dependent_dt((jl_datatype_t*)v, (jl_value_t*) ph, val); + } + } +} + JL_DLLEXPORT void jl_checked_assignment(jl_binding_t *b, jl_value_t *rhs) { if (b->constp && b->value != NULL) { - if (!jl_egal(rhs, b->value)) { + if (!jl_typeis(b->value, jl_placeholder_type) && + !jl_egal(rhs, b->value)) { if (jl_typeof(rhs) != jl_typeof(b->value) || jl_is_type(rhs) /*|| jl_is_function(rhs)*/ || jl_is_module(rhs)) { jl_errorf("invalid redefinition of constant %s", @@ -696,8 +760,12 @@ JL_DLLEXPORT void jl_checked_assignment(jl_binding_t *b, jl_value_t *rhs) jl_symbol_name(b->name)); } } + jl_value_t *val = b->value; b->value = rhs; jl_gc_wb_binding(b, rhs); + if (val && jl_typeis(val, jl_placeholder_type)) { + resolve_placeholder((jl_placeholder_t*)val, rhs); + } } JL_DLLEXPORT void jl_declare_constant(jl_binding_t *b) diff --git a/src/staticdata.c b/src/staticdata.c index 75615d3bab2e9..aaf79315580c2 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -506,9 +506,9 @@ static void jl_write_module(jl_serializer_state *s, uintptr_t item, jl_module_t // The deferred list should be empty memset(&newm->deferred._space, 0, sizeof(newm->deferred._space)); - newm->deferred.len = 1; + newm->deferred.len = 0; newm->deferred.max = AL_N_INLINE; - newm->deferred.items = (void**)offsetof(jl_module_t, usings._space); + newm->deferred.items = (void**)offsetof(jl_module_t, deferred._space); arraylist_push(&s->relocs_list, (void*)(reloc_offset + offsetof(jl_module_t, deferred.items))); arraylist_push(&s->relocs_list, (void*)(((uintptr_t)DataRef << RELOC_TAG_OFFSET) + item)); diff --git a/test/incomplete.jl b/test/incomplete.jl new file mode 100644 index 0000000000000..2e634ca39a108 --- /dev/null +++ b/test/incomplete.jl @@ -0,0 +1,33 @@ +using Test + +macro placeholder(sym) + :(const $(esc(sym)) = $(Expr(:new, Core.Placeholder, :(Any[])))) +end + +@placeholder Bar1 +struct Foo1 + x::Bar1 +end +@test length(methods(Foo1)) == 0 +@test Foo1.incomplete +struct Bar1 + x::Foo1 +end +@test length(methods(Bar1)) == 2 +@test length(methods(Foo1)) == 2 + + +@placeholder Bar2 +@placeholder Baz2 +struct Foo2 + x::Bar2 + y::Baz2 +end +@test Foo2.incomplete +struct Bar2 + x::Foo2 +end +@test length(methods(Bar2)) == 0 +@test Bar2.incomplete +struct Baz2; end +@test !Foo2.incomplete && !Bar2.incomplete