Skip to content

Commit a99a389

Browse files
authored
add __rvalue(expression) builtin (dlang#17050)
1 parent 13775eb commit a99a389

31 files changed

+471
-163
lines changed

.gitignore

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
*.[oa]
22
*.deps
3+
*.lst
4+
log*
35
.B*
46
*~
7+
/compiler/src/dmd/lwv
8+
/compiler/src/dmd/new
59
/compiler/src/bug2
610
/compiler/test/test_results
711
/compiler/test/trace.def
@@ -12,7 +16,6 @@ generated/
1216
-.DS_Store
1317
-trace.def
1418
-trace.log
15-
*.lst
1619
.dub
1720
dub.selections.json
1821

changelog/dmd.rvalue.dd

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
Add primary expression of the form `__rvalue(expression)` which causes `expression` to be treated as an rvalue, even if it is an lvalue.
2+
3+
Overloads on `ref`:
4+
```
5+
foo(S s); // selected if `s` is an rvalue
6+
foo(ref S s); // selected if argument `s` is an lvalue
7+
8+
S s;
9+
S bar();
10+
...
11+
foo(s); // selects foo(ref S)
12+
foo(bar()); // selects foo(S)
13+
```
14+
With this change,
15+
```
16+
foo(__rvalue(s)); // selects foo(S)
17+
```
18+
This also applies to constructors and assignments, meaning move constructors and
19+
move assignments are enabled. Moving instead of copying can be much more resource
20+
efficient, as, say, a string can be moved rather than copied/deleted.
21+
22+
A moved object can still be destructed, so take that into account when moving
23+
a field - set it to a benign value that can be destructed.

compiler/src/dmd/astbase.d

+1
Original file line numberDiff line numberDiff line change
@@ -4548,6 +4548,7 @@ struct ASTBase
45484548
EXP op;
45494549
ubyte size;
45504550
ubyte parens;
4551+
ubyte rvalue; // consider this an rvalue, even if it is an lvalue
45514552
Type type;
45524553
Loc loc;
45534554

compiler/src/dmd/clone.d

+11-5
Original file line numberDiff line numberDiff line change
@@ -1610,13 +1610,15 @@ private Statement generateCopyCtorBody(StructDeclaration sd)
16101610
* Params:
16111611
* sd = the `struct` for which the copy constructor is generated
16121612
* hasCpCtor = set to true if a copy constructor is already present
1613+
* hasMoveCtor = set to true if a move constructor is already present
16131614
*
16141615
* Returns:
16151616
* `true` if one needs to be generated
16161617
* `false` otherwise
16171618
*/
1618-
bool needCopyCtor(StructDeclaration sd, out bool hasCpCtor)
1619+
bool needCopyCtor(StructDeclaration sd, out bool hasCpCtor, out bool hasMoveCtor)
16191620
{
1621+
//printf("needCopyCtor() %s\n", sd.toChars());
16201622
if (global.errors)
16211623
return false;
16221624

@@ -1648,14 +1650,17 @@ bool needCopyCtor(StructDeclaration sd, out bool hasCpCtor)
16481650
return 0;
16491651
}
16501652

1651-
if (isRvalueConstructor(sd, ctorDecl))
1653+
if (ctorDecl.isMoveCtor)
16521654
rvalueCtor = ctorDecl;
16531655
return 0;
16541656
});
16551657

