From efb517caa9c0fde9ffc032dff1c4d3237578622b Mon Sep 17 00:00:00 2001 From: Walter Bright Date: Sat, 9 Nov 2024 22:07:37 -0800 Subject: [PATCH] add Placement New --- 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 | 50 +++++++++- 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/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/fail_compilation/placenew.d | 80 ++++++++++++++++ compiler/test/runnable/placenew.d | 106 ++++++++++++++++++++++ 21 files changed, 360 insertions(+), 41 deletions(-) create mode 100644 compiler/test/fail_compilation/placenew.d create mode 100644 compiler/test/runnable/placenew.d diff --git a/compiler/src/dmd/astbase.d b/compiler/src/dmd/astbase.d index bdc489871c36..1d91dd290df8 100644 --- a/compiler/src/dmd/astbase.d +++ b/compiler/src/dmd/astbase.d @@ -4813,10 +4813,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; @@ -5025,10 +5027,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 2a1f26948fde..a9df017aad17 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; @@ -5067,7 +5074,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 173532af397c..470a4c681553 100644 --- a/compiler/src/dmd/dsymbolsem.d +++ b/compiler/src/dmd/dsymbolsem.d @@ -1304,9 +1304,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 d46e4661a087..294f74eb3c70 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 1aff7df4e7e1..fda8d65da882 100644 --- a/compiler/src/dmd/e2ir.d +++ b/compiler/src/dmd/e2ir.d @@ -1116,19 +1116,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); @@ -1255,8 +1263,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"); @@ -1266,7 +1279,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()) @@ -1298,10 +1311,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); @@ -1321,8 +1336,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 3e18051afc12..5c92f26a6593 100644 --- a/compiler/src/dmd/escape.d +++ b/compiler/src/dmd/escape.d @@ -1635,6 +1635,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 3c79b02caa02..a05ff9092732 100644 --- a/compiler/src/dmd/expression.d +++ b/compiler/src/dmd/expression.d @@ -2554,6 +2554,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 @@ -2566,23 +2567,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), @@ -2603,10 +2606,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; @@ -2614,7 +2619,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 c353a191a662..39e6994e44dc 100644 --- a/compiler/src/dmd/expression.h +++ b/compiler/src/dmd/expression.h @@ -509,6 +509,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[] @@ -518,7 +519,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); } @@ -532,6 +533,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 86a35cf05b3e..4232538225cb 100644 --- a/compiler/src/dmd/expressionsem.d +++ b/compiler/src/dmd/expressionsem.d @@ -3130,7 +3130,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: @@ -4902,6 +4902,23 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor return; } + 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(); + } + } + //for error messages if the argument in [] is not convertible to size_t const originalNewtype = exp.newtype; @@ -4988,6 +5005,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; @@ -4996,9 +5026,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) @@ -5506,6 +5541,11 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } 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) { @@ -5555,7 +5595,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); @@ -15112,6 +15152,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; @@ -15295,6 +15337,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 44f65921ea87..f0d82fe5403a 100644 --- a/compiler/src/dmd/frontend.h +++ b/compiler/src/dmd/frontend.h @@ -3117,6 +3117,7 @@ class NewAnonClassExp final : public Expression Expression* thisexp; ClassDeclaration* cd; Array* arguments; + Expression* placement; NewAnonClassExp* syntaxCopy() override; void accept(Visitor* v) override; }; @@ -3128,12 +3129,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 86131f2f11c9..363b2b407592 100644 --- a/compiler/src/dmd/hdrgen.d +++ b/compiler/src/dmd/hdrgen.d @@ -2423,6 +2423,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) { @@ -2440,6 +2447,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 619af342678c..399bcdf0132a 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 c6434d08d5b8..c0813d6f1c83 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/ob.d b/compiler/src/dmd/ob.d index ee4b6525edc8..34ccc739f0cc 100644 --- a/compiler/src/dmd/ob.d +++ b/compiler/src/dmd/ob.d @@ -1725,6 +1725,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) @@ -2466,6 +2468,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 3e145be5499f..6a7875ba0178 100644 --- a/compiler/src/dmd/parse.d +++ b/compiler/src/dmd/parse.d @@ -9529,7 +9529,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; @@ -9565,7 +9575,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; } @@ -9589,7 +9599,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 952460c18cbe..9693e7adbe93 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/fail_compilation/placenew.d b/compiler/test/fail_compilation/placenew.d new file mode 100644 index 000000000000..ade8932c52fb --- /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` +--- +*/ + +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; +}