Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions compiler/src/dmd/aggregate.h
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ class StructDeclaration : public AggregateDeclaration
bool zeroInit(bool v);
bool hasIdentityAssign() const; // true if has identity opAssign
bool hasIdentityAssign(bool v);
bool hasMoveAssign() const; // true if has identity opAssign
bool hasMoveAssign(bool v);
bool hasBlitAssign() const; // true if opAssign is a blit
bool hasBlitAssign(bool v);
bool hasIdentityEquals() const; // true if has identity opEquals
Expand All @@ -185,6 +187,8 @@ class StructDeclaration : public AggregateDeclaration
bool hasNoFields(bool v);
bool hasCopyCtor() const; // copy constructor
bool hasCopyCtor(bool v);
bool hasMoveCtor() const; // copy constructor
bool hasMoveCtor(bool v);
// Even if struct is defined as non-root symbol, some built-in operations
// (e.g. TypeidExp, NewExp, ArrayLiteralExp, etc) request its TypeInfo.
// For those, today TypeInfo_Struct is generated in COMDAT.
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dmd/astbase.d
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,7 @@ struct ASTBase

extern (C++) final class CtorDeclaration : FuncDeclaration
{
extern (D) this(const ref Loc loc, Loc endloc, StorageClass stc, Type type, bool isCopyCtor = false)
extern (D) this(const ref Loc loc, Loc endloc, StorageClass stc, Type type, bool isCopyCtor = false, bool isMoveCtor = false)
{
super(loc, endloc, Id.ctor, stc, type);
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dmd/backend/debugprint.d
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,7 @@ void numberBlocks(block *startblock)
@trusted
void WRfunc(const char* msg, Symbol* sfunc, block* startblock)
{
printf("............%s...%s().............\n", msg, sfunc.Sident.ptr);
printf("............%s...%s()\n", msg, sfunc.Sident.ptr);
numberBlocks(startblock);
for (block *b = startblock; b; b = b.Bnext)
WRblock(b);
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dmd/backend/dout.d
Original file line number Diff line number Diff line change
Expand Up @@ -862,7 +862,7 @@ private void writefunc2(Symbol *sfunc, ref GlobalOptimizer go)
{
func_t *f = sfunc.Sfunc;

//printf("writefunc(%s)\n",sfunc.Sident.ptr);
debugb && printf("===================== writefunc %s =================\n",sfunc.Sident.ptr);
//symbol_print(sfunc);
debug debugy && printf("writefunc(%s)\n",sfunc.Sident.ptr);

Expand Down
216 changes: 213 additions & 3 deletions compiler/src/dmd/clone.d
Original file line number Diff line number Diff line change
Expand Up @@ -1544,6 +1544,9 @@ FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc)
return xpostblit;
}

/* ===================================== Copy Constructor ========================== */
static if (1) {

/**
* Generates a copy constructor declaration with the specified storage
* class for the parameter and the function.
Expand All @@ -1552,18 +1555,19 @@ FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc)
* sd = the `struct` that contains the copy constructor
* paramStc = the storage class of the copy constructor parameter
* funcStc = the storage class for the copy constructor declaration
* copyCtor = true for copy constructor, false for move constructor
*
* Returns:
* The copy constructor declaration for struct `sd`.
*/
private CtorDeclaration generateCopyCtorDeclaration(StructDeclaration sd, const StorageClass paramStc, const StorageClass funcStc)
private CtorDeclaration generateCopyCtorDeclaration(StructDeclaration sd, const StorageClass paramStc, const StorageClass funcStc, bool copyCtor)
{
auto fparams = new Parameters();
auto structType = sd.type;
fparams.push(new Parameter(Loc.initial, paramStc | STC.ref_ | STC.return_ | STC.scope_, structType, Id.p, null, null));
ParameterList pList = ParameterList(fparams);
auto tf = new TypeFunction(pList, structType, LINK.d, STC.ref_);
auto ccd = new CtorDeclaration(sd.loc, Loc.initial, STC.ref_, tf, true);
auto ccd = new CtorDeclaration(sd.loc, Loc.initial, STC.ref_, tf, copyCtor, !copyCtor);
ccd.storage_class |= funcStc;
ccd.storage_class |= STC.inference;
ccd.isGenerated = true;
Expand Down Expand Up @@ -1726,7 +1730,7 @@ bool buildCopyCtor(StructDeclaration sd, Scope* sc)
//printf("generating copy constructor for %s\n", sd.toChars());
const MOD paramMod = MODFlags.wild;
const MOD funcMod = MODFlags.wild;
auto ccd = generateCopyCtorDeclaration(sd, ModToStc(paramMod), ModToStc(funcMod));
auto ccd = generateCopyCtorDeclaration(sd, ModToStc(paramMod), ModToStc(funcMod), true);
auto copyCtorBody = generateCopyCtorBody(sd);
ccd.fbody = copyCtorBody;
sd.members.push(ccd);
Expand All @@ -1747,3 +1751,209 @@ bool buildCopyCtor(StructDeclaration sd, Scope* sc)
}
return true;
}

}

/* ===================================== Move Constructor ========================== */
static if (1) {

/**
* Generates a move constructor declaration with the specified storage
* class for the parameter and the function.
*
* Params:
* sd = the `struct` that contains the move constructor
* paramStc = the storage class of the move constructor parameter
* funcStc = the storage class for the move constructor declaration
*
* Returns:
* The move constructor declaration for struct `sd`.
*/
private CtorDeclaration generateMoveCtorDeclaration(StructDeclaration sd, const StorageClass paramStc, const StorageClass funcStc)
{
/* Although the move constructor is declared as `=this(S s) { ... }`,
* it is implemented as `=this(ref S s) { ... }`
*/
return generateCopyCtorDeclaration(sd, paramStc, funcStc, false);
}

/**
* Generates a trivial move constructor body that simply does memberwise
* initialization:
*
* this.field1 = rhs.field1;
* this.field2 = rhs.field2;
* ...
*
* Params:
* sd = the `struct` declaration that contains the copy constructor
*
* Returns:
* A `CompoundStatement` containing the body of the copy constructor.
*/
private Statement generateMoveCtorBody(StructDeclaration sd)
{
Loc loc;
Expression e;
foreach (v; sd.fields)
{
auto ec = new AssignExp(loc,
new DotVarExp(loc, new ThisExp(loc), v),
new DotVarExp(loc, new IdentifierExp(loc, Id.p), v));
e = Expression.combine(e, ec);
//printf("e.toChars = %s\n", e.toChars());
}
Statement s1 = new ExpStatement(loc, e);
return new CompoundStatement(loc, s1);
}

/**
* Determine if a move constructor is needed for struct sd,
* if the following conditions are met:
*
* 1. sd does not define a move constructor
* 2. at least one field of sd defines a move constructor
*
* Params:
* sd = the `struct` for which the move constructor is generated
* hasMoveCtor = set to true if a move constructor is already present
*
* Returns:
* `true` if one needs to be generated
* `false` otherwise
*/
bool needMoveCtor(StructDeclaration sd, out bool hasMoveCtor)
{
if (global.errors)
return false;

auto ctor = sd.search(sd.loc, Id.moveCtor);
if (ctor)
{
if (ctor.isOverloadSet())
return false;
if (auto td = ctor.isTemplateDeclaration())
ctor = td.funcroot;
}

CtorDeclaration moveCtor;
CtorDeclaration rvalueCtor;

if (!ctor)
goto LcheckFields;

overloadApply(ctor, (Dsymbol s)
{
if (s.isTemplateDeclaration())
return 0;
auto ctorDecl = s.isCtorDeclaration();
assert(ctorDecl);
if (ctorDecl.isMoveCtor)
{
if (!moveCtor)
moveCtor = ctorDecl;
return 0;
}

if (isRvalueConstructor(sd, ctorDecl))
rvalueCtor = ctorDecl;
return 0;
});

if (moveCtor)
{
if (rvalueCtor)
{
.error(sd.loc, "`struct %s` may not define both a rvalue constructor and a move constructor", sd.toChars());
errorSupplemental(rvalueCtor.loc,"rvalue constructor defined here");
errorSupplemental(moveCtor.loc, "move constructor defined here");
}
hasMoveCtor = true;
return false;
}

LcheckFields:
VarDeclaration fieldWithMoveCtor;
// see if any struct members define a copy constructor
foreach (v; sd.fields)
{
if (v.storage_class & STC.ref_)
continue;
if (v.overlapped)
continue;

auto ts = v.type.baseElemOf().isTypeStruct();
if (!ts)
continue;
if (ts.sym.hasMoveCtor)
{
fieldWithMoveCtor = v;
break;
}
}

if (fieldWithMoveCtor && rvalueCtor)
{
.error(sd.loc, "`struct %s` may not define a rvalue constructor and have fields with move constructors", sd.toChars());
errorSupplemental(rvalueCtor.loc,"rvalue constructor defined here");
errorSupplemental(fieldWithMoveCtor.loc, "field with move constructor defined here");
return false;
}
else if (!fieldWithMoveCtor)
return false;
return true;
}

/**
* Generates a move constructor if needMoveCtor() returns true.
* The generated move constructor will be of the form:
* this(ref return scope inout(S) rhs) inout
* {
* this.field1 = rhs.field1;
* this.field2 = rhs.field2;
* ...
* }
*
* Params:
* sd = the `struct` for which the copy constructor is generated
* sc = the scope where the copy constructor is generated
*
* Returns:
* `true` if `struct` sd defines a copy constructor (explicitly or generated),
* `false` otherwise.
* References:
* https://dlang.org/spec/struct.html#struct-copy-constructor
*/
bool buildMoveCtor(StructDeclaration sd, Scope* sc)
{
//printf("buildMoveCtor() %s\n", sd.toChars());
bool hasMoveCtor;
if (!needMoveCtor(sd, hasMoveCtor))
return hasMoveCtor;

//printf("generating move constructor for %s\n", sd.toChars());
const MOD paramMod = MODFlags.wild;
const MOD funcMod = MODFlags.wild;
auto ccd = generateMoveCtorDeclaration(sd, ModToStc(paramMod), ModToStc(funcMod));
auto moveCtorBody = generateMoveCtorBody(sd);
ccd.fbody = moveCtorBody;
sd.members.push(ccd);
ccd.addMember(sc, sd);
const errors = global.startGagging();
Scope* sc2 = sc.push();
sc2.stc = 0;
sc2.linkage = LINK.d;
ccd.dsymbolSemantic(sc2);
ccd.semantic2(sc2);
ccd.semantic3(sc2);
//printf("ccd semantic: %s\n", ccd.type.toChars());
sc2.pop();
if (global.endGagging(errors) || sd.isUnionDeclaration())
{
ccd.storage_class |= STC.disable;
ccd.fbody = null;
}
return true;
}

}
10 changes: 8 additions & 2 deletions compiler/src/dmd/dstruct.d
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,11 @@ extern (C++) class StructDeclaration : AggregateDeclaration
bool zeroInit; // !=0 if initialize with 0 fill
bool hasIdentityAssign; // true if has identity opAssign
bool hasBlitAssign; // true if opAssign is a blit
bool hasMoveAssign; // true if move assignment
bool hasIdentityEquals; // true if has identity opEquals
bool hasNoFields; // has no fields
bool hasCopyCtor; // copy constructor
bool hasMoveCtor; // move constructor
bool hasPointerField; // members with indirections
bool hasVoidInitPointers; // void-initialized unsafe fields
bool hasUnsafeBitpatterns; // @system members, pointers, bool
Expand Down Expand Up @@ -303,7 +305,7 @@ extern (C++) class StructDeclaration : AggregateDeclaration
* POD is defined as:
* $(OL
* $(LI not nested)
* $(LI no postblits, destructors, or assignment operators)
* $(LI no postblits, copy constructors, move constructors, destructors, or assignment operators)
* $(LI no `ref` fields or fields that are themselves non-POD)
* )
* The idea being these are compatible with C structs.
Expand All @@ -322,10 +324,14 @@ extern (C++) class StructDeclaration : AggregateDeclaration
bool hasCpCtorLocal;
needCopyCtor(this, hasCpCtorLocal);

bool hasMoveCtorLocal;
needMoveCtor(this, hasMoveCtorLocal);

if (enclosing || // is nested
search(this, loc, Id.postblit) || // has postblit
search(this, loc, Id.dtor) || // has destructor
hasCpCtorLocal) // has copy constructor
hasCpCtorLocal || // has copy constructor
hasMoveCtorLocal) // has move constructor
{
ispod = ThreeState.no;
return false;
Expand Down
Loading