Accept __rvalue attribute on ref functions; which will force the result to be treated as __rvalue. (#20946)

This is essential to implement `move`, `forward`, etc.
This commit is contained in:
Manu Evans 2025-03-10 21:06:29 +10:00
parent c6e387c448
commit 603225372b
5 changed files with 44 additions and 5 deletions

View file

@ -23,5 +23,18 @@ This also applies to constructors and assignments, meaning move constructors and
move assignments are enabled. Moving instead of copying can be much more resource
efficient, as, say, a string can be moved rather than copied/deleted.
A moved object can still be destructed, so take that into account when moving
A moved object will still be destructed, so take that into account when moving
a field - set it to a benign value that can be destructed.
`__rvalue` may also be used as an attribute on a function which returns by ref
to declare that the result should be treated as an rvalue at the callsite:
```
ref T move(T)(return ref T source) __rvalue
{
return source;
}
S s;
S t = move(s); // call expression rewritten as: S t = __rvalue(move(s))
```
This is used as an internal tool to implement library primitives such as `move` and `forward`.

View file

@ -5893,6 +5893,19 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
printf("CallExp::semantic() %s\n", exp.toChars());
}
scope (exit)
{
if (TypeFunction tf = exp.f ? cast(TypeFunction)exp.f.type : null)
{
result.rvalue = tf.isRvalue;
if (tf.isRvalue && !tf.isRef)
{
error(exp.f.loc, "`__rvalue` only valid on functions that return by `ref`");
setError();
}
}
}
Objects* tiargs = null; // initial list of template arguments
Expression ethis = null;
Type tthis = null;
@ -10839,7 +10852,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
return;
}
}
else if (sd.hasMoveCtor && !e2x.isCallExp() && !e2x.isStructLiteralExp())
else if (sd.hasMoveCtor && (!e2x.isCallExp() || e2x.rvalue) && !e2x.isStructLiteralExp())
{
// #move
/* The !e2x.isCallExp() is because it is already an rvalue

View file

@ -4596,6 +4596,7 @@ private:
bool isInOutQual;
bool isCtor;
bool isReturnScope;
bool isRvalue;
BitFields() :
isNothrow(),
isNogc(),
@ -4610,10 +4611,11 @@ private:
isInOutParam(),
isInOutQual(),
isCtor(),
isReturnScope()
isReturnScope(),
isRvalue()
{
}
BitFields(bool isNothrow, bool isNogc = false, bool isProperty = false, bool isRef = false, bool isReturn = false, bool isScopeQual = false, bool isReturnInferred = false, bool isScopeInferred = false, bool isLive = false, bool incomplete = false, bool isInOutParam = false, bool isInOutQual = false, bool isCtor = false, bool isReturnScope = false) :
BitFields(bool isNothrow, bool isNogc = false, bool isProperty = false, bool isRef = false, bool isReturn = false, bool isScopeQual = false, bool isReturnInferred = false, bool isScopeInferred = false, bool isLive = false, bool incomplete = false, bool isInOutParam = false, bool isInOutQual = false, bool isCtor = false, bool isReturnScope = false, bool isRvalue = false) :
isNothrow(isNothrow),
isNogc(isNogc),
isProperty(isProperty),
@ -4627,7 +4629,8 @@ private:
isInOutParam(isInOutParam),
isInOutQual(isInOutQual),
isCtor(isCtor),
isReturnScope(isReturnScope)
isReturnScope(isReturnScope),
isRvalue(isRvalue)
{}
};
@ -4660,6 +4663,8 @@ public:
bool isCtor(bool v);
bool isReturnScope() const;
bool isReturnScope(bool v);
bool isRvalue() const;
bool isRvalue(bool v);
private:
uint16_t bitFields;
public:

View file

@ -2488,6 +2488,7 @@ extern (C++) final class TypeFunction : TypeNext
bool isInOutQual; /// inout on the qualifier
bool isCtor; /// the function is a constructor
bool isReturnScope; /// `this` is returned by value
bool isRvalue; /// returned reference should be treated as rvalue
}
import dmd.common.bitfields : generateBitFields;
@ -2531,6 +2532,8 @@ extern (C++) final class TypeFunction : TypeNext
this.isScopeQual = true;
if (stc & STC.scopeinferred)
this.isScopeInferred = true;
if (stc & STC.rvalue)
this.isRvalue = true;
this.trust = TRUST.default_;
if (stc & STC.safe)
@ -2567,6 +2570,7 @@ extern (C++) final class TypeFunction : TypeNext
t.isScopeQual = isScopeQual;
t.isReturnInferred = isReturnInferred;
t.isScopeInferred = isScopeInferred;
t.isRvalue = isRvalue;
t.isInOutParam = isInOutParam;
t.isInOutQual = isInOutQual;
t.trust = trust;

View file

@ -1422,6 +1422,10 @@ class Parser(AST, Lexer = dmd.lexer.Lexer) : Lexer
stc = STC.scope_;
break;
case TOK.rvalue:
stc = STC.rvalue;
break;
case TOK.at:
{
AST.Expressions* udas = null;