Skip to content

Commit ba6a1e1

Browse files
committed
implement move constructor
1 parent 8ce08e9 commit ba6a1e1

File tree

19 files changed

+357
-44
lines changed

19 files changed

+357
-44
lines changed

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/astbase.d

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -703,7 +703,7 @@ struct ASTBase
703703

704704
extern (C++) final class CtorDeclaration : FuncDeclaration
705705
{
706-
extern (D) this(const ref Loc loc, Loc endloc, StorageClass stc, Type type, bool isCopyCtor = false)
706+
extern (D) this(const ref Loc loc, Loc endloc, StorageClass stc, Type type, bool isCopyCtor = false, bool isMoveCtor = false)
707707
{
708708
super(loc, endloc, Id.ctor, stc, type);
709709
}

compiler/src/dmd/backend/debugprint.d

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

compiler/src/dmd/backend/dout.d

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

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

compiler/src/dmd/clone.d

Lines changed: 213 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1544,6 +1544,9 @@ FuncDeclaration buildPostBlit(StructDeclaration sd, Scope* sc)
15441544
return xpostblit;
15451545
}
15461546

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

compiler/src/dmd/dstruct.d

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,11 @@ extern (C++) class StructDeclaration : AggregateDeclaration
9898
bool zeroInit; // !=0 if initialize with 0 fill
9999
bool hasIdentityAssign; // true if has identity opAssign
100100
bool hasBlitAssign; // true if opAssign is a blit
101+
bool hasMoveAssign; // true if move assignment
101102
bool hasIdentityEquals; // true if has identity opEquals
102103
bool hasNoFields; // has no fields
103104
bool hasCopyCtor; // copy constructor
105+
bool hasMoveCtor; // move constructor
104106
bool hasPointerField; // members with indirections
105107
bool hasVoidInitPointers; // void-initialized unsafe fields
106108
bool hasUnsafeBitpatterns; // @system members, pointers, bool
@@ -303,7 +305,7 @@ extern (C++) class StructDeclaration : AggregateDeclaration
303305
* POD is defined as:
304306
* $(OL
305307
* $(LI not nested)
306-
* $(LI no postblits, destructors, or assignment operators)
308+
* $(LI no postblits, copy constructors, move constructors, destructors, or assignment operators)
307309
* $(LI no `ref` fields or fields that are themselves non-POD)
308310
* )
309311
* The idea being these are compatible with C structs.
@@ -322,10 +324,14 @@ extern (C++) class StructDeclaration : AggregateDeclaration
322324
bool hasCpCtorLocal;
323325
needCopyCtor(this, hasCpCtorLocal);
324326

327+
bool hasMoveCtorLocal;
328+
needMoveCtor(this, hasMoveCtorLocal);
329+
325330
if (enclosing || // is nested
326331
search(this, loc, Id.postblit) || // has postblit
327332
search(this, loc, Id.dtor) || // has destructor
328-
hasCpCtorLocal) // has copy constructor
333+
hasCpCtorLocal || // has copy constructor
334+
hasMoveCtorLocal) // has move constructor
329335
{
330336
ispod = ThreeState.no;
331337
return false;

0 commit comments

Comments
 (0)