Skip to content

Commit

Permalink
implement move constructor
Browse files Browse the repository at this point in the history
  • Loading branch information
WalterBright committed Oct 10, 2024
1 parent 8ce08e9 commit ba6a1e1
Show file tree
Hide file tree
Showing 19 changed files with 357 additions and 44 deletions.
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)

Check warning on line 706 in compiler/src/dmd/astbase.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/astbase.d#L706

Added line #L706 was not covered by tests
{
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);

Check warning on line 512 in compiler/src/dmd/backend/debugprint.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/backend/debugprint.d#L512

Added line #L512 was not covered by tests
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);

Check warning on line 1777 in compiler/src/dmd/clone.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/clone.d#L1777

Added line #L1777 was not covered by tests
}

/**
* 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)

Check warning on line 1798 in compiler/src/dmd/clone.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/clone.d#L1796-L1798

Added lines #L1796 - L1798 were not covered by tests
{
auto ec = new AssignExp(loc,

Check warning on line 1800 in compiler/src/dmd/clone.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/clone.d#L1800

Added line #L1800 was not covered by tests
new DotVarExp(loc, new ThisExp(loc), v),
new DotVarExp(loc, new IdentifierExp(loc, Id.p), v));
e = Expression.combine(e, ec);

Check warning on line 1803 in compiler/src/dmd/clone.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/clone.d#L1803

Added line #L1803 was not covered by tests
//printf("e.toChars = %s\n", e.toChars());
}
Statement s1 = new ExpStatement(loc, e);
return new CompoundStatement(loc, s1);

Check warning on line 1807 in compiler/src/dmd/clone.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/clone.d#L1806-L1807

Added lines #L1806 - L1807 were not covered by tests
}

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

Check warning on line 1836 in compiler/src/dmd/clone.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/clone.d#L1833-L1836

Added lines #L1833 - L1836 were not covered by tests
}

CtorDeclaration moveCtor;
CtorDeclaration rvalueCtor;

if (!ctor)
goto LcheckFields;

overloadApply(ctor, (Dsymbol s)

Check warning on line 1845 in compiler/src/dmd/clone.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/clone.d#L1845

Added line #L1845 was not covered by tests
{
if (s.isTemplateDeclaration())
return 0;
auto ctorDecl = s.isCtorDeclaration();
assert(ctorDecl);
if (ctorDecl.isMoveCtor)

Check warning on line 1851 in compiler/src/dmd/clone.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/clone.d#L1847-L1851

Added lines #L1847 - L1851 were not covered by tests
{
if (!moveCtor)
moveCtor = ctorDecl;
return 0;

Check warning on line 1855 in compiler/src/dmd/clone.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/clone.d#L1853-L1855

Added lines #L1853 - L1855 were not covered by tests
}

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

Check warning on line 1860 in compiler/src/dmd/clone.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/clone.d#L1858-L1860

Added lines #L1858 - L1860 were not covered by tests
});

if (moveCtor)

Check warning on line 1863 in compiler/src/dmd/clone.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/clone.d#L1863

Added line #L1863 was not covered by tests
{
if (rvalueCtor)

Check warning on line 1865 in compiler/src/dmd/clone.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/clone.d#L1865

Added line #L1865 was not covered by tests
{
.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");

Check warning on line 1869 in compiler/src/dmd/clone.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/clone.d#L1867-L1869

Added lines #L1867 - L1869 were not covered by tests
}
hasMoveCtor = true;
return false;

Check warning on line 1872 in compiler/src/dmd/clone.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/clone.d#L1871-L1872

Added lines #L1871 - L1872 were not covered by tests
}

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

Check warning on line 1881 in compiler/src/dmd/clone.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/clone.d#L1881

Added line #L1881 was not covered by tests
if (v.overlapped)
continue;

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

Check warning on line 1891 in compiler/src/dmd/clone.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/clone.d#L1890-L1891

Added lines #L1890 - L1891 were not covered by tests
}
}

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;

Check warning on line 1900 in compiler/src/dmd/clone.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/clone.d#L1897-L1900

Added lines #L1897 - L1900 were not covered by tests
}
else if (!fieldWithMoveCtor)
return false;
return true;

Check warning on line 1904 in compiler/src/dmd/clone.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/clone.d#L1904

Added line #L1904 was not covered by tests
}

/**
* 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);

Check warning on line 1948 in compiler/src/dmd/clone.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/clone.d#L1935-L1948

Added lines #L1935 - L1948 were not covered by tests
//printf("ccd semantic: %s\n", ccd.type.toChars());
sc2.pop();
if (global.endGagging(errors) || sd.isUnionDeclaration())

Check warning on line 1951 in compiler/src/dmd/clone.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/clone.d#L1950-L1951

Added lines #L1950 - L1951 were not covered by tests
{
ccd.storage_class |= STC.disable;
ccd.fbody = null;

Check warning on line 1954 in compiler/src/dmd/clone.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/clone.d#L1953-L1954

Added lines #L1953 - L1954 were not covered by tests
}
return true;

Check warning on line 1956 in compiler/src/dmd/clone.d

View check run for this annotation

Codecov / codecov/patch

compiler/src/dmd/clone.d#L1956

Added line #L1956 was not covered by tests
}

}
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

0 comments on commit ba6a1e1

Please sign in to comment.