Merge pull request #17057 from WalterBright/placementNew

add Placement New
This commit is contained in:
Manu Evans 2025-03-03 19:50:22 +10:00 committed by GitHub
commit 37469bfae8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 407 additions and 46 deletions

View file

@ -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);
}
---

View file

@ -4808,10 +4808,12 @@ struct ASTBase
Expression thisexp; // if !=null, 'this' for class being allocated Expression thisexp; // if !=null, 'this' for class being allocated
ClassDeclaration cd; // class being instantiated ClassDeclaration cd; // class being instantiated
Expressions* arguments; // Array of Expression's to call class constructor Expressions* arguments; // Array of Expression's to call class constructor
Expression placement; // if != null, then PlacementExpression
extern (D) this(Loc loc, Expression thisexp, ClassDeclaration cd, Expressions* arguments) extern (D) this(Loc loc, Expression placement, Expression thisexp, ClassDeclaration cd, Expressions* arguments)
{ {
super(loc, EXP.newAnonymousClass, __traits(classInstanceSize, NewAnonClassExp)); super(loc, EXP.newAnonymousClass, __traits(classInstanceSize, NewAnonClassExp));
this.placement = placement;
this.thisexp = thisexp; this.thisexp = thisexp;
this.cd = cd; this.cd = cd;
this.arguments = arguments; this.arguments = arguments;
@ -5020,10 +5022,12 @@ struct ASTBase
Type newtype; Type newtype;
Expressions* arguments; // Array of Expression's Expressions* arguments; // Array of Expression's
Identifiers* names; // Array of names corresponding to expressions Identifiers* names; // Array of names corresponding to expressions
Expression placement; // if != null, then PlacementExpression
extern (D) this(Loc loc, Expression thisexp, Type newtype, Expressions* arguments, Identifiers* names = null) extern (D) this(Loc loc, Expression placement, Expression thisexp, Type newtype, Expressions* arguments, Identifiers* names = null)
{ {
super(loc, EXP.new_, __traits(classInstanceSize, NewExp)); super(loc, EXP.new_, __traits(classInstanceSize, NewExp));
this.placement = placement;
this.thisexp = thisexp; this.thisexp = thisexp;
this.newtype = newtype; this.newtype = newtype;
this.arguments = arguments; this.arguments = arguments;

View file

@ -2796,6 +2796,13 @@ public:
printf("%s NewExp::interpret() %s\n", e.loc.toChars(), e.toChars()); 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); Expression epre = interpret(pue, e.argprefix, istate, CTFEGoal.Nothing);
if (exceptionOrCant(epre)) if (exceptionOrCant(epre))
return; return;
@ -5042,7 +5049,7 @@ public:
auto ce = e.e2.isCallExp(); auto ce = e.e2.isCallExp();
assert(ce); 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; ne.type = e.e1.type;
result = interpret(ne, istate); result = interpret(ne, istate);

View file

@ -1312,9 +1312,12 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
ex = (cast(AssignExp)ex).e2; ex = (cast(AssignExp)ex).e2;
if (auto ne = ex.isNewExp()) if (auto ne = ex.isNewExp())
{ {
if (ne.placement)
{
}
/* See if initializer is a NewExp that can be allocated on the stack. /* 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. /* Unsafe to allocate on stack if constructor is not `scope` because the `this` can leak.
* https://issues.dlang.org/show_bug.cgi?id=23145 * https://issues.dlang.org/show_bug.cgi?id=23145

View file

@ -2998,6 +2998,8 @@ private bool reliesOnTemplateParameters(Expression e, TemplateParameter[] tparam
override void visit(NewExp e) override void visit(NewExp e)
{ {
//printf("NewExp.reliesOnTemplateParameters('%s')\n", e.toChars()); //printf("NewExp.reliesOnTemplateParameters('%s')\n", e.toChars());
if (e.placement)
e.placement.accept(this);
if (e.thisexp) if (e.thisexp)
e.thisexp.accept(this); e.thisexp.accept(this);
result = e.newtype.reliesOnTemplateParameters(tparams); result = e.newtype.reliesOnTemplateParameters(tparams);

View file

@ -1126,19 +1126,27 @@ elem* toElem(Expression e, ref IRState irs)
elem* ezprefix = null; elem* ezprefix = null;
elem* ez = null; elem* ez = null;
if (ne.onstack) if (ne.onstack || ne.placement)
{ {
/* Create an instance of the class on the stack, if (ne.placement)
* and call it stmp. {
* Set ex to be the &stmp. ex = toElem(ne.placement, irs);
*/ ex = addressElem(ex, ne.newtype.toBasetype(), false);
.type* tc = type_struct_class(tclass.sym.toChars(), }
tclass.sym.alignsize, tclass.sym.structsize, else
null, null, {
false, false, true, false); /* Create an instance of the class on the stack,
tc.Tcount--; * and call it stmp.
Symbol* stmp = symbol_genauto(tc); * Set ex to be the &stmp.
ex = el_ptr(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); Symbol* si = toInitializer(tclass.sym);
elem* ei = el_var(si); elem* ei = el_var(si);
@ -1265,8 +1273,13 @@ elem* toElem(Expression e, ref IRState irs)
elem* ezprefix = null; elem* ezprefix = null;
elem* ez = null; elem* ez = null;
// Call _d_newitemT() if (ne.placement)
if (auto lowering = ne.lowering) {
ex = toElem(ne.placement, irs);
ex = addressElem(ex, tclass, false);
}
else if (auto lowering = ne.lowering)
// Call _d_newitemT()
ex = toElem(ne.lowering, irs); ex = toElem(ne.lowering, irs);
else else
assert(0, "This case should have been rewritten to `_d_newitemT` in the semantic phase"); 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); elem* ev = el_same(ex);
if (ne.argprefix) if (ne.argprefix)
ezprefix = toElem(ne.argprefix, irs); ezprefix = toElem(ne.argprefix, irs);
if (ne.member) if (ne.member)
{ {
if (sd.isNested()) if (sd.isNested())
@ -1308,10 +1321,12 @@ elem* toElem(Expression e, ref IRState irs)
{ {
StructLiteralExp sle = StructLiteralExp.create(ne.loc, sd, ne.arguments, t); StructLiteralExp sle = StructLiteralExp.create(ne.loc, sd, ne.arguments, t);
ez = toElemStructLit(sle, irs, EXP.construct, ev.Vsym, false); 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(ex);
//elem_print(ey); //elem_print(ey);
//elem_print(ez); //printf("ez:\n"); elem_print(ez);
e = el_combine(ex, ey); e = el_combine(ex, ey);
e = el_combine(e, ew); 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; elem* ezprefix = ne.argprefix ? toElem(ne.argprefix, irs) : null;
// call _d_newitemT() if (ne.placement)
e = toElem(ne.lowering, irs); {
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) if (ne.arguments && ne.arguments.length == 1)
{ {

View file

@ -1637,6 +1637,9 @@ void escapeExp(Expression e, ref scope EscapeByResults er, int deref)
void visitNew(NewExp e) void visitNew(NewExp e)
{ {
if (e.placement)
escapeExp(e.placement, er, deref);
Type tb = e.newtype.toBasetype(); Type tb = e.newtype.toBasetype();
if (tb.isTypeStruct() && !e.member && e.arguments) if (tb.isTypeStruct() && !e.member && e.arguments)
{ {

View file

@ -304,8 +304,8 @@ extern (C++) abstract class Expression : ASTNode
static struct BitFields static struct BitFields
{ {
bool parens; // if this is a parenthesized expression bool parens; // if this is a parenthesized expression
bool rvalue; // true if this is considered to be an rvalue, even if it is an lvalue bool rvalue; // true if this is considered to be an rvalue, even if it is an lvalue
} }
import dmd.common.bitfields; import dmd.common.bitfields;
mixin(generateBitFields!(BitFields, ubyte)); mixin(generateBitFields!(BitFields, ubyte));
@ -2446,6 +2446,7 @@ extern (C++) final class NewExp : Expression
Type newtype; Type newtype;
Expressions* arguments; // Array of Expression's Expressions* arguments; // Array of Expression's
Identifiers* names; // Array of names corresponding to expressions Identifiers* names; // Array of names corresponding to expressions
Expression placement; // if !=null, then PlacementExpression
Expression argprefix; // expression to be evaluated just before arguments[] Expression argprefix; // expression to be evaluated just before arguments[]
CtorDeclaration member; // constructor function CtorDeclaration member; // constructor function
@ -2458,23 +2459,25 @@ extern (C++) final class NewExp : Expression
/// The fields are still separate for backwards compatibility /// The fields are still separate for backwards compatibility
extern (D) ArgumentList argumentList() { return ArgumentList(arguments, names); } extern (D) ArgumentList argumentList() { return ArgumentList(arguments, names); }
extern (D) this(Loc loc, Expression thisexp, Type newtype, Expressions* arguments, Identifiers* names = null) @safe extern (D) this(Loc loc, Expression placement, Expression thisexp, Type newtype, Expressions* arguments, Identifiers* names = null) @safe
{ {
super(loc, EXP.new_); super(loc, EXP.new_);
this.placement = placement;
this.thisexp = thisexp; this.thisexp = thisexp;
this.newtype = newtype; this.newtype = newtype;
this.arguments = arguments; this.arguments = arguments;
this.names = names; this.names = names;
} }
static NewExp create(Loc loc, Expression thisexp, Type newtype, Expressions* arguments) @safe static NewExp create(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() override NewExp syntaxCopy()
{ {
return new NewExp(loc, return new NewExp(loc,
placement ? placement.syntaxCopy() : null,
thisexp ? thisexp.syntaxCopy() : null, thisexp ? thisexp.syntaxCopy() : null,
newtype.syntaxCopy(), newtype.syntaxCopy(),
arraySyntaxCopy(arguments), arraySyntaxCopy(arguments),
@ -2495,10 +2498,12 @@ extern (C++) final class NewAnonClassExp : Expression
Expression thisexp; // if !=null, 'this' for class being allocated Expression thisexp; // if !=null, 'this' for class being allocated
ClassDeclaration cd; // class being instantiated ClassDeclaration cd; // class being instantiated
Expressions* arguments; // Array of Expression's to call class constructor Expressions* arguments; // Array of Expression's to call class constructor
Expression placement; // if !=null, then PlacementExpression
extern (D) this(Loc loc, Expression thisexp, ClassDeclaration cd, Expressions* arguments) @safe extern (D) this(Loc loc, Expression placement, Expression thisexp, ClassDeclaration cd, Expressions* arguments) @safe
{ {
super(loc, EXP.newAnonymousClass); super(loc, EXP.newAnonymousClass);
this.placement = placement;
this.thisexp = thisexp; this.thisexp = thisexp;
this.cd = cd; this.cd = cd;
this.arguments = arguments; this.arguments = arguments;
@ -2506,7 +2511,9 @@ extern (C++) final class NewAnonClassExp : Expression
override NewAnonClassExp syntaxCopy() 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) override void accept(Visitor v)

View file

@ -517,6 +517,7 @@ public:
Type *newtype; Type *newtype;
Expressions *arguments; // Array of Expression's Expressions *arguments; // Array of Expression's
Identifiers *names; // Array of names corresponding to expressions Identifiers *names; // Array of names corresponding to expressions
Expression *placement; // if !NULL, placement expression
Expression *argprefix; // expression to be evaluated just before arguments[] Expression *argprefix; // expression to be evaluated just before arguments[]
@ -526,7 +527,7 @@ public:
Expression *lowering; // lowered druntime hook: `_d_newclass` Expression *lowering; // lowered druntime hook: `_d_newclass`
static NewExp *create(Loc loc, Expression *thisexp, Type *newtype, Expressions *arguments); static NewExp *create(Loc loc, Expression *placement, Expression *thisexp, Type *newtype, Expressions *arguments);
NewExp *syntaxCopy() override; NewExp *syntaxCopy() override;
void accept(Visitor *v) override { v->visit(this); } void accept(Visitor *v) override { v->visit(this); }
@ -540,6 +541,7 @@ public:
Expression *thisexp; // if !NULL, 'this' for class being allocated Expression *thisexp; // if !NULL, 'this' for class being allocated
ClassDeclaration *cd; // class being instantiated ClassDeclaration *cd; // class being instantiated
Expressions *arguments; // Array of Expression's to call class constructor Expressions *arguments; // Array of Expression's to call class constructor
Expression *placement; // if !NULL, placement expression
NewAnonClassExp *syntaxCopy() override; NewAnonClassExp *syntaxCopy() override;
void accept(Visitor *v) override { v->visit(this); } void accept(Visitor *v) override { v->visit(this); }

View file

@ -3161,7 +3161,7 @@ private bool functionParameters(Loc loc, Scope* sc,
auto args = new Expressions(nargs - i); auto args = new Expressions(nargs - i);
foreach (u; i .. nargs) foreach (u; i .. nargs)
(*args)[u - i] = (*arguments)[u]; (*args)[u - i] = (*arguments)[u];
arg = new NewExp(loc, null, p.type, args); arg = new NewExp(loc, null, null, p.type, args);
break; break;
} }
default: default:
@ -4879,6 +4879,28 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
printf("\tnewtype: %s\n", exp.newtype.toChars()); 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 //for error messages if the argument in [] is not convertible to size_t
const originalNewtype = exp.newtype; const originalNewtype = exp.newtype;
@ -4965,6 +4987,19 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
return setError(); 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; const size_t nargs = exp.arguments ? exp.arguments.length : 0;
Expression newprefix = null; Expression newprefix = null;
@ -4973,9 +5008,14 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
auto cd = tc.sym; auto cd = tc.sym;
if (cd.errors) if (cd.errors)
return setError(); return setError();
cd.size(exp.loc); auto objectSize = cd.size(exp.loc);
if (cd.sizeok != Sizeok.done) if (cd.sizeok != Sizeok.done)
return setError(); 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) if (!cd.ctor)
cd.ctor = cd.searchCtor(); cd.ctor = cd.searchCtor();
if (cd.noDefaultCtor && !nargs && !cd.defaultCtor) if (cd.noDefaultCtor && !nargs && !cd.defaultCtor)
@ -5185,6 +5225,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
return; return;
} }
else if (sc.needsCodegen() && // interpreter doesn't need this lowered else if (sc.needsCodegen() && // interpreter doesn't need this lowered
!exp.placement &&
!exp.onstack && !exp.type.isScopeClass()) // these won't use the GC !exp.onstack && !exp.type.isScopeClass()) // these won't use the GC
{ {
/* replace `new T(arguments)` with `core.lifetime._d_newclassT!T(arguments)` /* replace `new T(arguments)` with `core.lifetime._d_newclassT!T(arguments)`
@ -5290,10 +5331,16 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
} }
exp.type = exp.type.pointerTo(); exp.type = exp.type.pointerTo();
tryLowerToNewItem(exp); if (!exp.placement)
tryLowerToNewItem(exp);
} }
else if (tb.ty == Tarray) else if (tb.ty == Tarray)
{ {
if (exp.placement)
{
error(exp.placement.loc, "placement new cannot be used with dynamic arrays");
return setError();
}
if (!nargs) if (!nargs)
{ {
// https://issues.dlang.org/show_bug.cgi?id=20422 // https://issues.dlang.org/show_bug.cgi?id=20422
@ -5364,7 +5411,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
goto LskipNewArrayLowering; 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; auto hook = global.params.tracegc ? Id._d_newarrayTTrace : Id._d_newarrayT;
if (!verifyHookExist(exp.loc, *sc, hook, "new array")) if (!verifyHookExist(exp.loc, *sc, hook, "new array"))
@ -5448,10 +5498,16 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
} }
exp.type = exp.type.pointerTo(); exp.type = exp.type.pointerTo();
tryLowerToNewItem(exp); if (!exp.placement)
tryLowerToNewItem(exp);
} }
else if (tb.ty == Taarray) 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)` // e.g. `new Alias(args)`
if (nargs) if (nargs)
{ {
@ -5501,7 +5557,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
sds.members.push(e.cd); 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); Expression c = new CommaExp(e.loc, d, n);
result = c.expressionSemantic(sc); result = c.expressionSemantic(sc);
@ -14928,6 +14984,8 @@ bool checkSharedAccess(Expression e, Scope* sc, bool returnRef = false)
bool visitNew(NewExp e) bool visitNew(NewExp e)
{ {
if (e.placement)
check(e.placement, false);
if (e.thisexp) if (e.thisexp)
check(e.thisexp, false); check(e.thisexp, false);
return false; return false;
@ -15111,6 +15169,8 @@ Expression resolveLoc(Expression exp, Loc loc, Scope* sc)
Expression visitNew(NewExp exp) Expression visitNew(NewExp exp)
{ {
if (exp.placement)
exp.placement = exp.placement.resolveLoc(loc, sc);
if (exp.thisexp) if (exp.thisexp)
exp.thisexp = exp.thisexp.resolveLoc(loc, sc); exp.thisexp = exp.thisexp.resolveLoc(loc, sc);
if (exp.argprefix) if (exp.argprefix)

View file

@ -3374,6 +3374,7 @@ public:
Expression* thisexp; Expression* thisexp;
ClassDeclaration* cd; ClassDeclaration* cd;
Array<Expression* >* arguments; Array<Expression* >* arguments;
Expression* placement;
NewAnonClassExp* syntaxCopy() override; NewAnonClassExp* syntaxCopy() override;
void accept(Visitor* v) override; void accept(Visitor* v) override;
}; };
@ -3385,12 +3386,13 @@ public:
Type* newtype; Type* newtype;
Array<Expression* >* arguments; Array<Expression* >* arguments;
Array<Identifier* >* names; Array<Identifier* >* names;
Expression* placement;
Expression* argprefix; Expression* argprefix;
CtorDeclaration* member; CtorDeclaration* member;
bool onstack; bool onstack;
bool thrownew; bool thrownew;
Expression* lowering; Expression* lowering;
static NewExp* create(Loc loc, Expression* thisexp, Type* newtype, Array<Expression* >* arguments); static NewExp* create(Loc loc, Expression* placement, Expression* thisexp, Type* newtype, Array<Expression* >* arguments);
NewExp* syntaxCopy() override; NewExp* syntaxCopy() override;
void accept(Visitor* v) override; void accept(Visitor* v) override;
}; };

View file

@ -2531,6 +2531,13 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt
buf.writeByte('.'); buf.writeByte('.');
} }
buf.writestring("new "); 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); typeToBuffer(e.newtype, null, buf, hgs);
if (e.arguments && e.arguments.length) if (e.arguments && e.arguments.length)
{ {
@ -2548,6 +2555,13 @@ private void expressionPrettyPrint(Expression e, ref OutBuffer buf, ref HdrGenSt
buf.writeByte('.'); buf.writeByte('.');
} }
buf.writestring("new"); buf.writestring("new");
if (e.placement)
{
buf.writeByte(' ');
buf.writeByte('(');
expToBuffer(e.placement, PREC.assign, buf, hgs);
buf.writeByte(')');
}
buf.writestring(" class "); buf.writestring(" class ");
if (e.arguments && e.arguments.length) if (e.arguments && e.arguments.length)
{ {

View file

@ -34,6 +34,7 @@ import dmd.errors;
import dmd.func; import dmd.func;
import dmd.funcsem; import dmd.funcsem;
import dmd.globals; import dmd.globals;
import dmd.hdrgen;
import dmd.id; import dmd.id;
import dmd.identifier; import dmd.identifier;
import dmd.init; import dmd.init;
@ -736,6 +737,7 @@ public:
goto LhasLowering; goto LhasLowering;
} }
ne.placement = doInlineAs!Expression(e.placement, ids);
ne.thisexp = doInlineAs!Expression(e.thisexp, ids); ne.thisexp = doInlineAs!Expression(e.thisexp, ids);
ne.argprefix = doInlineAs!Expression(e.argprefix, ids); ne.argprefix = doInlineAs!Expression(e.argprefix, ids);
ne.arguments = arrayExpressionDoInline(e.arguments); ne.arguments = arrayExpressionDoInline(e.arguments);
@ -1001,7 +1003,7 @@ public:
{ {
static if (LOG) static if (LOG)
{ {
printf("ExpStatement.inlineScan(%s)\n", s.toChars()); printf("ExpStatement.inlineScan(%s)\n", toChars(s));
} }
if (!s.exp) if (!s.exp)
return; return;
@ -2202,7 +2204,7 @@ private void expandInline(Loc callLoc, FuncDeclaration fd, FuncDeclaration paren
static if (EXPANDINLINE_LOG) static if (EXPANDINLINE_LOG)
printf("\n[%s] %s expandInline sresult =\n%s\n", printf("\n[%s] %s expandInline sresult =\n%s\n",
callLoc.toChars(), fd.toPrettyChars(), sresult.toChars()); callLoc.toChars(), fd.toPrettyChars(), toChars(sresult));
} }
else else
{ {

View file

@ -430,7 +430,7 @@ public:
{ {
//printf("NewExp.inlineCost3() %s\n", e.toChars()); //printf("NewExp.inlineCost3() %s\n", e.toChars());
AggregateDeclaration ad = isAggregate(e.newtype); AggregateDeclaration ad = isAggregate(e.newtype);
if (ad && ad.isNested()) if (ad && ad.isNested() || e.placement)
cost = COST_MAX; cost = COST_MAX;
else else
cost++; cost++;

View file

@ -137,6 +137,8 @@ public:
override void visit(NewExp e) 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)) if (e.member && !e.member.isNogc() && f.setGC(e.loc, null))
{ {
// @nogc-ness is already checked in NewExp::semantic // @nogc-ness is already checked in NewExp::semantic

View file

@ -1723,6 +1723,8 @@ void genKill(ref ObState obstate, ObNode* ob)
override void visit(NewExp e) override void visit(NewExp e)
{ {
if (e.placement)
e.placement.accept(this);
if (e.arguments) if (e.arguments)
{ {
foreach (ex; *e.arguments) foreach (ex; *e.arguments)
@ -2464,6 +2466,9 @@ void checkObErrors(ref ObState obstate)
override void visit(NewExp e) override void visit(NewExp e)
{ {
if (e.placement)
e.placement.accept(this);
if (e.arguments) if (e.arguments)
{ {
foreach (ex; *e.arguments) foreach (ex; *e.arguments)

View file

@ -751,6 +751,7 @@ Expression optimize(Expression e, int result, bool keepLvalue = false)
void visitNew(NewExp e) void visitNew(NewExp e)
{ {
expOptimize(e.placement, WANTvalue);
expOptimize(e.thisexp, WANTvalue); expOptimize(e.thisexp, WANTvalue);
// Optimize parameters // Optimize parameters
if (e.arguments) if (e.arguments)

View file

@ -9502,7 +9502,17 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
{ {
const loc = token.loc; 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.Expressions* arguments = null;
AST.Identifiers* names = null; AST.Identifiers* names = null;
@ -9538,7 +9548,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
} }
auto cd = new AST.ClassDeclaration(loc, id, baseclasses, members, false); 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; return e;
} }
@ -9562,7 +9572,7 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
parseNamedArguments(arguments, names); 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; return e;
} }

View file

@ -82,13 +82,13 @@ public:
override void visit(NewExp e) override void visit(NewExp e)
{ {
//printf("NewExp::apply(): %s\n", toChars()); //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) override void visit(NewAnonClassExp e)
{ {
//printf("NewAnonClassExp::apply(): %s\n", toChars()); //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) override void visit(TypeidExp e)

View file

@ -994,6 +994,8 @@ package(dmd.visitor) mixin template ParseVisitMethods(AST)
override void visit(AST.NewExp e) override void visit(AST.NewExp e)
{ {
//printf("Visiting NewExp\n"); //printf("Visiting NewExp\n");
if (e.placement)
e.placement.accept(this);
if (e.thisexp) if (e.thisexp)
e.thisexp.accept(this); e.thisexp.accept(this);
visitType(e.newtype); visitType(e.newtype);
@ -1003,6 +1005,8 @@ package(dmd.visitor) mixin template ParseVisitMethods(AST)
override void visit(AST.NewAnonClassExp e) override void visit(AST.NewAnonClassExp e)
{ {
//printf("Visiting NewAnonClassExp\n"); //printf("Visiting NewAnonClassExp\n");
if (e.placement)
e.placement.accept(this);
if (e.thisexp) if (e.thisexp)
e.thisexp.accept(this); e.thisexp.accept(this);
visitArgs(e.arguments.peekSlice()); visitArgs(e.arguments.peekSlice());

View file

@ -1344,7 +1344,7 @@ public:
(void)d->csym; (void)d->csym;
(void)d->vtblSymbol()->csym; (void)d->vtblSymbol()->csym;
(void)d->sinit; (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; ne->type = d->type;
Expression *e = dmd::ctfeInterpret(ne); Expression *e = dmd::ctfeInterpret(ne);
assert(e->op == EXP::classReference); assert(e->op == EXP::classReference);

View file

@ -0,0 +1,4 @@
void f(int* p) @nogc
{
new(*p) int;
}

View file

@ -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;
}
/*************************************************/

View file

@ -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;
}