From 58bcca24939bb5d9272c9d1d04089f03cfafbe5c Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Sat, 9 Nov 2024 22:07:37 -0800 Subject: [PATCH] add Placement New --- changelog/dmd.placementNew.dd | 20 ++++ compiler/src/dmd/astbase.d | 8 +- compiler/src/dmd/dinterpret.d | 9 +- compiler/src/dmd/dsymbolsem.d | 5 +- compiler/src/dmd/dtemplate.d | 2 + compiler/src/dmd/e2ir.d | 59 ++++++++---- compiler/src/dmd/escape.d | 3 + compiler/src/dmd/expression.d | 17 +++- compiler/src/dmd/expression.h | 4 +- compiler/src/dmd/expressionsem.d | 72 +++++++++++++-- compiler/src/dmd/frontend.h | 4 +- compiler/src/dmd/hdrgen.d | 14 +++ compiler/src/dmd/inline.d | 6 +- compiler/src/dmd/inlinecost.d | 2 +- compiler/src/dmd/nogc.d | 2 + compiler/src/dmd/ob.d | 5 + compiler/src/dmd/optimize.d | 1 + compiler/src/dmd/parse.d | 16 +++- compiler/src/dmd/visitor/postorder.d | 4 +- compiler/src/dmd/visitor/transitive.d | 4 + compiler/src/tests/cxxfrontend.cc | 2 +- compiler/test/compilable/placementnew.d | 4 + compiler/test/fail_compilation/placenew.d | 80 ++++++++++++++++ compiler/test/runnable/placenew.d | 106 ++++++++++++++++++++++ 24 files changed, 405 insertions(+), 44 deletions(-) create mode 100644 changelog/dmd.placementNew.dd create mode 100644 compiler/test/compilable/placementnew.d create mode 100644 compiler/test/fail_compilation/placenew.d create mode 100644 compiler/test/runnable/placenew.d diff --git a/changelog/dmd.placementNew.dd b/changelog/dmd.placementNew.dd new file mode 100644 index 000000000000..71dafac56a24 --- /dev/null +++ b/changelog/dmd.placementNew.dd @@ -0,0 +1,20 @@ +# Added Placement New Expression + +Placement new explicitly provides the storage for NewExpression to initialize +with the newly created value, rather than using the GC. + +--- +struct S +{ + float d; + int i; + char c; +} + +void main() +{ + S s; + S* p = new (s) S(); // place new object into s + assert(p.i == 0 && p.c == 0xFF); +} +--- diff --git a/compiler/src/dmd/astbase.d b/compiler/src/dmd/astbase.d index dc388b286321..1ab2878cba59 100644 --- a/compiler/src/dmd/astbase.d +++ b/compiler/src/dmd/astbase.d @@ -4814,10 +4814,12 @@ struct ASTBase Expression thisexp; // if !=null, 'this' for class being allocated ClassDeclaration cd; // class being instantiated Expressions* arguments; // Array of Expression's to call class constructor + Expression placement; // if != null, then PlacementExpression - extern (D) this(const ref Loc loc, Expression thisexp, ClassDeclaration cd, Expressions* arguments) + extern (D) this(const ref Loc loc, Expression placement, Expression thisexp, ClassDeclaration cd, Expressions* arguments) { super(loc, EXP.newAnonymousClass, __traits(classInstanceSize, NewAnonClassExp)); + this.placement = placement; this.thisexp = thisexp; this.cd = cd; this.arguments = arguments; @@ -5026,10 +5028,12 @@ struct ASTBase Type newtype; Expressions* arguments; // Array of Expression's Identifiers* names; // Array of names corresponding to expressions + Expression placement; // if != null, then PlacementExpression - extern (D) this(const ref Loc loc, Expression thisexp, Type newtype, Expressions* arguments, Identifiers* names = null) + extern (D) this(const ref Loc loc, Expression placement, Expression thisexp, Type newtype, Expressions* arguments, Identifiers* names = null) { super(loc, EXP.new_, __traits(classInstanceSize, NewExp)); + this.placement = placement; this.thisexp = thisexp; this.newtype = newtype; this.arguments = arguments; diff --git a/compiler/src/dmd/dinterpret.d b/compiler/src/dmd/dinterpret.d index f219e3f20279..7cef5c7877bc 100644 --- a/compiler/src/dmd/dinterpret.d +++ b/compiler/src/dmd/dinterpret.d @@ -2794,6 +2794,13 @@ public: printf("%s NewExp::interpret() %s\n", e.loc.toChars(), e.toChars()); } + if (e.placement) + { + error(e.placement.loc, "`new ( %s )` PlacementExpression cannot be evaluated at compile time", e.placement.toChars()); + result = CTFEExp.cantexp; + return; + } + Expression epre = interpret(pue, e.argprefix, istate, CTFEGoal.Nothing); if (exceptionOrCant(epre)) return; @@ -5040,7 +5047,7 @@ public: auto ce = e.e2.isCallExp(); assert(ce); - auto ne = new NewExp(e.loc, null, e.type, ce.arguments); + auto ne = new NewExp(e.loc, null, null, e.type, ce.arguments); ne.type = e.e1.type; result = interpret(ne, istate); diff --git a/compiler/src/dmd/dsymbolsem.d b/compiler/src/dmd/dsymbolsem.d index 0d87f6e26ecc..ab0c3160fc86 100644 --- a/compiler/src/dmd/dsymbolsem.d +++ b/compiler/src/dmd/dsymbolsem.d @@ -1312,9 +1312,12 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor ex = (cast(AssignExp)ex).e2; if (auto ne = ex.isNewExp()) { + if (ne.placement) + { + } /* See if initializer is a NewExp that can be allocated on the stack. */ - if (dsym.type.toBasetype().ty == Tclass) + else if (dsym.type.toBasetype().ty == Tclass) { /* Unsafe to allocate on stack if constructor is not `scope` because the `this` can leak. * https://issues.dlang.org/show_bug.cgi?id=23145 diff --git a/compiler/src/dmd/dtemplate.d b/compiler/src/dmd/dtemplate.d index 469b19c2b808..533adc20bcf2 100644 --- a/compiler/src/dmd/dtemplate.d +++ b/compiler/src/dmd/dtemplate.d @@ -3008,6 +3008,8 @@ private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparam override void visit(NewExp e) { //printf("NewExp.reliesOnTemplateParameters('%s')\n", e.toChars()); + if (e.placement) + e.placement.accept(this); if (e.thisexp) e.thisexp.accept(this); result = e.newtype.reliesOnTemplateParameters(tparams); diff --git a/compiler/src/dmd/e2ir.d b/compiler/src/dmd/e2ir.d index 98246c0c399f..e469d5aa6a65 100644 --- a/compiler/src/dmd/e2ir.d +++ b/compiler/src/dmd/e2ir.d @@ -1126,19 +1126,27 @@ elem* toElem(Expression e, ref IRState irs) elem *ezprefix = null; elem *ez = null; - if (ne.onstack) + if (ne.onstack || ne.placement) { - /* Create an instance of the class on the stack, - * and call it stmp. - * Set ex to be the &stmp. - */ - .type *tc = type_struct_class(tclass.sym.toChars(), - tclass.sym.alignsize, tclass.sym.structsize, - null, null, - false, false, true, false); - tc.Tcount--; - Symbol *stmp = symbol_genauto(tc); - ex = el_ptr(stmp); + if (ne.placement) + { + ex = toElem(ne.placement, irs); + ex = addressElem(ex, ne.newtype.toBasetype(), false); + } + else + { + /* Create an instance of the class on the stack, + * and call it stmp. + * Set ex to be the &stmp. + */ + .type *tc = type_struct_class(tclass.sym.toChars(), + tclass.sym.alignsize, tclass.sym.structsize, + null, null, + false, false, true, false); + tc.Tcount--; + Symbol *stmp = symbol_genauto(tc); + ex = el_ptr(stmp); + } Symbol *si = toInitializer(tclass.sym); elem *ei = el_var(si); @@ -1265,8 +1273,13 @@ elem* toElem(Expression e, ref IRState irs) elem *ezprefix = null; elem *ez = null; - // Call _d_newitemT() - if (auto lowering = ne.lowering) + if (ne.placement) + { + ex = toElem(ne.placement, irs); + ex = addressElem(ex, tclass, false); + } + else if (auto lowering = ne.lowering) + // Call _d_newitemT() ex = toElem(ne.lowering, irs); else assert(0, "This case should have been rewritten to `_d_newitemT` in the semantic phase"); @@ -1276,7 +1289,7 @@ elem* toElem(Expression e, ref IRState irs) elem *ev = el_same(ex); if (ne.argprefix) - ezprefix = toElem(ne.argprefix, irs); + ezprefix = toElem(ne.argprefix, irs); if (ne.member) { if (sd.isNested()) @@ -1308,10 +1321,12 @@ elem* toElem(Expression e, ref IRState irs) { StructLiteralExp sle = StructLiteralExp.create(ne.loc, sd, ne.arguments, t); ez = toElemStructLit(sle, irs, EXP.construct, ev.Vsym, false); + if (tybasic(ez.Ety) == TYstruct) + ez = el_una(OPaddr, TYnptr, ez); } //elem_print(ex); //elem_print(ey); - //elem_print(ez); + //printf("ez:\n"); elem_print(ez); e = el_combine(ex, ey); e = el_combine(e, ew); @@ -1331,8 +1346,16 @@ elem* toElem(Expression e, ref IRState irs) { elem *ezprefix = ne.argprefix ? toElem(ne.argprefix, irs) : null; - // call _d_newitemT() - e = toElem(ne.lowering, irs); + if (ne.placement) + { + e = toElem(ne.placement, irs); + e = addressElem(e, ne.newtype.toBasetype(), false); + } + else if (auto lowering = ne.lowering) + // Call _d_newitemT() + e = toElem(ne.lowering, irs); + else + assert(0, "This case should have been rewritten to `_d_newitemT` in the semantic phase"); if (ne.arguments && ne.arguments.length == 1) { diff --git a/compiler/src/dmd/escape.d b/compiler/src/dmd/escape.d index 947abf0c4bcb..93eafcaff63b 100644 --- a/compiler/src/dmd/escape.d +++ b/compiler/src/dmd/escape.d @@ -1637,6 +1637,9 @@ void escapeExp(Expression e, ref scope EscapeByResults er, int deref) void visitNew(NewExp e) { + if (e.placement) + escapeExp(e.placement, er, deref); + Type tb = e.newtype.toBasetype(); if (tb.isTypeStruct() && !e.member && e.arguments) { diff --git a/compiler/src/dmd/expression.d b/compiler/src/dmd/expression.d index 2a828d85473f..77e58cfeac39 100644 --- a/compiler/src/dmd/expression.d +++ b/compiler/src/dmd/expression.d @@ -2560,6 +2560,7 @@ extern (C++) final class NewExp : Expression Type newtype; Expressions* arguments; // Array of Expression's Identifiers* names; // Array of names corresponding to expressions + Expression placement; // if !=null, then PlacementExpression Expression argprefix; // expression to be evaluated just before arguments[] CtorDeclaration member; // constructor function @@ -2572,23 +2573,25 @@ extern (C++) final class NewExp : Expression /// The fields are still separate for backwards compatibility extern (D) ArgumentList argumentList() { return ArgumentList(arguments, names); } - extern (D) this(const ref Loc loc, Expression thisexp, Type newtype, Expressions* arguments, Identifiers* names = null) @safe + extern (D) this(const ref Loc loc, Expression placement, Expression thisexp, Type newtype, Expressions* arguments, Identifiers* names = null) @safe { super(loc, EXP.new_); + this.placement = placement; this.thisexp = thisexp; this.newtype = newtype; this.arguments = arguments; this.names = names; } - static NewExp create(const ref Loc loc, Expression thisexp, Type newtype, Expressions* arguments) @safe + static NewExp create(const ref Loc loc, Expression placement, Expression thisexp, Type newtype, Expressions* arguments) @safe { - return new NewExp(loc, thisexp, newtype, arguments); + return new NewExp(loc, placement, thisexp, newtype, arguments); } override NewExp syntaxCopy() { return new NewExp(loc, + placement ? placement.syntaxCopy() : null, thisexp ? thisexp.syntaxCopy() : null, newtype.syntaxCopy(), arraySyntaxCopy(arguments), @@ -2609,10 +2612,12 @@ extern (C++) final class NewAnonClassExp : Expression Expression thisexp; // if !=null, 'this' for class being allocated ClassDeclaration cd; // class being instantiated Expressions* arguments; // Array of Expression's to call class constructor + Expression placement; // if !=null, then PlacementExpression - extern (D) this(const ref Loc loc, Expression thisexp, ClassDeclaration cd, Expressions* arguments) @safe + extern (D) this(const ref Loc loc, Expression placement, Expression thisexp, ClassDeclaration cd, Expressions* arguments) @safe { super(loc, EXP.newAnonymousClass); + this.placement = placement; this.thisexp = thisexp; this.cd = cd; this.arguments = arguments; @@ -2620,7 +2625,9 @@ extern (C++) final class NewAnonClassExp : Expression override NewAnonClassExp syntaxCopy() { - return new NewAnonClassExp(loc, thisexp ? thisexp.syntaxCopy() : null, cd.syntaxCopy(null), arraySyntaxCopy(arguments)); + return new NewAnonClassExp(loc, placement ? placement.syntaxCopy : null, + thisexp ? thisexp.syntaxCopy() : null, + cd.syntaxCopy(null), arraySyntaxCopy(arguments)); } override void accept(Visitor v) diff --git a/compiler/src/dmd/expression.h b/compiler/src/dmd/expression.h index d62aea86d637..9f8a727a1360 100644 --- a/compiler/src/dmd/expression.h +++ b/compiler/src/dmd/expression.h @@ -510,6 +510,7 @@ class NewExp final : public Expression Type *newtype; Expressions *arguments; // Array of Expression's Identifiers *names; // Array of names corresponding to expressions + Expression *placement; // if !NULL, placement expression Expression *argprefix; // expression to be evaluated just before arguments[] @@ -519,7 +520,7 @@ class NewExp final : public Expression Expression *lowering; // lowered druntime hook: `_d_newclass` - static NewExp *create(const Loc &loc, Expression *thisexp, Type *newtype, Expressions *arguments); + static NewExp *create(const Loc &loc, Expression *placement, Expression *thisexp, Type *newtype, Expressions *arguments); NewExp *syntaxCopy() override; void accept(Visitor *v) override { v->visit(this); } @@ -533,6 +534,7 @@ class NewAnonClassExp final : public Expression Expression *thisexp; // if !NULL, 'this' for class being allocated ClassDeclaration *cd; // class being instantiated Expressions *arguments; // Array of Expression's to call class constructor + Expression *placement; // if !NULL, placement expression NewAnonClassExp *syntaxCopy() override; void accept(Visitor *v) override { v->visit(this); } diff --git a/compiler/src/dmd/expressionsem.d b/compiler/src/dmd/expressionsem.d index cd7548ea27cf..3cea0a9a6ee6 100644 --- a/compiler/src/dmd/expressionsem.d +++ b/compiler/src/dmd/expressionsem.d @@ -3169,7 +3169,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc, auto args = new Expressions(nargs - i); foreach (u; i .. nargs) (*args)[u - i] = (*arguments)[u]; - arg = new NewExp(loc, null, p.type, args); + arg = new NewExp(loc, null, null, p.type, args); break; } default: @@ -4888,6 +4888,28 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor printf("\tnewtype: %s\n", exp.newtype.toChars()); } + if (exp.placement) + { + exp.placement = exp.placement.expressionSemantic(sc); + auto p = exp.placement; + if (p.op == EXP.error) + return setError(); + if (!p.isLvalue()) + { + error(p.loc, "PlacementExpression `%s` is an rvalue, but must be an lvalue", p.toChars()); + return setError(); + } + if (sc.setUnsafe(false, p.loc, "`@safe` function `%s` cannot use placement `new`", sc.func)) + { + return setError(); + } + if (!exp.placement.type.isNaked()) + { + error(p.loc, "PlacementExpression `%s` of type `%s` be unshared and mutable", p.toChars(), toChars(p.type)); + return setError(); + } + } + //for error messages if the argument in [] is not convertible to size_t const originalNewtype = exp.newtype; @@ -4974,6 +4996,19 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return setError(); } + uinteger_t placementSize; + if (exp.placement) + { + placementSize = size(exp.placement.type, exp.placement.loc); + auto objectSize = size(tb, exp.placement.loc); + //printf("placementSize: %lld objectSize: %lld\n", placementSize, objectSize); + if (!tb.isTypeClass && placementSize < objectSize) + { + error(exp.placement.loc, "new placement size %llu must be >= object size %llu", placementSize, objectSize); + return setError(); + } + } + const size_t nargs = exp.arguments ? exp.arguments.length : 0; Expression newprefix = null; @@ -4982,9 +5017,14 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor auto cd = tc.sym; if (cd.errors) return setError(); - cd.size(exp.loc); + auto objectSize = cd.size(exp.loc); if (cd.sizeok != Sizeok.done) return setError(); + if (exp.placement && placementSize < objectSize) + { + error(exp.placement.loc, "new placement size %llu must be >= class object size %llu", placementSize, objectSize); + return setError(); + } if (!cd.ctor) cd.ctor = cd.searchCtor(); if (cd.noDefaultCtor && !nargs && !cd.defaultCtor) @@ -5194,6 +5234,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } else if (sc.needsCodegen() && // interpreter doesn't need this lowered + !exp.placement && !exp.onstack && !exp.type.isScopeClass()) // these won't use the GC { /* replace `new T(arguments)` with `core.lifetime._d_newclassT!T(arguments)` @@ -5307,10 +5348,16 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } exp.type = exp.type.pointerTo(); - tryLowerToNewItem(exp); + if (!exp.placement) + tryLowerToNewItem(exp); } else if (tb.ty == Tarray) { + if (exp.placement) + { + error(exp.placement.loc, "placement new cannot be used with dynamic arrays"); + return setError(); + } if (!nargs) { // https://issues.dlang.org/show_bug.cgi?id=20422 @@ -5381,7 +5428,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor goto LskipNewArrayLowering; } - if (nargs == 1) + if (exp.placement) // no need to lower + { + } + else if (nargs == 1) { auto hook = global.params.tracegc ? Id._d_newarrayTTrace : Id._d_newarrayT; if (!verifyHookExist(exp.loc, *sc, hook, "new array")) @@ -5481,10 +5531,16 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } exp.type = exp.type.pointerTo(); - tryLowerToNewItem(exp); + if (!exp.placement) + tryLowerToNewItem(exp); } else if (tb.ty == Taarray) { + if (exp.placement) + { + error(exp.placement.loc, "placement new cannot be used with associative arrays"); + return setError(); + } // e.g. `new Alias(args)` if (nargs) { @@ -5534,7 +5590,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor sds.members.push(e.cd); } - Expression n = new NewExp(e.loc, e.thisexp, e.cd.type, e.arguments); + Expression n = new NewExp(e.loc, e.placement, e.thisexp, e.cd.type, e.arguments); Expression c = new CommaExp(e.loc, d, n); result = c.expressionSemantic(sc); @@ -14935,6 +14991,8 @@ bool checkSharedAccess(Expression e, Scope* sc, bool returnRef = false) bool visitNew(NewExp e) { + if (e.placement) + check(e.placement, false); if (e.thisexp) check(e.thisexp, false); return false; @@ -15118,6 +15176,8 @@ Expression resolveLoc(Expression exp, const ref Loc loc, Scope* sc) Expression visitNew(NewExp exp) { + if (exp.placement) + exp.placement = exp.placement.resolveLoc(loc, sc); if (exp.thisexp) exp.thisexp = exp.thisexp.resolveLoc(loc, sc); if (exp.argprefix) diff --git a/compiler/src/dmd/frontend.h b/compiler/src/dmd/frontend.h index 6c955da1e831..71e842a21da7 100644 --- a/compiler/src/dmd/frontend.h +++ b/compiler/src/dmd/frontend.h @@ -3216,6 +3216,7 @@ class NewAnonClassExp final : public Expression Expression* thisexp; ClassDeclaration* cd; Array* arguments; + Expression* placement; NewAnonClassExp* syntaxCopy() override; void accept(Visitor* v) override; }; @@ -3227,12 +3228,13 @@ class NewExp final : public Expression Type* newtype; Array* arguments; Array* names; + Expression* placement; Expression* argprefix; CtorDeclaration* member; bool onstack; bool thrownew; Expression* lowering; - static NewExp* create(const Loc& loc, Expression* thisexp, Type* newtype, Array* arguments); + static NewExp* create(const Loc& loc, Expression* placement, Expression* thisexp, Type* newtype, Array* arguments); NewExp* syntaxCopy() override; void accept(Visitor* v) override; }; diff --git a/compiler/src/dmd/hdrgen.d b/compiler/src/dmd/hdrgen.d index d391bdb581ac..2b8d2629e24e 100644 --- a/compiler/src/dmd/hdrgen.d +++ b/compiler/src/dmd/hdrgen.d @@ -2404,6 +2404,13 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt buf.writeByte('.'); } buf.writestring("new "); + if (e.placement) + { + buf.writeByte('('); + expToBuffer(e.placement, PREC.assign, buf, hgs); + buf.writeByte(')'); + buf.writeByte(' '); + } typeToBuffer(e.newtype, null, buf, hgs); if (e.arguments && e.arguments.length) { @@ -2421,6 +2428,13 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt buf.writeByte('.'); } buf.writestring("new"); + if (e.placement) + { + buf.writeByte(' '); + buf.writeByte('('); + expToBuffer(e.placement, PREC.assign, buf, hgs); + buf.writeByte(')'); + } buf.writestring(" class "); if (e.arguments && e.arguments.length) { diff --git a/compiler/src/dmd/inline.d b/compiler/src/dmd/inline.d index 8fab820ac263..405f011e7821 100644 --- a/compiler/src/dmd/inline.d +++ b/compiler/src/dmd/inline.d @@ -34,6 +34,7 @@ import dmd.errors; import dmd.func; import dmd.funcsem; import dmd.globals; +import dmd.hdrgen; import dmd.id; import dmd.identifier; import dmd.init; @@ -736,6 +737,7 @@ public: goto LhasLowering; } + ne.placement = doInlineAs!Expression(e.placement, ids); ne.thisexp = doInlineAs!Expression(e.thisexp, ids); ne.argprefix = doInlineAs!Expression(e.argprefix, ids); ne.arguments = arrayExpressionDoInline(e.arguments); @@ -1001,7 +1003,7 @@ public: { static if (LOG) { - printf("ExpStatement.inlineScan(%s)\n", s.toChars()); + printf("ExpStatement.inlineScan(%s)\n", toChars(s)); } if (!s.exp) return; @@ -2202,7 +2204,7 @@ private void expandInline(Loc callLoc, FuncDeclaration fd, FuncDeclaration paren static if (EXPANDINLINE_LOG) printf("\n[%s] %s expandInline sresult =\n%s\n", - callLoc.toChars(), fd.toPrettyChars(), sresult.toChars()); + callLoc.toChars(), fd.toPrettyChars(), toChars(sresult)); } else { diff --git a/compiler/src/dmd/inlinecost.d b/compiler/src/dmd/inlinecost.d index 675881eb3ae3..744adef0dbc9 100644 --- a/compiler/src/dmd/inlinecost.d +++ b/compiler/src/dmd/inlinecost.d @@ -430,7 +430,7 @@ public: { //printf("NewExp.inlineCost3() %s\n", e.toChars()); AggregateDeclaration ad = isAggregate(e.newtype); - if (ad && ad.isNested()) + if (ad && ad.isNested() || e.placement) cost = COST_MAX; else cost++; diff --git a/compiler/src/dmd/nogc.d b/compiler/src/dmd/nogc.d index ff1e173af476..8d86c99217f5 100644 --- a/compiler/src/dmd/nogc.d +++ b/compiler/src/dmd/nogc.d @@ -137,6 +137,8 @@ public: override void visit(NewExp e) { + if (e.placement) + return; // placement new doesn't use the GC if (e.member && !e.member.isNogc() && f.setGC(e.loc, null)) { // @nogc-ness is already checked in NewExp::semantic diff --git a/compiler/src/dmd/ob.d b/compiler/src/dmd/ob.d index 2019e82cd7bd..a39698112cb1 100644 --- a/compiler/src/dmd/ob.d +++ b/compiler/src/dmd/ob.d @@ -1723,6 +1723,8 @@ void genKill(ref ObState obstate, ObNode* ob) override void visit(NewExp e) { + if (e.placement) + e.placement.accept(this); if (e.arguments) { foreach (ex; *e.arguments) @@ -2464,6 +2466,9 @@ void checkObErrors(ref ObState obstate) override void visit(NewExp e) { + if (e.placement) + e.placement.accept(this); + if (e.arguments) { foreach (ex; *e.arguments) diff --git a/compiler/src/dmd/optimize.d b/compiler/src/dmd/optimize.d index 282575db62cc..2edfd6bdf64d 100644 --- a/compiler/src/dmd/optimize.d +++ b/compiler/src/dmd/optimize.d @@ -751,6 +751,7 @@ Expression optimize(Expression e, int result, bool keepLvalue = false) void visitNew(NewExp e) { + expOptimize(e.placement, WANTvalue); expOptimize(e.thisexp, WANTvalue); // Optimize parameters if (e.arguments) diff --git a/compiler/src/dmd/parse.d b/compiler/src/dmd/parse.d index 415d57824d14..dfed263c2862 100644 --- a/compiler/src/dmd/parse.d +++ b/compiler/src/dmd/parse.d @@ -9549,7 +9549,17 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer { const loc = token.loc; - nextToken(); + nextToken(); // skip past `new` + + // parse PlacementExpression if any + AST.Expression placement; + if (token.value == TOK.leftParenthesis) + { + nextToken(); + placement = parseAssignExp(); + check(TOK.rightParenthesis); + } + AST.Expressions* arguments = null; AST.Identifiers* names = null; @@ -9585,7 +9595,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer } auto cd = new AST.ClassDeclaration(loc, id, baseclasses, members, false); - auto e = new AST.NewAnonClassExp(loc, thisexp, cd, arguments); + auto e = new AST.NewAnonClassExp(loc, placement, thisexp, cd, arguments); return e; } @@ -9609,7 +9619,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer parseNamedArguments(arguments, names); } - auto e = new AST.NewExp(loc, thisexp, t, arguments, names); + auto e = new AST.NewExp(loc, placement, thisexp, t, arguments, names); return e; } diff --git a/compiler/src/dmd/visitor/postorder.d b/compiler/src/dmd/visitor/postorder.d index af12f1e9e9ed..39c38a284f44 100644 --- a/compiler/src/dmd/visitor/postorder.d +++ b/compiler/src/dmd/visitor/postorder.d @@ -82,13 +82,13 @@ public: override void visit(NewExp e) { //printf("NewExp::apply(): %s\n", toChars()); - doCond(e.thisexp) || doCond(e.arguments.peekSlice()) || applyTo(e); + doCond(e.placement) || doCond(e.thisexp) || doCond(e.arguments.peekSlice()) || applyTo(e); } override void visit(NewAnonClassExp e) { //printf("NewAnonClassExp::apply(): %s\n", toChars()); - doCond(e.thisexp) || doCond(e.arguments.peekSlice()) || applyTo(e); + doCond(e.placement) || doCond(e.thisexp) || doCond(e.arguments.peekSlice()) || applyTo(e); } override void visit(TypeidExp e) diff --git a/compiler/src/dmd/visitor/transitive.d b/compiler/src/dmd/visitor/transitive.d index c3ce13e2d40a..89c23320b9da 100644 --- a/compiler/src/dmd/visitor/transitive.d +++ b/compiler/src/dmd/visitor/transitive.d @@ -994,6 +994,8 @@ package(dmd.visitor) mixin template ParseVisitMethods(AST) override void visit(AST.NewExp e) { //printf("Visiting NewExp\n"); + if (e.placement) + e.placement.accept(this); if (e.thisexp) e.thisexp.accept(this); visitType(e.newtype); @@ -1003,6 +1005,8 @@ package(dmd.visitor) mixin template ParseVisitMethods(AST) override void visit(AST.NewAnonClassExp e) { //printf("Visiting NewAnonClassExp\n"); + if (e.placement) + e.placement.accept(this); if (e.thisexp) e.thisexp.accept(this); visitArgs(e.arguments.peekSlice()); diff --git a/compiler/src/tests/cxxfrontend.cc b/compiler/src/tests/cxxfrontend.cc index 51669b51a8e1..3450d02b2a5d 100644 --- a/compiler/src/tests/cxxfrontend.cc +++ b/compiler/src/tests/cxxfrontend.cc @@ -1351,7 +1351,7 @@ class MiniGlueVisitor : public Visitor (void)d->csym; (void)d->vtblSymbol()->csym; (void)d->sinit; - NewExp *ne = NewExp::create(d->loc, NULL, d->type, NULL); + NewExp *ne = NewExp::create(d->loc, NULL, NULL, d->type, NULL); ne->type = d->type; Expression *e = dmd::ctfeInterpret(ne); assert(e->op == EXP::classReference); diff --git a/compiler/test/compilable/placementnew.d b/compiler/test/compilable/placementnew.d new file mode 100644 index 000000000000..e7181bbee7ec --- /dev/null +++ b/compiler/test/compilable/placementnew.d @@ -0,0 +1,4 @@ +void f(int* p) @nogc +{ + new(*p) int; +} diff --git a/compiler/test/fail_compilation/placenew.d b/compiler/test/fail_compilation/placenew.d new file mode 100644 index 000000000000..033d078e1910 --- /dev/null +++ b/compiler/test/fail_compilation/placenew.d @@ -0,0 +1,80 @@ +/* TEST_OUTPUT: +--- +fail_compilation/placenew.d(23): Error: PlacementExpression `3` is an rvalue, but must be an lvalue +fail_compilation/placenew.d(28): Error: undefined identifier `x` +fail_compilation/placenew.d(36): Error: `new ( i )` PlacementExpression cannot be evaluated at compile time +fail_compilation/placenew.d(39): called from here: `xxx()` +fail_compilation/placenew.d(39): while evaluating: `static assert(xxx() == 1)` +fail_compilation/placenew.d(48): Error: new placement size 24 must be >= object size 40 +fail_compilation/placenew.d(54): Error: placement new cannot be used with associative arrays +fail_compilation/placenew.d(67): Error: new placement size 4 must be >= class object size $?:32=16|64=24$ +fail_compilation/placenew.d(77): Error: `@safe` function `test7` cannot use placement `new` is not allowed in a `@safe` function +--- +*/ + +void test0() +{ + int i; + int* pi = new (i) int; +} + +void test1() +{ + int* pi = new (3) int; +} + +void test2() +{ + int* px = new (x) int; +} + +void test3() +{ + int xxx() + { + int i; + int* pi = new (i) int(1); + return 1; + } + static assert(xxx() == 1); +} + +struct S { int[6] a; } +struct T { int[10] a; } + +void test4() +{ + S s; + new (s) T(); +} + +void test5() +{ + T p; + auto aa = new(p) int[int*]; +} + +/*************************************************/ + +class C6 +{ + int i, j; +} + +int test6() +{ + int k; + C6 c = new(k) C6; + return c.j; +} + +/*************************************************/ + +@safe +void test7() +{ + int i; + int* p = new(i) int; +} + +/*************************************************/ diff --git a/compiler/test/runnable/placenew.d b/compiler/test/runnable/placenew.d new file mode 100644 index 000000000000..d90d10a5e744 --- /dev/null +++ b/compiler/test/runnable/placenew.d @@ -0,0 +1,106 @@ +import core.stdc.stdio; +import core.stdc.stdlib; + +/*************************************************/ + +struct S +{ + float d; + int i; + char c; +} + +void test1() +{ + S s; + S* p = new (s) S(); + assert(p.i == 0 && p.c == 0xFF); +} + +void test2() +{ + S s; + S* p = new (s) S(i:3); + assert(p.i == 3 && p.c == 0xFF); +} + +/*************************************************/ + +struct S3 +{ + int i; + this(int i) { this.i = i + 3; } +} + +void test3() +{ + S3 s; + s.i = 20; + S3* p = new (s) S3(4); + assert(p.i == 7); +} + + +/*************************************************/ + +void test4() +{ + int i = 3; + int* p = new(i) int; + *p = 4; + assert(i == 4); + + p = new(i) int(7); + assert(i == 7); +} + +/*************************************************/ + +class C5 +{ + int i, j = 4; +} + +int test5() +{ + int[10] k; + C5 c = new(k) C5; + //printf("c.j: %d\n", c.j); + assert(c.j == 4); + assert(cast(void*)c == cast(void*)k.ptr); + return c.j; +} + +/*************************************************/ + +struct S6 +{ + int i = 1, j = 4, k = 9; +} + +ref void[T.sizeof] mallocate(T)() +{ + return *(cast(void[T.sizeof]*) malloc(T.sizeof)); +} + +void test6() +{ + S6* ps = new(mallocate!S6()) S6; + assert(ps.i == 1); + assert(ps.j == 4); + assert(ps.k == 9); +} + +/*************************************************/ + +int main() +{ + test1(); + test2(); + test3(); + test4(); + test5(); + test6(); + + return 0; +}