From 2aec0010616dfd350fa8539f1de6f48a5e35822c Mon Sep 17 00:00:00 2001 From: Oscar Blumberg Date: Tue, 24 May 2016 10:43:43 -0400 Subject: [PATCH 1/4] wip kwargs --- base/boot.jl | 23 +++++++--- base/essentials.jl | 94 ++++++++++++++++++++++++++++++++++++++++ base/inference.jl | 80 ++++++++++++++++++++++++++++++++-- base/multi.jl | 8 ++-- base/reflection.jl | 4 +- src/alloc.c | 5 ++- src/builtin_proto.h | 2 +- src/builtins.c | 43 +++++++++++++++--- src/codegen.cpp | 37 ++++++++++++---- src/dump.c | 9 ++-- src/init.c | 3 ++ src/jltypes.c | 59 ++++++++++++++++++++----- src/julia-syntax.scm | 101 +++++++++++++++++++++++++++++++++++++------ src/julia.h | 17 +++++++- test/keywordargs.jl | 2 +- 15 files changed, 426 insertions(+), 61 deletions(-) diff --git a/base/boot.jl b/base/boot.jl index 19e245aefa20f..ab212e1e313a2 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -117,7 +117,7 @@ export # key types Any, DataType, Vararg, ANY, NTuple, Tuple, Type, TypeConstructor, TypeName, TypeVar, Union, Void, - SimpleVector, AbstractArray, DenseArray, + SimpleVector, AbstractArray, DenseArray, Struct, # special objects Function, LambdaInfo, Method, MethodTable, TypeMapEntry, TypeMapLevel, Module, Symbol, Task, Array, WeakRef, VecElement, @@ -332,15 +332,28 @@ end atdoc = (str, expr) -> Expr(:escape, expr) atdoc!(λ) = global atdoc = λ +immutable KwKeys{names} +end +function structdiff +end +function structmerge +end +function fieldname +end +function structadd +end +Struct(;args...) = args module TopModule # this defines the types that lowering expects to be defined in a (top) module # that are usually inherited from Core, but could be defined custom for a module - using Core: Box, IntrinsicFunction, Builtin, + using Core: Box, IntrinsicFunction, Builtin, Struct, KwKeys, arrayref, arrayset, arraysize, - _expr, _apply, typeassert, apply_type, svec, kwfunc - export Box, IntrinsicFunction, Builtin, + _expr, _apply, typeassert, apply_type, svec, kwfunc, fieldname, + struct, structdiff, structmerge, structadd + export Box, IntrinsicFunction, Builtin, Struct, KwKeys, arrayref, arrayset, arraysize, - _expr, _apply, typeassert, apply_type, svec, kwfunc + _expr, _apply, typeassert, apply_type, svec, kwfunc, fieldname, + struct, structdiff, structmerge, structadd end using .TopModule ccall(:jl_set_istopmod, Void, (Bool,), true) diff --git a/base/essentials.jl b/base/essentials.jl index f64418883a0e5..9f759b5828c7e 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -199,3 +199,97 @@ const (:) = Colon() # For passing constants through type inference immutable Val{T} end + +function sortedmerge(x,y) + ix,nx = 0, length(x) + iy,ny = 0, length(y) + z = Array(Symbol, nx+ny) + iz = 1 + while ix+iy < nx+ny + c = if ix < nx && (iy < ny && x[ix+1] <= y[iy+1] || iy == ny) + ix += 1 + x[ix] + else + iy += 1 + y[iy] + end + if iz == 1 || z[iz-1] != c + z[iz] = c + iz += 1 + end + end + resize!(z, iz-1) + (z...,) +end + +@generated function Core.structmerge{xn,xT,yn,yT}(x::Core.Struct{xn,xT},y::Core.Struct{yn,yT}) + names = sortedmerge(xn,yn) + fields = map(names) do name + if findfirst(xn, name) > 0 + :(getfield(x, $(Expr(:quote, name)))) + else + :(getfield(y, $(Expr(:quote, name)))) + end + end + quote + Core.struct($names, $(fields...)) + end +end + +function Core.structmerge{xn,xT}(x::Core.Struct{xn,xT}, y) + kvs = collect(y) + sort!(kvs, 1, length(kvs), Base.Sort.InsertionSort, Base.Order.By(x -> x[1])) + names = sortedmerge(xn,map(first,kvs)) + n = length(names) + values = Array(Any, n) + for (name,val) in y + idx = findfirst(names, name) + if idx > 0 + values[idx] = val + end + end + for i = 1:n + if isdefined(x,names[i]) + values[i] = getfield(x,names[i]) + end + end + Core.struct(names, values...) +end +function Core.structadd{n,T}(x::Core.Struct{n,T}, y) + Core.structmerge(x, (y,)) +end +function sorteddiff(x,y) + ix,xn = 1,length(x) + iy,yn = 1,length(y) + z = Array(Symbol, xn) + iz = 1 + while ix <= xn + v = x[ix] + if iy > yn || v < y[iy] + z[iz] = v + iz += 1 + ix += 1 + elseif v === y[iy] + ix += 1 + iy += 1 + else + iy += 1 + end + end + resize!(z, iz-1) + (z...,) +end + +@generated function Core.structdiff{xn,xT,yn}(x::Core.Struct{xn,xT},::Core.KwKeys{yn}) + names = sorteddiff(xn,yn) + fields = map(names) do name + :(getfield(x, $(Expr(:quote, name)))) + end + quote + Core.struct($names, $(fields...)) + end +end + +start(s::Core.Struct) = 1 +done(s::Core.Struct, i) = i > nfields(s) +next(s::Core.Struct, i) = ((fieldname(typeof(s),i) => getfield(s,i)), i+1) diff --git a/base/inference.jl b/base/inference.jl index 935bb03b7b9d2..65293bdbe6a8d 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -296,7 +296,42 @@ add_tfunc(is, 2, 2, return Bool end end) -add_tfunc(isdefined, 1, IInf, (args...)->Bool) +function isdefined_tfunc(args...) + a1 = widenconst(args[1]) + if isType(a1) + a1 = typeof(a1.parameters[1]) + if a1 === TypeVar + return Bool + end + end + if isleaftype(a1) + if a1 <: Array # TODO + elseif a1 === Module # TODO + elseif length(args) == 2 && isa(args[2],Const) + n = nfields(a1) + val = args[2].val + idx::Int = 0 + if isa(val, Symbol) + for i=1:n + if fieldname(a1, i) === val + idx = i + break + end + end + elseif isa(val, Int) + idx = val::Int + end + + if 1 <= idx <= a1.ninitialized + return Const(true) + elseif idx <= 0 || idx > n + return Const(false) + end + end + end + Bool +end +add_tfunc(isdefined, 1, IInf, isdefined_tfunc) add_tfunc(Core.sizeof, 1, 1, x->Int) add_tfunc(nfields, 1, 1, x->(isa(x,Const) ? Const(nfields(x.val)) : isType(x) && isleaftype(x.parameters[1]) ? Const(nfields(x.parameters[1])) : @@ -469,7 +504,11 @@ function getfield_tfunc(s0::ANY, name) end end end - snames = s.name.names + snames = isdefined(s,:names) ? s.names : s.name.names + if s <: Core.Struct && !isleaftype(s) + # TODO + return Any,false + end for i=1:length(snames) if is(snames[i],fld) R = s.types[i] @@ -642,6 +681,28 @@ function builtin_tfunction(f::ANY, argtypes::Array{Any,1}, sv::InferenceState) end end return Const(tuple(map(a->a.val, argtypes)...)) + elseif is(f,Core.struct) + if length(argtypes) >= 1 + if isa(argtypes[1],Const) + names = argtypes[1].val + values = argtypes[2:end] + has_dup = false + n = nfields(names) + for i=2:n + name = getfield(names, i) + for j=1:i-1 + if name == getfield(names, j) + has_dup = true + break + end + end + end + if !has_dup && length(values) == nfields(names) + tup = limit_tuple_type(argtypes_to_type(values)) + return isleaftype(tup) ? Core.Struct{names, tup} : Core.Struct{names,TypeVar(:_,tup)} + end + end + end elseif is(f,svec) return SimpleVector elseif is(f,arrayset) @@ -2165,7 +2226,7 @@ end const _pure_builtins = Any[tuple, svec, fieldtype, apply_type, is, isa, typeof] # known effect-free calls (might not be affect-free) -const _pure_builtins_volatile = Any[getfield, arrayref] +const _pure_builtins_volatile = Any[getfield, arrayref, isdefined] function is_pure_builtin(f::ANY) if contains_is(_pure_builtins, f) @@ -2314,6 +2375,17 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference if isa(f,IntrinsicFunction) || ft ⊑ IntrinsicFunction return NF end + #=println("F ==============") + println(f) + println(istopfunction(topmod,f,:struct)) + println(e.typ) + println("================")=# + if (is(f, Core.struct) && + e.typ <: Core.Struct && isleaftype(e.typ)) + new_e = Expr(:new, e.typ, argexprs[3:end]...) + new_e.typ = e.typ + return (new_e, ()) + end atype = argtypes_to_type(atypes) if length(atype.parameters) - 1 > MAX_TUPLETYPE_LEN @@ -3258,7 +3330,7 @@ function gotoifnot_elim_pass!(linfo::LambdaInfo, sv::InferenceState) # doesn't recognize the error for strictly non-Bool condition) if isa(val, Bool) # in case there's side effects... (like raising `UndefVarError`) - body[i - 1] = cond + body[i - 1] = effect_free(cond, sv, true) ? nothing : cond if val === false insert!(body, i, GotoNode(expr.args[2])) i += 1 diff --git a/base/multi.jl b/base/multi.jl index 3cb3f5d8079bf..08bf3771e2c98 100644 --- a/base/multi.jl +++ b/base/multi.jl @@ -805,7 +805,7 @@ end function remotecall(f, w::Worker, args...; kwargs...) rr = Future(w) #println("$(myid()) asking for $rr") - send_msg(w, CallMsg{:call}(f, args, kwargs, remoteref_id(rr))) + send_msg(w, CallMsg{:call}(f, args, [kwargs...], remoteref_id(rr))) rr end @@ -823,7 +823,7 @@ function remotecall_fetch(f, w::Worker, args...; kwargs...) oid = RRID() rv = lookup_ref(oid) rv.waitingfor = w.id - send_msg(w, CallMsg{:call_fetch}(f, args, kwargs, oid)) + send_msg(w, CallMsg{:call_fetch}(f, args, [kwargs...], oid)) v = take!(rv) delete!(PGRP.refs, oid) isa(v, RemoteException) ? throw(v) : v @@ -840,7 +840,7 @@ function remotecall_wait(f, w::Worker, args...; kwargs...) rv = lookup_ref(prid) rv.waitingfor = w.id rr = Future(w) - send_msg(w, CallWaitMsg(f, args, kwargs, remoteref_id(rr), prid)) + send_msg(w, CallWaitMsg(f, args, [kwargs...], remoteref_id(rr), prid)) v = fetch(rv.c) delete!(PGRP.refs, prid) isa(v, RemoteException) && throw(v) @@ -860,7 +860,7 @@ function remote_do(f, w::LocalProcess, args...; kwargs...) end function remote_do(f, w::Worker, args...; kwargs...) - send_msg(w, RemoteDoMsg(f, args, kwargs)) + send_msg(w, RemoteDoMsg(f, args, [kwargs...])) nothing end diff --git a/base/reflection.jl b/base/reflection.jl index 4e75ba95222ef..ab983db4bf413 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -56,8 +56,8 @@ end Get the name of field `i` of a `DataType`. """ -fieldname(t::DataType, i::Integer) = t.name.names[i]::Symbol -fieldname{T<:Tuple}(t::Type{T}, i::Integer) = i < 1 || i > nfields(t) ? throw(BoundsError(t, i)) : Int(i) +Core.fieldname(t::DataType, i::Integer) = isdefined(t,:names) ? t.names[i]::Symbol : t.name.names[i]::Symbol +Core.fieldname{T<:Tuple}(t::Type{T}, i::Integer) = i < 1 || i > nfields(t) ? throw(BoundsError(t, i)) : Int(i) """ fieldnames(x::DataType) diff --git a/src/alloc.c b/src/alloc.c index b8942ee19ab43..438d8010dad5c 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -60,6 +60,8 @@ jl_value_t *jl_stackovf_exception; #ifdef SEGV_EXCEPTION jl_value_t *jl_segv_exception; #endif +jl_typename_t *jl_struct_typename; +jl_datatype_t *jl_struct_type; JL_DLLEXPORT jl_value_t *jl_diverror_exception; JL_DLLEXPORT jl_value_t *jl_domain_exception; JL_DLLEXPORT jl_value_t *jl_overflow_exception; @@ -160,7 +162,7 @@ void jl_assign_bits(void *dest, jl_value_t *bits) 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; @@ -829,6 +831,7 @@ JL_DLLEXPORT jl_datatype_t *jl_new_uninitialized_datatype(size_t nfields, int8_t // corruption otherwise. t->fielddesc_type = fielddesc_type; t->nfields = nfields; + t->names = NULL; t->haspadding = 0; t->pointerfree = 0; t->depth = 0; diff --git a/src/builtin_proto.h b/src/builtin_proto.h index 695916e7fdc15..335fbaa0c84ac 100644 --- a/src/builtin_proto.h +++ b/src/builtin_proto.h @@ -30,7 +30,7 @@ DECLARE_BUILTIN(fieldtype); DECLARE_BUILTIN(arrayref); DECLARE_BUILTIN(arrayset); DECLARE_BUILTIN(arraysize); DECLARE_BUILTIN(apply_type); DECLARE_BUILTIN(applicable); DECLARE_BUILTIN(invoke); DECLARE_BUILTIN(_expr); - +DECLARE_BUILTIN(struct); #ifdef __cplusplus } #endif diff --git a/src/builtins.c b/src/builtins.c index 60a323ac09b81..d019e03988489 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -338,7 +338,7 @@ JL_CALLABLE(jl_f_sizeof) jl_datatype_t *dx = (jl_datatype_t*)x; if (dx->name == jl_array_typename || dx == jl_symbol_type || dx == jl_simplevector_type) jl_error("type does not have a canonical binary representation"); - if (!(dx->name->names == jl_emptysvec && dx->size > 0)) { + if (!(jl_field_names(dx) == jl_emptysvec && dx->size > 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"); @@ -607,10 +607,9 @@ JL_CALLABLE(jl_f_isdefined) // tuples --------------------------------------------------------------------- -JL_CALLABLE(jl_f_tuple) +static jl_datatype_t *tupletype_from_args(jl_value_t **args, size_t nargs) { size_t i; - if (nargs == 0) return (jl_value_t*)jl_emptytuple; jl_datatype_t *tt; if (nargs < jl_page_size/sizeof(jl_value_t*)) { jl_value_t **types = (jl_value_t**)alloca(nargs*sizeof(jl_value_t*)); @@ -626,9 +625,41 @@ JL_CALLABLE(jl_f_tuple) tt = jl_inst_concrete_tupletype(types); JL_GC_POP(); } - return jl_new_structv(tt, args, nargs); + return tt; +} + +JL_CALLABLE(jl_f_tuple) +{ + if (nargs == 0) return (jl_value_t*)jl_emptytuple; + return jl_new_structv(tupletype_from_args(args,nargs), args, nargs); +} + + +JL_CALLABLE(jl_f_struct) +{ + JL_NARGSV(struct, 1); + jl_value_t *names = args[0]; + JL_TYPECHK(struct, tuple, args[0]); + size_t n = jl_nfields(args[0]); + JL_NARGS(struct, n+1, n+1); + for(size_t i=0; i < n; i++) { + jl_value_t *name = jl_get_nth_field(args[0], i); + for (size_t j = 0; j < i; j++) { + if (name == jl_get_nth_field(names, j)) { + jl_errorf("struct field names must be unique (%s)", jl_symbol_name(name)); + } + } + } + jl_value_t *t=NULL; + JL_GC_PUSH1(&t); + t = tupletype_from_args(args+1,n); + t = jl_svec(2, names, t); + t = jl_apply_type(jl_struct_type, t); + JL_GC_POP(); + return jl_new_structv(t, args+1, n); } + JL_CALLABLE(jl_f_svec) { size_t i; @@ -1088,6 +1119,7 @@ void jl_init_primitives(void) add_builtin_func("typeassert", jl_f_typeassert); add_builtin_func("throw", jl_f_throw); add_builtin_func("tuple", jl_f_tuple); + add_builtin_func("struct", jl_f_struct); // field access add_builtin_func("getfield", jl_f_getfield); @@ -1154,6 +1186,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("Struct", (jl_value_t*)jl_struct_type); #ifdef _P64 add_builtin("Int", (jl_value_t*)jl_int64_type); @@ -1452,7 +1485,7 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt else { for (size_t i = 0; 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))); //jl_fielddesc_t f = t->fields[i]; n += jl_printf(out, "="); } diff --git a/src/codegen.cpp b/src/codegen.cpp index fade5478f78f0..b5b6317350c84 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1120,13 +1120,17 @@ void *jl_get_llvmf(jl_tupletype_t *tt, bool getwrapper, bool getdeclarations) jl_lambda_info_t *linfo = NULL; JL_GC_PUSH2(&linfo, &tt); if (tt != NULL) { - linfo = jl_get_specialization1(tt); - if (linfo == NULL) { - linfo = jl_method_lookup_by_type( - ((jl_datatype_t*)jl_tparam0(tt))->name->mt, tt, 0, 0); - if (linfo == NULL || jl_has_call_ambiguities(tt, linfo->def)) { - JL_GC_POP(); - return NULL; + jl_value_t *ftype = jl_tparam0(tt); + if (!(jl_subtype(ftype, (jl_value_t*)jl_builtin_type, 0) || + jl_subtype(ftype, (jl_value_t*)jl_intrinsic_type, 0))) { + linfo = jl_get_specialization1(tt); + if (linfo == NULL) { + linfo = jl_method_lookup_by_type(((jl_datatype_t*)ftype)->name->mt, + tt, 0, 0); + if (linfo == NULL || jl_has_call_ambiguities(tt, linfo->def)) { + JL_GC_POP(); + return NULL; + } } } } @@ -2579,7 +2583,7 @@ static bool emit_builtin_call(jl_cgval_t *ret, jl_value_t *f, jl_value_t **args, // this is issue #8798 sty != jl_datatype_type) { if (jl_is_leaf_type((jl_value_t*)sty) || - (sty->name->names == jl_emptysvec && sty->size > 0)) { + (jl_field_names(sty) == jl_emptysvec && sty->size > 0)) { *ret = mark_julia_type(ConstantInt::get(T_size, sty->size), false, jl_long_type, ctx); JL_GC_POP(); return true; @@ -2611,6 +2615,22 @@ static bool emit_builtin_call(jl_cgval_t *ret, jl_value_t *f, jl_value_t **args, } } } + + else if (0 && f==jl_builtin_struct && nargs >= 1) { + //jl_datatype_t *sty = (jl_datatype_t*)expr_type(args[2], ctx); + //rt1 = (jl_value_t*)sty; + jl_value_t *names = static_eval(args[1], ctx, true, true); + rt1 = names; + jl_value_t *typ = expr_type(expr, ctx); + if (names && jl_is_leaf_type(typ)) { // TODO repeated + *ret = emit_new_struct(expr_type(expr, ctx), nargs, args+1, ctx); + JL_GC_POP(); + return true; + } + /*jl_printf(JL_STDOUT, "args :\n"); + jl_static_show(JL_STDOUT, args[0]); + jl_static_show(JL_STDOUT, args[1]);*/ + } // TODO: other known builtins JL_GC_POP(); return false; @@ -5313,6 +5333,7 @@ static void init_julia_llvm_env(Module *m) builtin_func_map[jl_f__apply] = jlcall_func_to_llvm("jl_f__apply", &jl_f__apply, m); builtin_func_map[jl_f_throw] = jlcall_func_to_llvm("jl_f_throw", &jl_f_throw, m); builtin_func_map[jl_f_tuple] = jlcall_func_to_llvm("jl_f_tuple", &jl_f_tuple, m); + builtin_func_map[jl_f_struct] = jlcall_func_to_llvm("jl_f_struct", &jl_f_struct, m); builtin_func_map[jl_f_svec] = jlcall_func_to_llvm("jl_f_svec", &jl_f_svec, m); builtin_func_map[jl_f_applicable] = jlcall_func_to_llvm("jl_f_applicable", &jl_f_applicable, m); builtin_func_map[jl_f_invoke] = jlcall_func_to_llvm("jl_f_invoke", &jl_f_invoke, m); diff --git a/src/dump.c b/src/dump.c index c431365ab4c26..b506acbb39098 100644 --- a/src/dump.c +++ b/src/dump.c @@ -72,7 +72,7 @@ static const jl_fptr_t id_to_fptrs[] = { jl_f_getfield, jl_f_setfield, jl_f_fieldtype, jl_f_nfields, jl_f_arrayref, jl_f_arrayset, jl_f_arraysize, jl_f_apply_type, jl_f_applicable, jl_f_invoke, jl_unprotect_stack, jl_f_sizeof, jl_f__expr, - jl_f_intrinsic_call, + jl_f_intrinsic_call, jl_f_struct, NULL }; // pointers to non-AST-ish objects in a compressed tree @@ -568,6 +568,7 @@ static void jl_serialize_datatype(ios_t *s, jl_datatype_t *dt) jl_serialize_value(s, dt->parameters); jl_serialize_value(s, dt->name); + jl_serialize_value(s, dt->names); jl_serialize_value(s, dt->super); } @@ -1242,6 +1243,8 @@ static jl_value_t *jl_deserialize_datatype(ios_t *s, int pos, jl_value_t **loc) jl_gc_wb(dt, dt->parameters); 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->super = (jl_datatype_t*)jl_deserialize_value(s, (jl_value_t**)&dt->super); jl_gc_wb(dt, dt->super); if (datatype_list) { @@ -1249,7 +1252,7 @@ static jl_value_t *jl_deserialize_datatype(ios_t *s, int pos, jl_value_t **loc) dt->name == jl_pointer_type->name || dt->name == jl_type_type->name || dt->name == jl_simplevector_type->name || dt->name == jl_abstractarray_type->name || dt->name == jl_densearray_type->name || dt->name == jl_tuple_typename || - dt->name == jl_vararg_type->name) { + dt->name == jl_vararg_type->name || dt->name == jl_struct_typename) { // builtin types are not serialized, so their caches aren't // explicitly saved. so we reconstruct the caches of builtin // parametric types here. @@ -2472,7 +2475,7 @@ void jl_init_serializer(void) jl_typector_type->name, 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_globalref_type->name, jl_struct_type->name, jl_struct_type, jl_tparam0(jl_struct_type), jl_tparam1(jl_struct_type), jl_root_task, diff --git a/src/init.c b/src/init.c index fde4b550ae17c..6cd5f7eb5ee0f 100644 --- a/src/init.c +++ b/src/init.c @@ -841,6 +841,8 @@ void jl_get_builtin_hooks(void) jl_string_type = (jl_datatype_t*)core("String"); jl_weakref_type = (jl_datatype_t*)core("WeakRef"); jl_vecelement_typename = ((jl_datatype_t*)core("VecElement"))->name; + jl_struct_type = ((jl_datatype_t*)core("Struct")); + jl_struct_typename = jl_struct_type->name; } JL_DLLEXPORT void jl_get_system_hooks(void) @@ -868,6 +870,7 @@ void jl_get_builtins(void) jl_builtin_arrayset = core("arrayset"); jl_builtin_arraysize = core("arraysize"); jl_builtin_apply_type = core("apply_type"); jl_builtin_applicable = core("applicable"); jl_builtin_invoke = core("invoke"); jl_builtin__expr = core("_expr"); + jl_builtin_struct = core("struct"); } #ifdef __cplusplus diff --git a/src/jltypes.c b/src/jltypes.c index 530aa4988d8cd..f1dc1fc787b0d 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -2113,6 +2113,7 @@ static jl_value_t *inst_datatype(jl_datatype_t *dt, jl_svec_t *p, jl_value_t **i jl_typename_t *tn = dt->name; jl_value_t *tc = tn->primary; int istuple = (tn == jl_tuple_typename); + int isstruct = (tn == jl_struct_typename); // check type cache if (cacheable) { jl_value_t *lkup = (jl_value_t*)lookup_type(tn, iparams, ntp); @@ -2160,8 +2161,18 @@ static jl_value_t *inst_datatype(jl_datatype_t *dt, jl_svec_t *p, jl_value_t **i jl_svecset(p, i, iparams[i]); } + size_t nf = dt->nfields; + if (istuple) + nf = ntp; + else if (isstruct && jl_svec_len(p) == 2) { + if (!jl_is_tuple(jl_svecref(p,0)) || + !jl_is_tuple_type(jl_svecref(p,1))) + isstruct = 0; + else + nf = jl_datatype_nfields(jl_svecref(p, 1)); + } // create and initialize new type - ndt = jl_new_uninitialized_datatype(istuple ? ntp : dt->nfields, 2); // TODO + ndt = jl_new_uninitialized_datatype(nf, 2); // TODO // associate these parameters with the new type on // the stack, in case one of its field types references it. top.tt = (jl_datatype_t*)ndt; @@ -2172,7 +2183,21 @@ static jl_value_t *inst_datatype(jl_datatype_t *dt, jl_svec_t *p, jl_value_t **i ndt->super = jl_any_type; ndt->parameters = p; jl_gc_wb(ndt, ndt->parameters); - ndt->types = istuple ? p : jl_emptysvec; // to be filled in below + ndt->types = jl_emptysvec; // to be filled in below + if (istuple) { + ndt->types = p; + } else if (isstruct && jl_svec_len(p) == 2) { + jl_value_t *names_tup = jl_svecref(p, 0); + jl_value_t *values_tt = jl_svecref(p, 1); + if (jl_is_tuple_type(values_tt) && jl_is_tuple(names_tup)) { + jl_svec_t *names = jl_alloc_svec_uninit(jl_nfields(names_tup)); + memcpy(jl_svec_data(names), (jl_value_t**)names_tup, jl_nfields(names_tup)*sizeof(jl_value_t*)); + 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; @@ -2187,14 +2212,14 @@ 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 || isstruct) ndt->super = jl_any_type; else ndt->super = (jl_datatype_t*)inst_type_w_((jl_value_t*)dt->super, env,n,stack, 1); jl_gc_wb(ndt, ndt->super); ftypes = dt->types; if (ftypes != NULL) { - if (!istuple) { + if (!istuple && !isstruct) { // recursively instantiate the types of the fields ndt->types = inst_all(ftypes, env, n, stack, 1); jl_gc_wb(ndt, ndt->types); @@ -2220,8 +2245,8 @@ static jl_value_t *inst_datatype(jl_datatype_t *dt, jl_svec_t *p, jl_value_t **i if (tn == jl_array_typename) ndt->pointerfree = 0; } - if (istuple) - ndt->ninitialized = ntp; + if (istuple || isstruct) + ndt->ninitialized = nf; else ndt->ninitialized = dt->ninitialized; @@ -3249,7 +3274,7 @@ extern void jl_init_int32_int64_cache(void); void jl_init_types(void) { // create base objects - jl_datatype_type = jl_new_uninitialized_datatype(11, 1); + jl_datatype_type = jl_new_uninitialized_datatype(12, 1); jl_set_typeof(jl_datatype_type, jl_datatype_type); jl_typename_type = jl_new_uninitialized_datatype(8, 1); jl_sym_type = jl_new_uninitialized_datatype(0, 1); @@ -3272,7 +3297,7 @@ void jl_init_types(void) jl_datatype_type->name->primary = (jl_value_t*)jl_datatype_type; jl_datatype_type->super = jl_type_type; jl_datatype_type->parameters = jl_emptysvec; - jl_datatype_type->name->names = jl_svec(11, jl_symbol("name"), + jl_datatype_type->name->names = jl_svec(12, jl_symbol("name"), jl_symbol("super"), jl_symbol("parameters"), jl_symbol("types"), @@ -3282,12 +3307,13 @@ void jl_init_types(void) jl_symbol("mutable"), jl_symbol("pointerfree"), jl_symbol("ninitialized"), - jl_symbol("depth")); - jl_datatype_type->types = jl_svec(11, jl_typename_type, jl_type_type, + jl_symbol("depth"), + jl_symbol("names")); + jl_datatype_type->types = jl_svec(12, jl_typename_type, jl_type_type, jl_simplevector_type, jl_simplevector_type, jl_any_type, jl_any_type, // size - 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; @@ -3696,6 +3722,17 @@ void jl_init_types(void) jl_ANY_flag = (jl_value_t*)tvar("ANY"); + // Struct{names,T} + tv = jl_svec2(tvar("names"), tvar("T")); + jl_struct_type = jl_new_datatype(jl_symbol("Struct"), jl_any_type, tv, + jl_emptysvec, jl_svec(1, jl_wrap_vararg((jl_value_t*)jl_any_type, (jl_value_t*)NULL)), + 0, 0, 0); + jl_struct_type->nfields = 1; + jl_struct_type->hastypevars = 1; + jl_struct_type->haswildcard = 1; + jl_struct_type->isleaftype = 0; + + jl_struct_typename = jl_struct_type->name; // complete builtin type metadata jl_value_t *pointer_void = jl_apply_type((jl_value_t*)jl_pointer_type, jl_svec1(jl_void_type)); diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 4d0721876c8d3..288db5453cd4d 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -437,6 +437,58 @@ ,@lno ,@stmts) isstaged) + ,(let ((restkw-expr `(call (core structdiff) + ,kw (call (call (core apply_type) + (core KwKeys) + (call (core tuple) + ,@(map (lambda (x) `(quote ,x)) + (simple-sort keynames)))))))) + (method-def-expr- + name + (filter ;; remove sparams that don't occur, to avoid printing the warning twice + (lambda (s) (let ((name (if (symbol? s) s (cadr s)))) + (expr-contains-eq name (cons 'list argl)))) + positional-sparams) + `((|::| + ;; if there are optional positional args, we need to be able to reference the function name + ,(if (any kwarg? pargl) (gensy) UNUSED) + (call (core kwftype) ,ftype)) (:: ,kw (core Struct)) ,@pargl ,@vararg) + `(block + ,(foldl (lambda (kvf rest) + (let* ((k (car kvf)) + (k-sym (decl-var k)) + (rval `(call (core getfield) ,kw (quote ,k-sym))) + (rval (if (and (decl? k) + (not (any (lambda (s) + (expr-contains-eq s (caddr k))) + keyword-sparam-names))) + `(call (core typeassert) + ,rval + ,(caddr k)) + rval))) + `(block + (if (call (core isdefined) ,kw (quote ,k-sym)) + (= ,k-sym ,rval) + (= ,k-sym ,(cadr kvf))) + ,rest))) + (if (null? restkw) + (let ((rkw (make-ssavalue))) + `(block + (= ,rkw ,restkw-expr) + (if (comparison (call (core nfields) ,rkw) === 0) + (block) + (call (top kwerr) + (call (core fieldname) (call (core typeof) ,rkw) 1))))) + '()) + (reverse (map list vars vals flags))) + (return (call ,mangled + ,@keynames + ,@(if (null? restkw) '() + (list restkw-expr)) + ,@(map arg-name pargl) + ,@(if (null? vararg) '() + (list `(... ,(arg-name (car vararg)))))))) + #f)) ;; call with unsorted keyword args. this sorts and re-dispatches. ,(method-def-expr- name @@ -1334,20 +1386,40 @@ (error "more than one semicolon in argument list")) (receive (keys restkeys) (separate kwarg? kw) - (let ((keyargs (apply append - (map (lambda (a) - (if (not (symbol? (cadr a))) - (error (string "keyword argument is not a symbol: \"" - (deparse (cadr a)) "\""))) - (if (vararg? (caddr a)) - (error "splicing with \"...\" cannot be used for a keyword argument value")) - `((quote ,(cadr a)) ,(caddr a))) - keys)))) - (if (null? restkeys) - `(call (call (core kwfunc) ,f) (cell1d ,@keyargs) ,f ,@pa) - (let ((container (make-ssavalue))) + (let* ((keyargs (simple-sort + (map (lambda (a) + (if (not (symbol? (cadr a))) + (error (string "keyword argument is not a symbol: \"" + (deparse (cadr a)) "\""))) + (if (vararg? (caddr a)) + (error "splicing with \"...\" cannot be used for a keyword argument value")) + `((quote ,(cadr a)) ,(caddr a))) + keys))) + (struct `(call (core struct) + (call (core tuple) ,@(map car keyargs)) + ,@(map cadr keyargs))) + (struct (foldl (lambda (struct rest) + (if (vararg? struct) + `(call (core structmerge) + ,rest ,(cadr struct)) + `(call (core structadd) + ,rest ,struct))) + struct + restkeys))) + (if (null? keys) + (let ((struct-val (make-ssavalue))) + `(block + (= ,struct-val ,struct) + (if (comparison (call (core nfields) ,struct-val) === 0) + (call ,f ,@pa) + (call (call (core kwfunc) ,f) + ,struct-val ,f ,@pa)))) + `(call (call (core kwfunc) ,f) + ,struct ,f ,@pa))))) +; `(call (call (core kwfunc) ,f) (cell1d ,@keyargs) ,f ,@pa) + #;(let ((container (make-ssavalue))) `(block - (= ,container (cell1d ,@keyargs)) + (= ,container (cell1d ,@(apply append keyargs))) ,@(map (lambda (rk) (let* ((k (make-ssavalue)) (v (make-ssavalue)) @@ -1371,7 +1443,8 @@ ,@stmts (if (call (top isempty) ,container) (call ,f ,@pa) - (call (call (core kwfunc) ,f) ,container ,f ,@pa))))))))))) +(call (call (core kwfunc) ,f) ,container ,f ,@pa))))))) +;)))) ;; convert e.g. A'*B to Ac_mul_B(A,B) (define (expand-transposed-op e ops) diff --git a/src/julia.h b/src/julia.h index f7e5ec96e8dfe..cc51168d9b9f1 100644 --- a/src/julia.h +++ b/src/julia.h @@ -331,10 +331,11 @@ typedef struct _jl_datatype_t { int32_t ninitialized; // memoized properties int32_t depth; + jl_svec_t *names; + // hidden fields: int8_t hastypevars; // bound int8_t haswildcard; // unbound int8_t isleaftype; - // hidden fields: uint32_t nfields; uint32_t alignment : 29; // strictest alignment over all fields uint32_t haspadding : 1; // has internal undefined bytes @@ -478,6 +479,8 @@ extern JL_DLLEXPORT jl_datatype_t *jl_array_type; extern JL_DLLEXPORT jl_typename_t *jl_array_typename; extern JL_DLLEXPORT jl_datatype_t *jl_weakref_type; extern JL_DLLEXPORT jl_datatype_t *jl_string_type; +extern JL_DLLEXPORT jl_datatype_t *jl_struct_type; +extern JL_DLLEXPORT jl_typename_t *jl_struct_typename; extern JL_DLLEXPORT jl_datatype_t *jl_errorexception_type; extern JL_DLLEXPORT jl_datatype_t *jl_argumenterror_type; extern JL_DLLEXPORT jl_datatype_t *jl_loaderror_type; @@ -764,7 +767,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_datatype_size(t) (((jl_datatype_t*)t)->size) #define jl_datatype_nfields(t) (((jl_datatype_t*)(t))->nfields) diff --git a/test/keywordargs.jl b/test/keywordargs.jl index ce2c979f051af..aafd7aa63db1a 100644 --- a/test/keywordargs.jl +++ b/test/keywordargs.jl @@ -175,7 +175,7 @@ function test4974(;kwargs...) end end -@test test4974(a=1) == (2, [(:a, 1)]) +@test test4974(a=1) == (2, Struct(a=1)) # issue #7704, computed keywords @test kwf1(1; (:tens, 2)) == 21 From 96529e3b8ee1ea0603277bdd4d53abf84d7b4c45 Mon Sep 17 00:00:00 2001 From: Oscar Blumberg Date: Tue, 31 May 2016 08:20:34 -0400 Subject: [PATCH 2/4] cleanup --- base/inference.jl | 6 +-- src/julia-syntax.scm | 106 ------------------------------------------- 2 files changed, 1 insertion(+), 111 deletions(-) diff --git a/base/inference.jl b/base/inference.jl index 65293bdbe6a8d..17ae3e9dc76de 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -2375,11 +2375,7 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference if isa(f,IntrinsicFunction) || ft ⊑ IntrinsicFunction return NF end - #=println("F ==============") - println(f) - println(istopfunction(topmod,f,:struct)) - println(e.typ) - println("================")=# + if (is(f, Core.struct) && e.typ <: Core.Struct && isleaftype(e.typ)) new_e = Expr(:new, e.typ, argexprs[3:end]...) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 288db5453cd4d..96c6add38c347 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -489,83 +489,6 @@ ,@(if (null? vararg) '() (list `(... ,(arg-name (car vararg)))))))) #f)) - ;; call with unsorted keyword args. this sorts and re-dispatches. - ,(method-def-expr- - name - (filter ;; remove sparams that don't occur, to avoid printing the warning twice - (lambda (s) (let ((name (if (symbol? s) s (cadr s)))) - (expr-contains-eq name (cons 'list argl)))) - positional-sparams) - `((|::| - ;; if there are optional positional args, we need to be able to reference the function name - ,(if (any kwarg? pargl) (gensy) UNUSED) - (call (core kwftype) ,ftype)) (:: ,kw (core Array)) ,@pargl ,@vararg) - `(block - ;; initialize keyword args to their defaults, or set a flag telling - ;; whether this keyword needs to be set. - ,@(map (lambda (name dflt flag) - (if (const-default? dflt) - `(= ,name ,dflt) - `(= ,flag true))) - keynames vals flags) - ,@(if (null? restkw) '() - `((= ,rkw (cell1d)))) - ;; for i = 1:(length(kw)>>1) - (for (= ,i (: 1 (call (top >>) (call (top length) ,kw) 1))) - (block - ;; ii = i*2 - 1 - (= ,ii (call (top -) (call (top *) ,i 2) 1)) - (= ,elt (call (core arrayref) ,kw ,ii)) - ,(foldl (lambda (kvf else) - (let* ((k (car kvf)) - (rval0 `(call (core arrayref) ,kw - (call (top +) ,ii 1))) - ;; note: if the "declared" type of a KW arg - ;; includes something from keyword-sparam-names, - ;; then don't assert it here, since those static - ;; parameters don't have values yet. - ;; instead, the type will be picked up when the - ;; underlying method is called. - (rval (if (and (decl? k) - (not (any (lambda (s) - (expr-contains-eq s (caddr k))) - keyword-sparam-names))) - `(call (core typeassert) - ,rval0 - ,(caddr k)) - rval0))) - ;; if kw[ii] == 'k; k = kw[ii+1]::Type; end - `(if (comparison ,elt === (quote ,(decl-var k))) - (block - (= ,(decl-var k) ,rval) - ,@(if (not (const-default? (cadr kvf))) - `((= ,(caddr kvf) false)) - '())) - ,else))) - (if (null? restkw) - ;; if no rest kw, give error for unrecognized - `(call (top kwerr) ,elt) - ;; otherwise add to rest keywords - `(ccall 'jl_cell_1d_push Void (tuple Any Any) - ,rkw (tuple ,elt - (call (core arrayref) ,kw - (call (top +) ,ii 1))))) - (map list vars vals flags)))) - ;; set keywords that weren't present to their default values - ,@(apply append - (map (lambda (name dflt flag) - (if (const-default? dflt) - '() - `((if ,flag (= ,name ,dflt))))) - keynames vals flags)) - ;; finally, call the core function - (return (call ,mangled - ,@keynames - ,@(if (null? restkw) '() (list rkw)) - ,@(map arg-name pargl) - ,@(if (null? vararg) '() - (list `(... ,(arg-name (car vararg)))))))) - #f) ;; return primary function ,(if (or (not (symbol? name)) (is-call-name? name)) '(null) name))))) @@ -1416,35 +1339,6 @@ ,struct-val ,f ,@pa)))) `(call (call (core kwfunc) ,f) ,struct ,f ,@pa))))) -; `(call (call (core kwfunc) ,f) (cell1d ,@keyargs) ,f ,@pa) - #;(let ((container (make-ssavalue))) - `(block - (= ,container (cell1d ,@(apply append keyargs))) - ,@(map (lambda (rk) - (let* ((k (make-ssavalue)) - (v (make-ssavalue)) - (push-expr `(ccall 'jl_cell_1d_push2 Void - (tuple Any Any Any) - ,container - (|::| ,k (core Symbol)) - ,v))) - (if (vararg? rk) - `(for (= (tuple ,k ,v) ,(cadr rk)) - ,push-expr) - `(block (= (tuple ,k ,v) ,rk) - ,push-expr)))) - restkeys) - ,(if (not (null? keys)) - `(call (call (core kwfunc) ,f) ,container ,f ,@pa) - (let* ((expr_stmts (remove-argument-side-effects `(call ,f ,@pa))) - (pa (cddr (car expr_stmts))) - (stmts (cdr expr_stmts))) - `(block - ,@stmts - (if (call (top isempty) ,container) - (call ,f ,@pa) -(call (call (core kwfunc) ,f) ,container ,f ,@pa))))))) -;)))) ;; convert e.g. A'*B to Ac_mul_B(A,B) (define (expand-transposed-op e ops) From 2c0d2f8fa74e888d999dbe760f9ad501d3209057 Mon Sep 17 00:00:00 2001 From: Oscar Blumberg Date: Tue, 31 May 2016 08:40:07 -0400 Subject: [PATCH 3/4] infer constant nfields --- src/julia-syntax.scm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 96c6add38c347..681fd367845e4 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -475,7 +475,7 @@ (let ((rkw (make-ssavalue))) `(block (= ,rkw ,restkw-expr) - (if (comparison (call (core nfields) ,rkw) === 0) + (if (comparison (call (core nfields) (call (core typeof) ,rkw)) === 0) (block) (call (top kwerr) (call (core fieldname) (call (core typeof) ,rkw) 1))))) From fbe11c41f01200ec161bc0d84516b4bdb2165f40 Mon Sep 17 00:00:00 2001 From: Oscar Blumberg Date: Tue, 31 May 2016 17:02:12 -0400 Subject: [PATCH 4/4] fix method show --- base/methodshow.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/methodshow.jl b/base/methodshow.jl index 2b6409adc6585..651c9603be438 100644 --- a/base/methodshow.jl +++ b/base/methodshow.jl @@ -74,7 +74,7 @@ function arg_decl_parts(m::Method) end function kwarg_decl(sig::ANY, kwtype::DataType) - sig = Tuple{kwtype, Array, sig.parameters...} + sig = Tuple{kwtype, Struct, sig.parameters...} kwli = ccall(:jl_methtable_lookup, Any, (Any, Any), kwtype.name.mt, sig) if kwli !== nothing kwli = kwli::Method