diff --git a/changelog/dmd.rvalue.dd b/changelog/dmd.rvalue.dd index f01d939236..1c33f36d20 100644 --- a/changelog/dmd.rvalue.dd +++ b/changelog/dmd.rvalue.dd @@ -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`. diff --git a/compiler/src/dmd/expressionsem.d b/compiler/src/dmd/expressionsem.d index 81c1c5bda4..83f28be96a 100644 --- a/compiler/src/dmd/expressionsem.d +++ b/compiler/src/dmd/expressionsem.d @@ -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 diff --git a/compiler/src/dmd/frontend.h b/compiler/src/dmd/frontend.h index dd39b74672..557d9bb7f3 100644 --- a/compiler/src/dmd/frontend.h +++ b/compiler/src/dmd/frontend.h @@ -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: diff --git a/compiler/src/dmd/mtype.d b/compiler/src/dmd/mtype.d index 63663dca99..5ba866c9e5 100644 --- a/compiler/src/dmd/mtype.d +++ b/compiler/src/dmd/mtype.d @@ -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; diff --git a/compiler/src/dmd/parse.d b/compiler/src/dmd/parse.d index d75597ed3c..3d25464fc5 100644 --- a/compiler/src/dmd/parse.d +++ b/compiler/src/dmd/parse.d @@ -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;