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 4e04a272141a..5848f7b851f3 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/escape.d b/compiler/src/dmd/escape.d index e1b2ef791a7f..6b11c5b6dd2e 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, 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 0e6bc114709d..d16a2de6bdfd 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,19 @@ 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(); + } + } + //for error messages if the argument in [] is not convertible to size_t const originalNewtype = exp.newtype; @@ -5555,7 +5568,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); @@ -15244,6 +15257,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; @@ -15427,6 +15442,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 b36f54f6fbb8..ebccb5ab3e0f 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..bb6ffe3c9592 100644 --- a/compiler/src/dmd/inline.d +++ b/compiler/src/dmd/inline.d @@ -736,6 +736,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); 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 e8324eb07106..2cd46b88dbbd 100644 --- a/compiler/src/dmd/parse.d +++ b/compiler/src/dmd/parse.d @@ -9516,7 +9516,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; @@ -9552,7 +9562,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; } @@ -9576,7 +9586,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);