From 04d66042f12bfbc2fa2f34f0b6738fef27436db2 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Thu, 1 Jun 2017 17:56:31 -0400 Subject: [PATCH] WIP: very bare-bones NamedTuple type Based on #16580 --- base/boot.jl | 2 +- base/essentials.jl | 9 ++++++ base/reflection.jl | 2 +- src/builtins.c | 3 +- src/codegen.cpp | 2 +- src/datatype.c | 5 ++-- src/dump.c | 9 ++++-- src/interpreter.c | 2 +- src/jltypes.c | 69 ++++++++++++++++++++++++++++++++++++---------- src/julia.h | 21 +++++++++++++- src/rtutils.c | 2 +- 11 files changed, 99 insertions(+), 27 deletions(-) diff --git a/base/boot.jl b/base/boot.jl index 31255bea909a5..634adaba5bfc3 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -120,7 +120,7 @@ export # key types Any, DataType, Vararg, ANY, NTuple, Tuple, Type, UnionAll, TypeName, TypeVar, Union, Void, - SimpleVector, AbstractArray, DenseArray, + SimpleVector, AbstractArray, DenseArray, NamedTuple, # special objects Function, CodeInfo, Method, MethodTable, TypeMapEntry, TypeMapLevel, Module, Symbol, Task, Array, WeakRef, VecElement, diff --git a/base/essentials.jl b/base/essentials.jl index 02e5a6815cad6..97847e1cd2586 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -359,3 +359,12 @@ call obsolete versions of a function `f`. `f` directly, and the type of the result cannot be inferred by the compiler.) """ invokelatest(f, args...) = Core._apply_latest(f, args) + +@generated function namedtuple(::Type{NamedTuple{names,T} where T}, args...) where names + N = length(names) + if length(args) != N + :(throw(ArgumentError("wrong number of arguments to named tuple constructor"))) + else + Expr(:new, :(NamedTuple{names,$(Tuple{args...})}), [ :(args[$i]) for i in 1:N ]...) + end +end diff --git a/base/reflection.jl b/base/reflection.jl index a13fc7e9bdb4a..9d4009b3322e5 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -118,7 +118,7 @@ julia> fieldname(SparseMatrixCSC,5) :nzval ``` """ -fieldname(t::DataType, i::Integer) = t.name.names[i]::Symbol +fieldname(t::DataType, i::Integer) = (isdefined(t,:names) ? t.names[i] : t.name.names[i])::Symbol fieldname(t::UnionAll, i::Integer) = fieldname(unwrap_unionall(t), i) fieldname(t::Type{<:Tuple}, i::Integer) = i < 1 || i > nfields(t) ? throw(BoundsError(t, i)) : Int(i) diff --git a/src/builtins.c b/src/builtins.c index 70ca4262d1ba2..f6f6aedd88670 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -254,7 +254,7 @@ JL_CALLABLE(jl_f_sizeof) if (dx->name == jl_array_typename || dx == jl_symbol_type || dx == jl_simplevector_type || dx == jl_string_type) jl_error("type does not have a canonical binary representation"); - if (!(dx->name->names == jl_emptysvec && jl_datatype_size(dx) > 0)) { + if (!(jl_field_names(dx) == jl_emptysvec && jl_datatype_size(dx) > 0)) { // names===() and size > 0 => bitstype, size always known if (dx->abstract || !jl_is_leaf_type(x)) jl_error("argument is an abstract type; size is indeterminate"); @@ -1154,6 +1154,7 @@ void jl_init_primitives(void) add_builtin("QuoteNode", (jl_value_t*)jl_quotenode_type); add_builtin("NewvarNode", (jl_value_t*)jl_newvarnode_type); add_builtin("GlobalRef", (jl_value_t*)jl_globalref_type); + add_builtin("NamedTuple", (jl_value_t*)jl_namedtuple_type); #ifdef _P64 add_builtin("Int", (jl_value_t*)jl_int64_type); diff --git a/src/codegen.cpp b/src/codegen.cpp index a2e99c6bd4001..887bf1b94debf 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3156,7 +3156,7 @@ static bool emit_builtin_call(jl_cgval_t *ret, jl_value_t *f, jl_value_t **args, // this is issue #8798 sty != (jl_value_t*)jl_datatype_type) { if (jl_is_leaf_type(sty) || - (((jl_datatype_t*)sty)->name->names == jl_emptysvec && jl_datatype_size(sty) > 0)) { + (jl_field_names((jl_datatype_t*)sty) == jl_emptysvec && jl_datatype_size(sty) > 0)) { *ret = mark_julia_type(ConstantInt::get(T_size, jl_datatype_size(sty)), false, jl_long_type, ctx); JL_GC_POP(); return true; diff --git a/src/datatype.c b/src/datatype.c index 1c2486414b840..a363b48d63047 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -81,6 +81,7 @@ jl_datatype_t *jl_new_uninitialized_datatype(void) t->hasfreetypevars = 0; t->isleaftype = 1; t->layout = NULL; + t->names = NULL; return t; } @@ -224,7 +225,7 @@ void jl_compute_field_offsets(jl_datatype_t *st) uint64_t max_offset = (((uint64_t)1) << 32) - 1; uint64_t max_size = max_offset >> 1; - if (st->name->wrapper) { + if (st->name->wrapper && !jl_is_namedtuple_type(st)) { // If layout doesn't depend on type parameters, it's stored in st->name->wrapper // and reused by all subtypes. jl_datatype_t *w = (jl_datatype_t*)jl_unwrap_unionall(st->name->wrapper); @@ -689,7 +690,7 @@ JL_DLLEXPORT jl_value_t *jl_new_struct_uninit(jl_datatype_t *type) JL_DLLEXPORT int jl_field_index(jl_datatype_t *t, jl_sym_t *fld, int err) { - jl_svec_t *fn = t->name->names; + jl_svec_t *fn = jl_field_names(t); for(size_t i=0; i < jl_svec_len(fn); i++) { if (jl_svecref(fn,i) == (jl_value_t*)fld) { return (int)i; diff --git a/src/dump.c b/src/dump.c index 73e7a2c60a42a..e5b26b1ab0db5 100644 --- a/src/dump.c +++ b/src/dump.c @@ -652,6 +652,7 @@ static void jl_serialize_datatype(jl_serializer_state *s, jl_datatype_t *dt) if (has_instance) jl_serialize_value(s, dt->instance); jl_serialize_value(s, dt->name); + jl_serialize_value(s, dt->names); jl_serialize_value(s, dt->parameters); jl_serialize_value(s, dt->super); jl_serialize_value(s, dt->types); @@ -1534,6 +1535,8 @@ static jl_value_t *jl_deserialize_datatype(jl_serializer_state *s, int pos, jl_v } dt->name = (jl_typename_t*)jl_deserialize_value(s, (jl_value_t**)&dt->name); jl_gc_wb(dt, dt->name); + dt->names = (jl_svec_t*)jl_deserialize_value(s, (jl_value_t**)&dt->names); + jl_gc_wb(dt, dt->names); dt->parameters = (jl_svec_t*)jl_deserialize_value(s, (jl_value_t**)&dt->parameters); jl_gc_wb(dt, dt->parameters); dt->super = (jl_datatype_t*)jl_deserialize_value(s, (jl_value_t**)&dt->super); @@ -3365,7 +3368,6 @@ void jl_init_serializer(void) jl_box_int32(30), jl_box_int32(31), jl_box_int32(32), #ifndef _P64 jl_box_int32(33), jl_box_int32(34), jl_box_int32(35), - jl_box_int32(36), jl_box_int32(37), #endif jl_box_int64(0), jl_box_int64(1), jl_box_int64(2), jl_box_int64(3), jl_box_int64(4), jl_box_int64(5), @@ -3380,7 +3382,6 @@ void jl_init_serializer(void) jl_box_int64(30), jl_box_int64(31), jl_box_int64(32), #ifdef _P64 jl_box_int64(33), jl_box_int64(34), jl_box_int64(35), - jl_box_int64(36), jl_box_int64(37), #endif jl_labelnode_type, jl_linenumbernode_type, jl_gotonode_type, jl_quotenode_type, jl_type_type, jl_bottom_type, jl_ref_type, @@ -3406,7 +3407,8 @@ void jl_init_serializer(void) jl_intrinsic_type->name, jl_task_type->name, jl_labelnode_type->name, jl_linenumbernode_type->name, jl_builtin_type->name, jl_gotonode_type->name, jl_quotenode_type->name, jl_globalref_type->name, jl_typeofbottom_type->name, - jl_string_type->name, jl_abstractstring_type->name, + jl_string_type->name, jl_abstractstring_type->name, jl_namedtuple_type, + jl_namedtuple_typename, ptls->root_task, @@ -3450,6 +3452,7 @@ void jl_init_serializer(void) arraylist_push(&builtin_typenames, ((jl_datatype_t*)jl_unwrap_unionall((jl_value_t*)jl_densearray_type))->name); arraylist_push(&builtin_typenames, jl_tuple_typename); arraylist_push(&builtin_typenames, jl_vararg_typename); + arraylist_push(&builtin_typenames, jl_namedtuple_typename); } #ifdef __cplusplus diff --git a/src/interpreter.c b/src/interpreter.c index 55ac7b7b819a3..3b1fbbc39ed08 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -168,7 +168,7 @@ void jl_set_datatype_super(jl_datatype_t *tt, jl_value_t *super) if (!jl_is_datatype(super) || !jl_is_abstracttype(super) || tt->name == ((jl_datatype_t*)super)->name || jl_subtype(super,(jl_value_t*)jl_vararg_type) || - jl_is_tuple_type(super) || + jl_is_tuple_type(super) || jl_is_namedtuple_type(super) || jl_subtype(super,(jl_value_t*)jl_type_type) || super == (jl_value_t*)jl_builtin_type) { jl_errorf("invalid subtyping in definition of %s", diff --git a/src/jltypes.c b/src/jltypes.c index 34ef0c3160547..5b77f24779018 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -113,6 +113,8 @@ jl_unionall_t *jl_pointer_type; jl_typename_t *jl_pointer_typename; jl_datatype_t *jl_void_type; jl_datatype_t *jl_voidpointer_type; +jl_typename_t *jl_namedtuple_typename; +jl_unionall_t *jl_namedtuple_type; jl_value_t *jl_an_empty_vec_any=NULL; jl_value_t *jl_stackovf_exception; #ifdef SEGV_EXCEPTION @@ -1006,6 +1008,7 @@ static jl_value_t *inst_datatype(jl_datatype_t *dt, jl_svec_t *p, jl_value_t **i jl_typestack_t top; jl_typename_t *tn = dt->name; int istuple = (tn == jl_tuple_typename); + int isnamedtuple = (tn == jl_namedtuple_typename); // check type cache if (cacheable) { JL_LOCK(&typecache_lock); // Might GC @@ -1128,7 +1131,32 @@ static jl_value_t *inst_datatype(jl_datatype_t *dt, jl_svec_t *p, jl_value_t **i ndt->super = NULL; ndt->parameters = p; jl_gc_wb(ndt, ndt->parameters); - ndt->types = istuple ? p : NULL; // to be filled in below + ndt->types = NULL; // to be filled in below + if (istuple) { + ndt->types = p; + } + else if (isnamedtuple) { + jl_value_t *names_tup = jl_svecref(p, 0); + jl_value_t *values_tt = jl_svecref(p, 1); + if (!jl_has_free_typevars(names_tup) && !jl_has_free_typevars(values_tt)) { + if (!jl_is_tuple(names_tup)) + jl_type_error_rt("NamedTuple", "names", jl_anytuple_type, names_tup); + size_t nf = jl_nfields(names_tup); + jl_svec_t *names = jl_alloc_svec_uninit(nf); + for(size_t i = 0; i < nf; i++) { + jl_value_t *ni = jl_fieldref(names_tup, i); + if (!jl_is_symbol(ni)) + jl_type_error_rt("NamedTuple", "name", jl_symbol_type, ni); + jl_svecset(names, i, ni); + } + if (jl_is_va_tuple(values_tt) || jl_nparams(values_tt) != nf) + jl_error("NamedTuple names and field types must have matching lengths"); + ndt->names = names; + jl_gc_wb(ndt, ndt->names); + ndt->types = ((jl_datatype_t*)values_tt)->parameters; + jl_gc_wb(ndt, ndt->types); + } + } ndt->mutabl = dt->mutabl; ndt->abstract = dt->abstract; ndt->instance = NULL; @@ -1142,7 +1170,7 @@ static jl_value_t *inst_datatype(jl_datatype_t *dt, jl_svec_t *p, jl_value_t **i if (cacheable && !ndt->abstract) ndt->uid = jl_assign_type_uid(); - if (istuple) { + if (istuple || isnamedtuple) { ndt->super = jl_any_type; } else if (dt->super) { @@ -1150,7 +1178,7 @@ static jl_value_t *inst_datatype(jl_datatype_t *dt, jl_svec_t *p, jl_value_t **i jl_gc_wb(ndt, ndt->super); } jl_svec_t *ftypes = dt->types; - if (!istuple && ndt->name->names == jl_emptysvec) { + if (!istuple && jl_field_names(ndt) == jl_emptysvec) { assert(ftypes == NULL || ftypes == jl_emptysvec); ndt->size = dt->size; ndt->layout = dt->layout; @@ -1163,13 +1191,13 @@ static jl_value_t *inst_datatype(jl_datatype_t *dt, jl_svec_t *p, jl_value_t **i if (ftypes == NULL || dt->super == NULL) { // in the process of creating this type definition: // need to instantiate the super and types fields later - assert(inside_typedef && !istuple); + assert(inside_typedef && !istuple && !isnamedtuple); arraylist_push(&partial_inst, ndt); } else { - if (ftypes != jl_emptysvec) { + if (ftypes != jl_emptysvec || isnamedtuple) { assert(!ndt->abstract); - if (!istuple) { + if (!istuple && !isnamedtuple) { // recursively instantiate the types of the fields ndt->types = inst_all(ftypes, env, stack, 1); jl_gc_wb(ndt, ndt->types); @@ -1188,6 +1216,8 @@ static jl_value_t *inst_datatype(jl_datatype_t *dt, jl_svec_t *p, jl_value_t **i } if (istuple) ndt->ninitialized = ntp - isvatuple; + else if (isnamedtuple) + ndt->ninitialized = jl_svec_len(ndt->types); else ndt->ninitialized = dt->ninitialized; @@ -1568,11 +1598,12 @@ void jl_init_types(void) 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(16, + jl_datatype_type->name->names = jl_perm_symsvec(17, "name", "super", "parameters", "types", + "names", "instance", "layout", "size", @@ -1585,11 +1616,11 @@ void jl_init_types(void) "depth", "hasfreetypevars", "isleaftype"); - jl_datatype_type->types = jl_svec(16, + jl_datatype_type->types = jl_svec(17, jl_typename_type, jl_datatype_type, jl_simplevector_type, - jl_simplevector_type, + jl_simplevector_type, jl_simplevector_type, jl_any_type, // instance jl_any_type, jl_any_type, jl_any_type, jl_any_type, jl_any_type, jl_any_type, jl_any_type, jl_any_type, @@ -2031,20 +2062,28 @@ void jl_init_types(void) jl_perm_symsvec(1, "len"), jl_svec1(jl_long_type), 0, 1, 1); + jl_tvar_t *ntval_var = jl_new_typevar(jl_symbol("T"), (jl_value_t*)jl_bottom_type, + (jl_value_t*)jl_anytuple_type); + tv = jl_svec2(tvar("names"), ntval_var); + jl_datatype_t *ntt = jl_new_datatype(jl_symbol("NamedTuple"), jl_any_type, tv, + jl_emptysvec, jl_emptysvec, 0, 0, 0); + jl_namedtuple_type = (jl_unionall_t*)ntt->name->wrapper; + jl_namedtuple_typename = ntt->name; + // complete builtin type metadata jl_value_t *pointer_void = jl_apply_type1((jl_value_t*)jl_pointer_type, (jl_value_t*)jl_void_type); jl_voidpointer_type = (jl_datatype_t*)pointer_void; - jl_svecset(jl_datatype_type->types, 5, jl_voidpointer_type); - jl_svecset(jl_datatype_type->types, 6, jl_int32_type); + jl_svecset(jl_datatype_type->types, 6, jl_voidpointer_type); jl_svecset(jl_datatype_type->types, 7, jl_int32_type); jl_svecset(jl_datatype_type->types, 8, jl_int32_type); - jl_svecset(jl_datatype_type->types, 9, jl_bool_type); + jl_svecset(jl_datatype_type->types, 9, jl_int32_type); jl_svecset(jl_datatype_type->types, 10, jl_bool_type); - jl_svecset(jl_datatype_type->types, 11, jl_voidpointer_type); + jl_svecset(jl_datatype_type->types, 11, jl_bool_type); jl_svecset(jl_datatype_type->types, 12, jl_voidpointer_type); - jl_svecset(jl_datatype_type->types, 13, jl_int32_type); - jl_svecset(jl_datatype_type->types, 14, jl_bool_type); + jl_svecset(jl_datatype_type->types, 13, jl_voidpointer_type); + jl_svecset(jl_datatype_type->types, 14, jl_int32_type); jl_svecset(jl_datatype_type->types, 15, jl_bool_type); + jl_svecset(jl_datatype_type->types, 16, jl_bool_type); jl_svecset(jl_simplevector_type->types, 0, jl_long_type); jl_svecset(jl_typename_type->types, 1, jl_module_type); jl_svecset(jl_typename_type->types, 6, jl_long_type); diff --git a/src/julia.h b/src/julia.h index c6d63d1f3517c..f31323eb9b280 100644 --- a/src/julia.h +++ b/src/julia.h @@ -369,6 +369,7 @@ typedef struct _jl_datatype_t { struct _jl_datatype_t *super; jl_svec_t *parameters; jl_svec_t *types; + jl_svec_t *names; jl_value_t *instance; // for singletons const jl_datatype_layout_t *layout; int32_t size; // TODO: move to _jl_datatype_layout_t @@ -555,6 +556,8 @@ extern JL_DLLEXPORT jl_datatype_t *jl_voidpointer_type; extern JL_DLLEXPORT jl_unionall_t *jl_pointer_type; extern JL_DLLEXPORT jl_unionall_t *jl_ref_type; extern JL_DLLEXPORT jl_typename_t *jl_pointer_typename; +extern JL_DLLEXPORT jl_typename_t *jl_namedtuple_typename; +extern JL_DLLEXPORT jl_unionall_t *jl_namedtuple_type; extern JL_DLLEXPORT jl_value_t *jl_array_uint8_type; extern JL_DLLEXPORT jl_value_t *jl_array_any_type; @@ -774,7 +777,17 @@ STATIC_INLINE void jl_array_uint8_set(void *a, size_t i, uint8_t x) #define jl_gf_name(f) (jl_gf_mtable(f)->name) // struct type info -#define jl_field_name(st,i) (jl_sym_t*)jl_svecref(((jl_datatype_t*)st)->name->names, (i)) +STATIC_INLINE jl_svec_t *jl_field_names(jl_datatype_t *st) +{ + jl_svec_t *names = st->names; + if (!names) + names = st->name->names; + return names; +} +STATIC_INLINE jl_sym_t *jl_field_name(jl_datatype_t *st, size_t i) +{ + 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) #define jl_datatype_size(t) (((jl_datatype_t*)t)->size) @@ -957,6 +970,12 @@ STATIC_INLINE int jl_is_tuple_type(void *t) ((jl_datatype_t*)(t))->name == jl_tuple_typename); } +STATIC_INLINE int jl_is_namedtuple_type(void *t) +{ + return (jl_is_datatype(t) && + ((jl_datatype_t*)(t))->name == jl_namedtuple_typename); +} + STATIC_INLINE int jl_is_vecelement_type(jl_value_t* t) { return (jl_is_datatype(t) && diff --git a/src/rtutils.c b/src/rtutils.c index f6a1e4cb68083..7a11776e2c7a9 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -868,7 +868,7 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt i = 1; for (; i < tlen; i++) { if (!istuple) { - n += jl_printf(out, "%s", jl_symbol_name((jl_sym_t*)jl_svecref(vt->name->names, i))); + n += jl_printf(out, "%s", jl_symbol_name((jl_sym_t*)jl_svecref(jl_field_names(vt), i))); n += jl_printf(out, "="); } size_t offs = jl_field_offset(vt, i);