Skip to content

Commit b9826ca

Browse files
committed
implement move constructor
1 parent 17ee130 commit b9826ca

12 files changed

Lines changed: 289 additions & 23 deletions

File tree

compiler/src/dmd/aggregate.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,8 @@ class StructDeclaration : public AggregateDeclaration
177177
bool zeroInit(bool v);
178178
bool hasIdentityAssign() const; // true if has identity opAssign
179179
bool hasIdentityAssign(bool v);
180+
bool hasMoveAssign() const; // true if has identity opAssign
181+
bool hasMoveAssign(bool v);
180182
bool hasBlitAssign() const; // true if opAssign is a blit
181183
bool hasBlitAssign(bool v);
182184
bool hasIdentityEquals() const; // true if has identity opEquals
@@ -185,6 +187,8 @@ class StructDeclaration : public AggregateDeclaration
185187
bool hasNoFields(bool v);
186188
bool hasCopyCtor() const; // copy constructor
187189
bool hasCopyCtor(bool v);
190+
bool hasMoveCtor() const; // copy constructor
191+
bool hasMoveCtor(bool v);
188192
// Even if struct is defined as non-root symbol, some built-in operations
189193
// (e.g. TypeidExp, NewExp, ArrayLiteralExp, etc) request its TypeInfo.
190194
// For those, today TypeInfo_Struct is generated in COMDAT.

compiler/src/dmd/backend/debugprint.d

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,7 @@ void numberBlocks(block *startblock)
510510
@trusted
511511
void WRfunc(const char* msg, Symbol* sfunc, block* startblock)
512512
{
513-
printf("............%s...%s().............\n", msg, sfunc.Sident.ptr);
513+
printf("............%s...%s()\n", msg, sfunc.Sident.ptr);
514514
numberBlocks(startblock);
515515
for (block *b = startblock; b; b = b.Bnext)
516516
WRblock(b);

compiler/src/dmd/backend/dout.d

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -861,7 +861,7 @@ private void writefunc2(Symbol *sfunc)
861861
{
862862
func_t *f = sfunc.Sfunc;
863863

864-
//printf("writefunc(%s)\n",sfunc.Sident.ptr);
864+
debugb && printf("===================== writefunc %s =================\n",sfunc.Sident.ptr);
865865
//symbol_print(sfunc);
866866
debug debugy && printf("writefunc(%s)\n",sfunc.Sident.ptr);
867867

compiler/src/dmd/clone.d

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1533,6 +1533,9 @@ FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc)
15331533
return xpostblit;
15341534
}
15351535