1658+
if (rvalueCtor)
1659+
hasMoveCtor = true;
1660+
16561661
if (cpCtor)
16571662
{
1658-
if (rvalueCtor)
1663+
if (0 && rvalueCtor)
16591664
{
16601665
.error(sd.loc, "`struct %s` may not define both a rvalue constructor and a copy constructor", sd.toChars());
16611666
errorSupplemental(rvalueCtor.loc,"rvalue constructor defined here");
@@ -1710,17 +1715,18 @@ LcheckFields:
17101715
* Params:
17111716
* sd = the `struct` for which the copy constructor is generated
17121717
* sc = the scope where the copy constructor is generated
1718+
* hasMoveCtor = set to true when a move constructor is also detected
17131719
*
17141720
* Returns:
17151721
* `true` if `struct` sd defines a copy constructor (explicitly or generated),
17161722
* `false` otherwise.
17171723
* References:
17181724
* https://dlang.org/spec/struct.html#struct-copy-constructor
17191725
*/
1720-
bool buildCopyCtor(StructDeclaration sd, Scope* sc)
1726+
bool buildCopyCtor(StructDeclaration sd, Scope* sc, out bool hasMoveCtor)
17211727
{
17221728
bool hasCpCtor;
1723-
if (!needCopyCtor(sd, hasCpCtor))
1729+
if (!needCopyCtor(sd, hasCpCtor, hasMoveCtor))
17241730
return hasCpCtor;
17251731

17261732
//printf("generating copy constructor for %s\n", sd.toChars());

compiler/src/dmd/declaration.h

+1
Original file line numberDiff line numberDiff line change
@@ -782,6 +782,7 @@ class CtorDeclaration final : public FuncDeclaration
782782
{
783783
public:
784784
d_bool isCpCtor;
785+
d_bool isMoveCtor;
785786
CtorDeclaration *syntaxCopy(Dsymbol *) override;
786787
const char *kind() const override;
787788
const char *toChars() const override;

compiler/src/dmd/dscope.d

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import dmd.dclass;
2424
import dmd.declaration;
2525
import dmd.dmodule;
2626
import dmd.doc;
27+
import dmd.dstruct;
2728
import dmd.dsymbol;
2829
import dmd.dsymbolsem;
2930
import dmd.dtemplate;
@@ -146,6 +147,7 @@ extern (C++) struct Scope
146147

147148
AliasDeclaration aliasAsg; /// if set, then aliasAsg is being assigned a new value,
148149
/// do not set wasRead for it
150+
StructDeclaration argStruct; /// elimiate recursion when looking for rvalue construction
149151

150152
extern (D) __gshared Scope* freelist;
151153

compiler/src/dmd/dstruct.d

+7-1
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ extern (C++) class StructDeclaration : AggregateDeclaration
101101
bool hasIdentityEquals; // true if has identity opEquals
102102
bool hasNoFields; // has no fields
103103
bool hasCopyCtor; // copy constructor
104+
bool hasMoveCtor; // move constructor
104105
bool hasPointerField; // members with indirections
105106
bool hasVoidInitPointers; // void-initialized unsafe fields
106107
bool hasUnsafeBitpatterns; // @system members, pointers, bool
@@ -321,11 +322,16 @@ extern (C++) class StructDeclaration : AggregateDeclaration
321322
import dmd.clone;
322323

323324
bool hasCpCtorLocal;
324-
needCopyCtor(this, hasCpCtorLocal);
325+
bool hasMoveCtorLocal;
326+
needCopyCtor(this, hasCpCtorLocal, hasMoveCtorLocal);
325327

326328
if (enclosing || // is nested
327329
search(this, loc, Id.postblit) || // has postblit
328330
search(this, loc, Id.dtor) || // has destructor
331+
/* This is commented out because otherwise buildkite vibe.d:
332+
`canCAS!Task` fails to compile
333+
*/
334+
//hasMoveCtorLocal || // has move constructor
329335
hasCpCtorLocal) // has copy constructor
330336
{
331337
ispod = ThreeState.no;

compiler/src/dmd/dsymbolsem.d

+18-6
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,10 @@ Return:
256256
*/
257257
bool checkHasBothRvalueAndCpCtor(StructDeclaration sd, CtorDeclaration ctor, TemplateInstance ti)
258258
{
259+
//printf("checkHasBothRvalueAndCpCtor() sd: %s ctor: %s ti: %s\n", sd.toChars(), ctor.toChars(), ti.toChars());
260+
/* cannot use ctor.isMoveCtor because semantic pass may not have been run yet,
261+
* so use isRvalueConstructor()
262+
*/
259263
if (sd && sd.hasCopyCtor && isRvalueConstructor(sd, ctor))
260264
{
261265
.error(ctor.loc, "cannot define both an rvalue constructor and a copy constructor for `struct %s`", sd.toChars());
@@ -280,6 +284,7 @@ bool checkHasBothRvalueAndCpCtor(StructDeclaration sd, CtorDeclaration ctor, Tem
280284
*/
281285
bool isRvalueConstructor(StructDeclaration sd, CtorDeclaration ctor)
282286
{
287+
// note commonality with setting isMoveCtor in the semantic code for CtorDeclaration
283288
auto tf = ctor.type.isTypeFunction();
284289
const dim = tf.parameterList.length;
285290
if (dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg))
@@ -306,6 +311,7 @@ bool isRvalueConstructor(StructDeclaration sd, CtorDeclaration ctor)
306311
*/
307312
Expression resolveAliasThis(Scope* sc, Expression e, bool gag = false, bool findOnly = false)
308313
{
314+
//printf("resolveAliasThis() %s\n", toChars(e));
309315
import dmd.typesem : dotExp;
310316
for (AggregateDeclaration ad = isAggregate(e.type); ad;)
311317
{
@@ -2399,7 +2405,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
23992405

24002406
override void visit(CtorDeclaration ctd)
24012407
{
2402-
//printf("CtorDeclaration::semantic() %s\n", toChars());
2408+
//printf("CtorDeclaration::semantic() %p %s\n", ctd, ctd.toChars());
24032409
if (ctd.semanticRun >= PASS.semanticdone)
24042410
return;
24052411
if (ctd._scope)
@@ -2500,12 +2506,15 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
25002506
}
25012507
else if ((dim == 1 || (dim > 1 && tf.parameterList[1].defaultArg)))
25022508
{
2503-
//printf("tf: %s\n", tf.toChars());
2509+
//printf("tf: %s\n", toChars(tf));
25042510
auto param = tf.parameterList[0];
2505-
if (param.storageClass & STC.ref_ && param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf())
2511+
if (param.type.mutableOf().unSharedOf() == sd.type.mutableOf().unSharedOf())
25062512
{
2507-
//printf("copy constructor\n");
2508-
ctd.isCpCtor = true;
2513+
//printf("copy constructor %p\n", ctd);
2514+
if (param.storageClass & STC.ref_)
2515+
ctd.isCpCtor = true; // copy constructor
2516+
else
2517+
ctd.isMoveCtor = true; // move constructor
25092518
}
25102519
}
25112520
}
@@ -2996,7 +3005,10 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor
29963005

29973006
buildDtors(sd, sc2);
29983007

2999-
sd.hasCopyCtor = buildCopyCtor(sd, sc2);
3008+
bool hasMoveCtor;
3009+
sd.hasCopyCtor = buildCopyCtor(sd, sc2, hasMoveCtor);
3010+
sd.hasMoveCtor = hasMoveCtor;
3011+
30003012
sd.postblit = buildPostBlit(sd, sc2);
30013013

30023014
buildOpAssign(sd, sc2);

compiler/src/dmd/e2ir.d

+2
Original file line numberDiff line numberDiff line change
@@ -5532,6 +5532,8 @@ elem *callfunc(const ref Loc loc,
55325532
v = ve.var.isVarDeclaration();
55335533
bool copy = !(v && (v.isArgDtorVar || v.storage_class & STC.rvalue)); // copy unless the destructor is going to be run on it
55345534
// then assume the frontend took care of the copying and pass it by ref
5535+
if (arg.rvalue) // marked with __rvalue
5536+
copy = false;
55355537

55365538
elems[i] = addressElem(ea, arg.type, copy);
55375539
continue;

0 commit comments

Comments
 (0)