From 7deecacd6d5feecc6b39b6803aa3f2171353f171 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Sat, 2 Feb 2019 14:53:11 -0500 Subject: [PATCH 1/2] implement splatting in `new` calls adds `splatnew` form; like `new` but accepts a tuple --- NEWS.md | 11 +++-- base/compiler/abstractinterpretation.jl | 3 ++ base/compiler/ssair/inlining.jl | 25 ++++++++++ base/compiler/ssair/ir.jl | 2 +- base/compiler/tfuncs.jl | 20 ++++---- base/compiler/validation.jl | 5 +- base/essentials.jl | 7 +++ base/show.jl | 4 +- doc/src/devdocs/ast.md | 5 ++ src/ast.c | 2 + src/codegen.cpp | 21 +++++++++ src/datatype.c | 62 ++++++++++++++++++++----- src/interpreter.c | 10 ++++ src/julia-syntax.scm | 26 ++++++++--- src/julia.h | 4 +- src/julia_internal.h | 1 + test/core.jl | 30 ++++++++++++ 17 files changed, 198 insertions(+), 40 deletions(-) diff --git a/NEWS.md b/NEWS.md index 78f68636312b2..5c130547c7b78 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,10 +4,8 @@ Julia v1.2 Release Notes New language features --------------------- -* The `extrema` function now accepts a function argument in the same manner as `minimum` and - `maximum` ([#30323]). -* `hasmethod` can now check for matching keyword argument names ([#30712]). -* `startswith` and `endswith` now accept a `Regex` for the second argument ([#29790]). + * Argument splatting (`x...`) can now be used in calls to the `new` pseudo-function in + constructors ([#30577]). Multi-threading changes ----------------------- @@ -35,6 +33,11 @@ New library functions Standard library changes ------------------------ + * The `extrema` function now accepts a function argument in the same manner as `minimum` and + `maximum` ([#30323]). + * `hasmethod` can now check for matching keyword argument names ([#30712]). + * `startswith` and `endswith` now accept a `Regex` for the second argument ([#29790]). + #### LinearAlgebra * Added keyword arguments `rtol`, `atol` to `pinv` and `nullspace` ([#29998]). diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 2131a11e40ef3..68282d77be98b 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -907,6 +907,9 @@ function abstract_eval(@nospecialize(e), vtypes::VarTable, sv::InferenceState) t = Const(ccall(:jl_new_structv, Any, (Any, Ptr{Cvoid}, UInt32), t, args, length(args))) end end + elseif e.head === :splatnew + t = instanceof_tfunc(abstract_eval(e.args[1], vtypes, sv))[1] + # TODO: improve elseif e.head === :& abstract_eval(e.args[1], vtypes, sv) t = Any diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index b82756e567e73..edb63af068c8f 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -782,6 +782,31 @@ function assemble_inline_todo!(ir::IRCode, linetable::Vector{LineInfoNode}, sv:: todo = Any[] for idx in 1:length(ir.stmts) stmt = ir.stmts[idx] + + if isexpr(stmt, :splatnew) + ty = ir.types[idx] + nf = nfields_tfunc(ty) + if nf isa Const + eargs = stmt.args + tup = eargs[2] + tt = argextype(tup, ir, sv.sptypes) + tnf = nfields_tfunc(tt) + if tnf isa Const && tnf.val <= nf.val + n = tnf.val + new_argexprs = Any[eargs[1]] + for j = 1:n + atype = getfield_tfunc(tt, Const(j)) + new_call = Expr(:call, Core.getfield, tup, j) + new_argexpr = insert_node!(ir, idx, atype, new_call) + push!(new_argexprs, new_argexpr) + end + stmt.head = :new + stmt.args = new_argexprs + end + end + continue + end + isexpr(stmt, :call) || continue eargs = stmt.args isempty(eargs) && continue diff --git a/base/compiler/ssair/ir.jl b/base/compiler/ssair/ir.jl index 35bee5e43dd65..04e65a52c00f5 100644 --- a/base/compiler/ssair/ir.jl +++ b/base/compiler/ssair/ir.jl @@ -325,7 +325,7 @@ function getindex(x::UseRef) end function is_relevant_expr(e::Expr) - return e.head in (:call, :invoke, :new, :(=), :(&), + return e.head in (:call, :invoke, :new, :splatnew, :(=), :(&), :gc_preserve_begin, :gc_preserve_end, :foreigncall, :isdefined, :copyast, :undefcheck, :throw_undef_if_not, diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index b9629fa1a8845..c657ffc3a610b 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -329,17 +329,17 @@ function sizeof_tfunc(@nospecialize(x),) return Int end add_tfunc(Core.sizeof, 1, 1, sizeof_tfunc, 0) -add_tfunc(nfields, 1, 1, - function (@nospecialize(x),) - isa(x, Const) && return Const(nfields(x.val)) - isa(x, Conditional) && return Const(0) - if isa(x, DataType) && !x.abstract && !(x.name === Tuple.name && isvatuple(x)) - if !(x.name === _NAMEDTUPLE_NAME && !isconcretetype(x)) - return Const(length(x.types)) - end +function nfields_tfunc(@nospecialize(x)) + isa(x, Const) && return Const(nfields(x.val)) + isa(x, Conditional) && return Const(0) + if isa(x, DataType) && !x.abstract && !(x.name === Tuple.name && isvatuple(x)) + if !(x.name === _NAMEDTUPLE_NAME && !isconcretetype(x)) + return Const(length(x.types)) end - return Int - end, 0) + end + return Int +end +add_tfunc(nfields, 1, 1, nfields_tfunc, 0) add_tfunc(Core._expr, 1, INT_INF, (@nospecialize args...)->Expr, 100) function typevar_tfunc(@nospecialize(n), @nospecialize(lb_arg), @nospecialize(ub_arg)) lb = Union{} diff --git a/base/compiler/validation.jl b/base/compiler/validation.jl index 567d06861ff1f..1fcbf624e4256 100644 --- a/base/compiler/validation.jl +++ b/base/compiler/validation.jl @@ -11,6 +11,7 @@ const VALID_EXPR_HEADS = IdDict{Any,Any}( :method => 1:4, :const => 1:1, :new => 1:typemax(Int), + :splatnew => 2:2, :return => 1:1, :unreachable => 0:0, :the_exception => 0:0, @@ -142,7 +143,7 @@ function validate_code!(errors::Vector{>:InvalidCodeError}, c::CodeInfo, is_top_ head === :inbounds || head === :foreigncall || head === :cfunction || head === :const || head === :enter || head === :leave || head == :pop_exception || head === :method || head === :global || head === :static_parameter || - head === :new || head === :thunk || head === :simdloop || + head === :new || head === :splatnew || head === :thunk || head === :simdloop || head === :throw_undef_if_not || head === :unreachable validate_val!(x) else @@ -224,7 +225,7 @@ end function is_valid_rvalue(@nospecialize(x)) is_valid_argument(x) && return true - if isa(x, Expr) && x.head in (:new, :the_exception, :isdefined, :call, :invoke, :foreigncall, :cfunction, :gc_preserve_begin, :copyast) + if isa(x, Expr) && x.head in (:new, :splatnew, :the_exception, :isdefined, :call, :invoke, :foreigncall, :cfunction, :gc_preserve_begin, :copyast) return true end return false diff --git a/base/essentials.jl b/base/essentials.jl index 572a306f56965..122eb385f19de 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -280,6 +280,13 @@ convert(::Type{Tuple{Vararg{V}}}, x::Tuple{Vararg{V}}) where {V} = x convert(T::Type{Tuple{Vararg{V}}}, x::Tuple) where {V} = (convert(tuple_type_head(T), x[1]), convert(T, tail(x))...) +# used for splatting in `new` +convert_prefix(::Type{Tuple{}}, x::Tuple) = x +convert_prefix(::Type{<:AtLeast1}, x::Tuple{}) = x +convert_prefix(::Type{T}, x::T) where {T<:AtLeast1} = x +convert_prefix(::Type{T}, x::AtLeast1) where {T<:AtLeast1} = + (convert(tuple_type_head(T), x[1]), convert_prefix(tuple_type_tail(T), tail(x))...) + # TODO: the following definitions are equivalent (behaviorally) to the above method # I think they may be faster / more efficient for inference, # if we could enable them, but are they? diff --git a/base/show.jl b/base/show.jl index 410a48137e016..67d7a27fecd92 100644 --- a/base/show.jl +++ b/base/show.jl @@ -1223,8 +1223,8 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) end # new expr - elseif head === :new - show_enclosed_list(io, "%new(", args, ", ", ")", indent) + elseif head === :new || head === :splatnew + show_enclosed_list(io, "%$head(", args, ", ", ")", indent) # other call-like expressions ("A[1,2]", "T{X,Y}", "f.(X,Y)") elseif haskey(expr_calls, head) && nargs >= 1 # :ref/:curly/:calldecl/:(.) diff --git a/doc/src/devdocs/ast.md b/doc/src/devdocs/ast.md index b89b4db12b724..2b27c94ba1183 100644 --- a/doc/src/devdocs/ast.md +++ b/doc/src/devdocs/ast.md @@ -359,6 +359,11 @@ These symbols appear in the `head` field of [`Expr`](@ref)s in lowered form. to this, and the type is always inserted by the compiler. This is very much an internal-only feature, and does no checking. Evaluating arbitrary `new` expressions can easily segfault. + * `splatnew` + + Similar to `new`, except field values are passed as a single tuple. Works similarly to + `Base.splat(new)` if `new` were a first-class function, hence the name. + * `return` Returns its argument as the value of the enclosing function. diff --git a/src/ast.c b/src/ast.c index e8fa38df24048..0aeedac33b1b0 100644 --- a/src/ast.c +++ b/src/ast.c @@ -41,6 +41,7 @@ jl_sym_t *enter_sym; jl_sym_t *leave_sym; jl_sym_t *pop_exception_sym; jl_sym_t *exc_sym; jl_sym_t *error_sym; jl_sym_t *new_sym; jl_sym_t *using_sym; +jl_sym_t *splatnew_sym; jl_sym_t *const_sym; jl_sym_t *thunk_sym; jl_sym_t *abstracttype_sym; jl_sym_t *primtype_sym; jl_sym_t *structtype_sym; jl_sym_t *foreigncall_sym; @@ -325,6 +326,7 @@ void jl_init_frontend(void) leave_sym = jl_symbol("leave"); pop_exception_sym = jl_symbol("pop_exception"); new_sym = jl_symbol("new"); + splatnew_sym = jl_symbol("splatnew"); const_sym = jl_symbol("const"); global_sym = jl_symbol("global"); thunk_sym = jl_symbol("thunk"); diff --git a/src/codegen.cpp b/src/codegen.cpp index 83bfcbe9071bc..63bfe69062474 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -264,6 +264,7 @@ static Function *jltls_states_func; // important functions static Function *jlnew_func; +static Function *jlsplatnew_func; static Function *jlthrow_func; static Function *jlerror_func; static Function *jltypeerror_func; @@ -4069,6 +4070,15 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaval) // it to the inferred type. return mark_julia_type(ctx, val, true, (jl_value_t*)jl_any_type); } + else if (head == splatnew_sym) { + jl_cgval_t argv[2]; + argv[0] = emit_expr(ctx, args[0]); + argv[1] = emit_expr(ctx, args[1]); + Value *typ = boxed(ctx, argv[0]); + Value *tup = boxed(ctx, argv[1]); + Value *val = ctx.builder.CreateCall(prepare_call(jlsplatnew_func), { typ, tup }); + return mark_julia_type(ctx, val, true, (jl_value_t*)jl_any_type); + } else if (head == exc_sym) { return mark_julia_type(ctx, ctx.builder.CreateCall(prepare_call(jl_current_exception_func)), @@ -6981,6 +6991,17 @@ static void init_julia_llvm_env(Module *m) jlnew_func->addFnAttr(Thunk); add_named_global(jlnew_func, &jl_new_structv); + std::vector args_2rptrs_(0); + args_2rptrs_.push_back(T_prjlvalue); + args_2rptrs_.push_back(T_prjlvalue); + jlsplatnew_func = + Function::Create(FunctionType::get(T_prjlvalue, args_2rptrs_, false), + Function::ExternalLinkage, + "jl_new_structt", m); + add_return_attr(jlsplatnew_func, Attribute::NonNull); + jlsplatnew_func->addFnAttr(Thunk); + add_named_global(jlsplatnew_func, &jl_new_structt); + std::vector args2(0); args2.push_back(T_pint8); #ifndef _OS_WINDOWS_ diff --git a/src/datatype.c b/src/datatype.c index 8a9a883889905..1d21881e3917d 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -797,8 +797,24 @@ JL_DLLEXPORT jl_value_t *jl_new_struct(jl_datatype_t *type, ...) return jv; } -JL_DLLEXPORT jl_value_t *jl_new_structv(jl_datatype_t *type, jl_value_t **args, - uint32_t na) +static void init_struct_tail(jl_datatype_t *type, jl_value_t *jv, size_t na) +{ + size_t nf = jl_datatype_nfields(type); + for(size_t i=na; i < nf; i++) { + if (jl_field_isptr(type, i)) { + *(jl_value_t**)((char*)jl_data_ptr(jv)+jl_field_offset(type,i)) = NULL; + } + else { + jl_value_t *ft = jl_field_type(type, i); + if (jl_is_uniontype(ft)) { + uint8_t *psel = &((uint8_t *)jv)[jl_field_offset(type, i) + jl_field_size(type, i) - 1]; + *psel = 0; + } + } + } +} + +JL_DLLEXPORT jl_value_t *jl_new_structv(jl_datatype_t *type, jl_value_t **args, uint32_t na) { jl_ptls_t ptls = jl_get_ptls_states(); if (type->instance != NULL) { @@ -811,7 +827,6 @@ JL_DLLEXPORT jl_value_t *jl_new_structv(jl_datatype_t *type, jl_value_t **args, } if (type->layout == NULL) jl_type_error("new", (jl_value_t*)jl_datatype_type, (jl_value_t*)type); - size_t nf = jl_datatype_nfields(type); jl_value_t *jv = jl_gc_alloc(ptls, jl_datatype_size(type), type); JL_GC_PUSH1(&jv); for (size_t i = 0; i < na; i++) { @@ -820,18 +835,41 @@ JL_DLLEXPORT jl_value_t *jl_new_structv(jl_datatype_t *type, jl_value_t **args, jl_type_error("new", ft, args[i]); jl_set_nth_field(jv, i, args[i]); } - for(size_t i=na; i < nf; i++) { - if (jl_field_isptr(type, i)) { - *(jl_value_t**)((char*)jl_data_ptr(jv)+jl_field_offset(type,i)) = NULL; - } - else { + init_struct_tail(type, jv, na); + JL_GC_POP(); + return jv; +} + +JL_DLLEXPORT jl_value_t *jl_new_structt(jl_datatype_t *type, jl_value_t *tup) +{ + jl_ptls_t ptls = jl_get_ptls_states(); + if (!jl_is_tuple(tup)) + jl_type_error("new", (jl_value_t*)jl_tuple_type, tup); + size_t na = jl_nfields(tup); + size_t nf = jl_datatype_nfields(type); + if (na > nf) + jl_too_many_args("new", nf); + if (type->instance != NULL) { + for (size_t i = 0; i < na; i++) { jl_value_t *ft = jl_field_type(type, i); - if (jl_is_uniontype(ft)) { - uint8_t *psel = &((uint8_t *)jv)[jl_field_offset(type, i) + jl_field_size(type, i) - 1]; - *psel = 0; - } + jl_value_t *fi = jl_get_nth_field(tup, i); + if (!jl_isa(fi, ft)) + jl_type_error("new", ft, fi); } + return type->instance; + } + if (type->layout == NULL) + jl_type_error("new", (jl_value_t*)jl_datatype_type, (jl_value_t*)type); + jl_value_t *jv = jl_gc_alloc(ptls, jl_datatype_size(type), type); + JL_GC_PUSH1(&jv); + for (size_t i = 0; i < na; i++) { + jl_value_t *ft = jl_field_type(type, i); + jl_value_t *fi = jl_get_nth_field(tup, i); + if (!jl_isa(fi, ft)) + jl_type_error("new", ft, fi); + jl_set_nth_field(jv, i, fi); } + init_struct_tail(type, jv, na); JL_GC_POP(); return jv; } diff --git a/src/interpreter.c b/src/interpreter.c index 2e08c0b94744a..6d54bb86307d5 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -467,6 +467,16 @@ SECT_INTERP static jl_value_t *eval_value(jl_value_t *e, interpreter_state *s) JL_GC_POP(); return v; } + else if (head == splatnew_sym) { + jl_value_t **argv; + JL_GC_PUSHARGS(argv, 2); + argv[0] = eval_value(args[0], s); + argv[1] = eval_value(args[1], s); + assert(jl_is_structtype(argv[0])); + jl_value_t *v = jl_new_structt((jl_datatype_t*)argv[0], argv[1]); + JL_GC_POP(); + return v; + } else if (head == static_parameter_sym) { ssize_t n = jl_unbox_long(args[0]); assert(n > 0); diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 46fd6b290d919..764f04793f880 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -696,8 +696,6 @@ (call (curly ,name ,@params) ,@field-names))))) (define (new-call Tname type-params params args field-names field-types) - (if (any vararg? args) - (error "... is not supported inside \"new\"")) (if (any kwarg? args) (error "\"new\" does not accept keyword arguments")) (if (length> params (length type-params)) @@ -706,8 +704,22 @@ `(outerref ,Tname) `(curly (outerref ,Tname) ,@type-params)))) - (cond ((length> args (length field-names)) - `(call (top error) "new: too many arguments")) + (cond ((length> (filter (lambda (a) (not (vararg? a))) args) (length field-names)) + `(call (core throw) (call (top ArgumentError) + ,(string "new: too many arguments (expected " (length field-names) ")")))) + ((any vararg? args) + (if (every (lambda (ty) (equal? ty '(core Any))) + field-types) + `(splatnew ,Texpr (call (core tuple) ,@args)) + (let ((tn (make-ssavalue))) + `(block + (= ,tn ,Texpr) + (splatnew ,tn (call (top convert_prefix) + (curly (core Tuple) + ,@(map (lambda (fld) + `(call (core fieldtype) ,tn (quote ,fld))) + field-names)) + (call (core tuple) ,@args))))))) (else (if (equal? type-params params) `(new ,Texpr ,@(map (lambda (fty val) @@ -2995,7 +3007,7 @@ f(x) = yt(x) #f) ((eq? (car e) 'scope-block) (visit (cadr e))) - ((memq (car e) '(block call new _do_while)) + ((memq (car e) '(block call new splatnew _do_while)) (eager-any visit (cdr e))) ((eq? (car e) 'break-block) (visit (caddr e))) @@ -3405,7 +3417,7 @@ f(x) = yt(x) (or (ssavalue? lhs) (valid-ir-argument? e) (and (symbol? lhs) (pair? e) - (memq (car e) '(new the_exception isdefined call invoke foreigncall cfunction gc_preserve_begin copyast))))) + (memq (car e) '(new splatnew the_exception isdefined call invoke foreigncall cfunction gc_preserve_begin copyast))))) (define (valid-ir-return? e) ;; returning lambda directly is needed for @generated @@ -3595,7 +3607,7 @@ f(x) = yt(x) ((and (pair? e1) (eq? (car e1) 'globalref)) (emit e1) #f) ;; keep globals for undefined-var checking (else #f))) (case (car e) - ((call new foreigncall cfunction) + ((call new splatnew foreigncall cfunction) (let* ((args (cond ((eq? (car e) 'foreigncall) ;; NOTE: 2nd to 5th arguments of ccall must be left in place diff --git a/src/julia.h b/src/julia.h index 79f343cb75465..a9040eaee70e6 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1146,8 +1146,8 @@ jl_datatype_t *jl_new_abstracttype(jl_value_t *name, jl_module_t *module, // constructors JL_DLLEXPORT jl_value_t *jl_new_bits(jl_value_t *bt, void *data); JL_DLLEXPORT jl_value_t *jl_new_struct(jl_datatype_t *type, ...); -JL_DLLEXPORT jl_value_t *jl_new_structv(jl_datatype_t *type, jl_value_t **args, - uint32_t na); +JL_DLLEXPORT jl_value_t *jl_new_structv(jl_datatype_t *type, jl_value_t **args, uint32_t na); +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; diff --git a/src/julia_internal.h b/src/julia_internal.h index 60609018686e1..25b2287b2e60b 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -991,6 +991,7 @@ extern jl_sym_t *method_sym; extern jl_sym_t *core_sym; extern jl_sym_t *enter_sym; extern jl_sym_t *leave_sym; extern jl_sym_t *exc_sym; extern jl_sym_t *error_sym; extern jl_sym_t *new_sym; extern jl_sym_t *using_sym; +extern jl_sym_t *splatnew_sym; extern jl_sym_t *pop_exception_sym; extern jl_sym_t *const_sym; extern jl_sym_t *thunk_sym; extern jl_sym_t *abstracttype_sym; extern jl_sym_t *primtype_sym; diff --git a/test/core.jl b/test/core.jl index 33f1eb5233079..daa792873427e 100644 --- a/test/core.jl +++ b/test/core.jl @@ -6835,3 +6835,33 @@ f29828() = 2::String g29828() = 2::Any[String][1] @test_throws TypeError(:typeassert, String, 2) f29828() @test_throws TypeError(:typeassert, String, 2) g29828() + +# splatting in `new` +struct SplatNew{T} + x::Int8 + y::T + SplatNew{T}(args...) where {T} = new(0,args...,1) + SplatNew(args...) = new{Float32}(args...) + SplatNew{Any}(args...) = new(args...) + SplatNew{Tuple{Int16}}(args...) = new([2]..., args...) + SplatNew{Int8}() = new(1,2,3) +end +let x = SplatNew{Int16}() + @test x.x === Int8(0) + @test x.y === Int16(1) +end +@test_throws ArgumentError SplatNew{Int16}(1) +let x = SplatNew(3,2) + @test x.x === Int8(3) + @test x.y === 2.0f0 +end +@test_throws ArgumentError SplatNew(1,2,3) +let x = SplatNew{Any}(1) + @test x.x === Int8(1) + @test !isdefined(x, :y) +end +let x = SplatNew{Tuple{Int16}}((1,)) + @test x.x === Int8(2) + @test x.y === (Int16(1),) +end +@test_throws ArgumentError SplatNew{Int8}() From e456a72b033e3da8ade872a856ad4415a8924663 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Sat, 2 Feb 2019 14:52:32 -0500 Subject: [PATCH 2/2] define core NamedTuple constructor without generated functions --- base/boot.jl | 29 ++--------------------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/base/boot.jl b/base/boot.jl index 63c7f4b5b4821..a134c0bacf8c6 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -548,33 +548,8 @@ NamedTuple{names}(args::Tuple) where {names} = NamedTuple{names,typeof(args)}(ar using .Intrinsics: sle_int, add_int -macro generated() - return Expr(:generated) -end - -function NamedTuple{names,T}(args::T) where {names, T <: Tuple} - if @generated - N = nfields(names) - flds = Array{Any,1}(undef, N) - i = 1 - while sle_int(i, N) - arrayset(false, flds, :(getfield(args, $i)), i) - i = add_int(i, 1) - end - Expr(:new, :(NamedTuple{names,T}), flds...) - else - N = nfields(names) - NT = NamedTuple{names,T} - flds = Array{Any,1}(undef, N) - i = 1 - while sle_int(i, N) - arrayset(false, flds, getfield(args, i), i) - i = add_int(i, 1) - end - ccall(:jl_new_structv, Any, (Any, Ptr{Cvoid}, UInt32), NT, - ccall(:jl_array_ptr, Ptr{Cvoid}, (Any,), flds), toUInt32(N))::NT - end -end +eval(Core, :(NamedTuple{names,T}(args::T) where {names, T <: Tuple} = + $(Expr(:splatnew, :(NamedTuple{names,T}), :args)))) # constructors for built-in types