1536+
/* ===================================== Copy Constructor ========================== */
1537+
static if (1) {
1538+
15361539
/**
15371540
* Generates a copy constructor declaration with the specified storage
15381541
* class for the parameter and the function.
@@ -1736,3 +1739,209 @@ bool buildCopyCtor(StructDeclaration sd, Scope* sc)
17361739
}
17371740
return true;
17381741
}
1742+
1743+
}
1744+
1745+
/* ===================================== Move Constructor ========================== */
1746+
static if (1) {
1747+
1748+
/**
1749+
* Generates a move constructor declaration with the specified storage
1750+
* class for the parameter and the function.
1751+
*
1752+
* Params:
1753+
* sd = the `struct` that contains the move constructor
1754+
* paramStc = the storage class of the move constructor parameter
1755+
* funcStc = the storage class for the move constructor declaration
1756+
*
1757+
* Returns:
1758+
* The move constructor declaration for struct `sd`.
1759+
*/
1760+
private CtorDeclaration generateMoveCtorDeclaration(StructDeclaration sd, const StorageClass paramStc, const StorageClass funcStc)
1761+
{
1762+
/* Although the move constructor is declared as `this(S s) { ... }`,
1763+
* it is implemented as `this(ref S s) { ... }`
1764+
*/
1765+
return generateCopyCtorDeclaration(sd, paramStc, funcStc);
1766+
}
1767+
1768+
/**
1769+
* Generates a trivial move constructor body that simply does memberwise
1770+
* initialization:
1771+
*
1772+
* this.field1 = rhs.field1;
1773+
* this.field2 = rhs.field2;
1774+
* ...
1775+
*
1776+
* Params:
1777+
* sd = the `struct` declaration that contains the copy constructor
1778+
*
1779+
* Returns:
1780+
* A `CompoundStatement` containing the body of the copy constructor.
1781+
*/
1782+
private Statement generateMoveCtorBody(StructDeclaration sd)
1783+
{
1784+
Loc loc;
1785+
Expression e;
1786+
foreach (v; sd.fields)
1787+
{
1788+
auto ec = new AssignExp(loc,
1789+
new DotVarExp(loc, new ThisExp(loc), v),
1790+
new DotVarExp(loc, new IdentifierExp(loc, Id.p), v));
1791+
e = Expression.combine(e, ec);
1792+
//printf("e.toChars = %s\n", e.toChars());
1793+
}
1794+
Statement s1 = new ExpStatement(loc, e);
1795+
return new CompoundStatement(loc, s1);
1796+
}
1797+
1798+
/**
1799+
* Determine if a move constructor is needed for struct sd,
1800+
* if the following conditions are met:
1801+
*
1802+
* 1. sd does not define a move constructor
1803+
* 2. at least one field of sd defines a move constructor
1804+
*
1805+
* Params:
1806+
* sd = the `struct` for which the move constructor is generated
1807+
* hasMoveCtor = set to true if a move constructor is already present
1808+
*
1809+
* Returns:
1810+
* `true` if one needs to be generated
1811+
* `false` otherwise
1812+
*/
1813+
bool needMoveCtor(StructDeclaration sd, out bool hasMoveCtor)
1814+
{
1815+
if (global.errors)
1816+
return false;
1817+
1818+
auto ctor = sd.search(sd.loc, Id.ctor);
1819+
if (ctor)
1820+
{
1821+
if (ctor.isOverloadSet())
1822+
return false;
1823+
if (auto td = ctor.isTemplateDeclaration())
1824+
ctor = td.funcroot;
1825+
}
1826+
1827+
CtorDeclaration moveCtor;
1828+
CtorDeclaration rvalueCtor;
1829+
1830+
if (!ctor)
1831+
goto LcheckFields;
1832+
1833+
overloadApply(ctor, (Dsymbol s)
1834+
{
1835+
if (s.isTemplateDeclaration())
1836+
return 0;
1837+
auto ctorDecl = s.isCtorDeclaration();
1838+
assert(ctorDecl);
1839+
if (ctorDecl.isMoveCtor)
1840+
{
1841+
if (!moveCtor)
1842+
moveCtor = ctorDecl;
1843+
return 0;
1844+
}
1845+
1846+
if (isRvalueConstructor(sd, ctorDecl))
1847+
rvalueCtor = ctorDecl;
1848+
return 0;
1849+
});
1850+
1851+
if (moveCtor)
1852+
{
1853+
if (rvalueCtor)
1854+
{
1855+
.error(sd.loc, "`struct %s` may not define both a rvalue constructor and a move constructor", sd.toChars());
1856+
errorSupplemental(rvalueCtor.loc,"rvalue constructor defined here");
1857+
errorSupplemental(moveCtor.loc, "move constructor defined here");
1858+
}
1859+
hasMoveCtor = true;
1860+
return false;
1861+
}
1862+
1863+
LcheckFields:
1864+
VarDeclaration fieldWithMoveCtor;
1865+
// see if any struct members define a copy constructor
1866+
foreach (v; sd.fields)
1867+
{
1868+
if (v.storage_class & STC.ref_)
1869+
continue;
1870+
if (v.overlapped)
1871+
continue;
1872+
1873+
auto ts = v.type.baseElemOf().isTypeStruct();
1874+
if (!ts)
1875+
continue;
1876+
if (ts.sym.hasMoveCtor)
1877+
{
1878+
fieldWithMoveCtor = v;
1879+
break;
1880+
}
1881+
}
1882+
1883+
if (fieldWithMoveCtor && rvalueCtor)
1884+
{
1885+
.error(sd.loc, "`struct %s` may not define a rvalue constructor and have fields with move constructors", sd.toChars());
1886+
errorSupplemental(rvalueCtor.loc,"rvalue constructor defined here");
1887+
errorSupplemental(fieldWithMoveCtor.loc, "field with move constructor defined here");
1888+
return false;
1889+
}
1890+
else if (!fieldWithMoveCtor)
1891+
return false;
1892+
return true;
1893+
}
1894+
1895+
/**
1896+
* Generates a move constructor if needMoveCtor() returns true.
1897+
* The generated move constructor will be of the form:
1898+
* this(ref return scope inout(S) rhs) inout
1899+
* {
1900+
* this.field1 = rhs.field1;
1901+
* this.field2 = rhs.field2;
1902+
* ...
1903+
* }
1904+
*
1905+
* Params:
1906+
* sd = the `struct` for which the copy constructor is generated
1907+
* sc = the scope where the copy constructor is generated
1908+
*
1909+
* Returns:
1910+
* `true` if `struct` sd defines a copy constructor (explicitly or generated),
1911+
* `false` otherwise.
1912+
* References:
1913+
* https://dlang.org/spec/struct.html#struct-copy-constructor
1914+
*/
1915+
bool buildMoveCtor(StructDeclaration sd, Scope* sc)
1916+
{
1917+
//printf("buildMoveCtor() %s\n", sd.toChars());
1918+
bool hasMoveCtor;
1919+
if (!needMoveCtor(sd, hasMoveCtor))
1920+
return hasMoveCtor;
1921+
1922+
//printf("generating move constructor for %s\n", sd.toChars());
1923+
const MOD paramMod = MODFlags.wild;
1924+
const MOD funcMod = MODFlags.wild;
1925+
auto ccd = generateMoveCtorDeclaration(sd, ModToStc(paramMod), ModToStc(funcMod));
1926+
auto moveCtorBody = generateMoveCtorBody(sd);
1927+
ccd.fbody = moveCtorBody;
1928+
sd.members.push(ccd);
1929+
ccd.addMember(sc, sd);
1930+
const errors = global.startGagging();
1931+
Scope* sc2 = sc.push();
1932+
sc2.stc = 0;
1933+
sc2.linkage = LINK.d;
1934+
ccd.dsymbolSemantic(sc2);
1935+
ccd.semantic2(sc2);
1936+
ccd.semantic3(sc2);
1937+
//printf("ccd semantic: %s\n", ccd.type.toChars());
1938+
sc2.pop();
1939+
if (global.endGagging(errors) || sd.isUnionDeclaration())
1940+
{
1941+
ccd.storage_class |= STC.disable;
1942+
ccd.fbody = null;
1943+
}
1944+
return true;
1945+
}
1946+
1947+
}

compiler/src/dmd/dstruct.d

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,9 +218,11 @@ extern (C++) class StructDeclaration : AggregateDeclaration
218218
bool zeroInit; // !=0 if initialize with 0 fill
219219
bool hasIdentityAssign; // true if has identity opAssign
220220
bool hasBlitAssign; // true if opAssign is a blit
221+
bool hasMoveAssign; // true if move assignment
221222
bool hasIdentityEquals; // true if has identity opEquals
222223
bool hasNoFields; // has no fields
223224
bool hasCopyCtor; // copy constructor
225+
bool hasMoveCtor; // move constructor
224226
bool hasPointerField; // members with indirections
225227
bool hasVoidInitPointers; // void-initialized unsafe fields
226228
bool hasUnsafeBitpatterns; // @system members, pointers, bool
@@ -417,7 +419,7 @@ extern (C++) class StructDeclaration : AggregateDeclaration
417419
* POD is defined as:
418420
* $(OL
419421
* $(LI not nested)
420-
* $(LI no postblits, destructors, or assignment operators)
422+
* $(LI no postblits, copy constructors, move constructors, destructors, or assignment operators)
421423
* $(LI no `ref` fields or fields that are themselves non-POD)
422424
* )
423425
* The idea being these are compatible with C structs.
@@ -436,10 +438,14 @@ extern (C++) class StructDeclaration : AggregateDeclaration
436438
bool hasCpCtorLocal;
437439
needCopyCtor(this, hasCpCtorLocal);
438440

441+
bool hasMoveCtorLocal;
442+
needMoveCtor(this, hasMoveCtorLocal);
443+
439444
if (enclosing || // is nested
440445
search(this, loc, Id.postblit) || // has postblit
441446
search(this, loc, Id.dtor) || // has destructor
442-
hasCpCtorLocal) // has copy constructor
447+
hasCpCtorLocal || // has copy constructor
448+
hasMoveCtorLocal) // has move constructor
443449
{
444450
ispod = ThreeState.no;
445451
return false;

compiler/src/dmd/dsymbolsem.d

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ bool isRvalueConstructor(StructDeclaration sd, CtorDeclaration ctor)
281281
{
282282
auto tf = ctor.type.isTypeFunction();
283283
const dim = tf.parameterList.length;
284-
if (dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg))
284+
if (dim > 1 && tf.parameterList[1].defaultArg)
285285
{
286286
auto param = tf.parameterList[0];
287287
if (!(param.storageClass & STC.ref_) && param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf())
@@ -554,6 +554,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
554554

555555
override void visit(VarDeclaration dsym)
556556
{
557+
//printf("VarDeclaration %s\n", dsym.toChars());
557558
version (none)
558559
{
559560
printf("VarDeclaration::semantic('%s', parent = '%s') sem = %d\n",
@@ -2473,14 +2474,22 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
24732474
.error(ctd.loc, "%s `%s` all parameters have default arguments, "~
24742475
"but structs cannot have default constructors.", ctd.kind, ctd.toPrettyChars);
24752476
}
2476-
else if ((dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg)))
2477+
else if (dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg))
24772478
{
24782479
//printf("tf: %s\n", tf.toChars());
24792480
auto param = tf.parameterList[0];
2480-
if (param.storageClass & STC.ref_ && param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf())
2481+
if (param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf())
24812482
{
2482-
//printf("copy constructor\n");
2483-
ctd.isCpCtor = true;
2483+
if (param.storageClass & STC.ref_)
2484+
{
2485+
//printf("found copy constructor\n");
2486+
ctd.isCpCtor = true;
2487+
}
2488+
else
2489+
{
2490+
//printf("found move constructor\n");
2491+
ctd.isMoveCtor = true;
2492+
}
24842493
}
24852494
}
24862495
}
@@ -3055,6 +3064,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
30553064
buildDtors(sd, sc2);
30563065

30573066
sd.hasCopyCtor = buildCopyCtor(sd, sc2);
3067+
sd.hasMoveCtor = buildMoveCtor(sd, sc2);
30583068
sd.postblit = buildPostBlit(sd, sc2);
30593069

30603070
buildOpAssign(sd, sc2);

compiler/src/dmd/e2ir.d

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5507,6 +5507,12 @@ elem *callfunc(const ref Loc loc,
55075507
bool copy = !(v && v.isArgDtorVar); // copy unless the destructor is going to be run on it
55085508
// then assume the frontend took care of the copying and pass it by ref
55095509

5510+
if (ea.Eoper == OPind && ea.E1.Eoper == OPcall && arg.type.isTypeStruct())
5511+
{
5512+
if (auto ctor = fd.isCtorDeclaration())
5513+
copy = !ctor.isMoveCtor;
5514+
}
5515+
55105516
elems[i] = addressElem(ea, arg.type, copy);
55115517
continue;
55125518
}

0 commit comments

Comments
 (0)