@@ -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+ }
0 commit comments