fix issue 20562 returning __rvalue should move-construct the NRVO value (#20585)

This commit is contained in:
Walter Bright 2024-12-22 21:08:50 -08:00 committed by GitHub
parent 6963395dc1
commit cdc7334e44
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 96 additions and 6 deletions

View file

@ -818,20 +818,56 @@ extern(D) bool arrayExpressionSemantic(
* e = the expression the needs to be moved or copied (source)
* t = if the struct defines a copy constructor, the type of the destination (can be NULL)
* nrvo = true if the generated copy can be treated as NRVO
*
* move = true to allow a move constructor to be used, false to prevent infinite recursion
* Returns:
* The expression that copy constructs or moves the value.
*/
extern (D) Expression doCopyOrMove(Scope *sc, Expression e, Type t, bool nrvo)
extern (D) Expression doCopyOrMove(Scope *sc, Expression e, Type t, bool nrvo, bool move = false)
{
//printf("doCopyOrMove() %s\n", toChars(e));
StructDeclaration sd;
if (t)
{
if (auto ts = t.isTypeStruct())
sd = ts.sym;
}
if (auto ce = e.isCondExp())
{
ce.e1 = doCopyOrMove(sc, ce.e1, null, nrvo);
ce.e2 = doCopyOrMove(sc, ce.e2, null, nrvo);
}
else if (e.isLvalue())
{
e = callCpCtor(sc, e, t, nrvo);
}
else if (move && sd && sd.hasMoveCtor && !e.isCallExp() && !e.isStructLiteralExp())
{
// #move
/* Rewrite as:
* S __copyrvalue;
* __copyrvalue.moveCtor(e);
* __copyrvalue;
*/
VarDeclaration vd = new VarDeclaration(e.loc, e.type, Identifier.generateId("__copyrvalue"), null);
if (nrvo)
vd.adFlags |= Declaration.nrvo;
vd.storage_class |= STC.nodtor;
vd.dsymbolSemantic(sc);
Expression de = new DeclarationExp(e.loc, vd);
Expression ve = new VarExp(e.loc, vd);
Expression er;
er = new DotIdExp(e.loc, ve, Id.ctor); // ve.ctor
er = new CallExp(e.loc, er, e); // ve.ctor(e)
er = new CommaExp(e.loc, er, new VarExp(e.loc, vd)); // ve.ctor(e),vd
er = Expression.combine(de, er); // de,ve.ctor(e),vd
e = er.expressionSemantic(sc);
}
else
{
e = e.isLvalue() ? callCpCtor(sc, e, t, nrvo) : valueNoDtor(e);
e = valueNoDtor(e);
}
return e;
}
@ -844,7 +880,7 @@ extern (D) Expression doCopyOrMove(Scope *sc, Expression e, Type t, bool nrvo)
* sc = just used to specify the scope of created temporary variable
* destinationType = the type of the object on which the copy constructor is called;
* may be null if the struct defines a postblit
* nrvo = true if the generated copy can be treated as NRVO
* nrvo = true if the generated copy can be treated as NRVO
*/
private Expression callCpCtor(Scope* sc, Expression e, Type destinationType, bool nrvo)
{
@ -865,7 +901,7 @@ private Expression callCpCtor(Scope* sc, Expression e, Type destinationType, boo
*/
VarDeclaration tmp = copyToTemp(STC.rvalue, "__copytmp", e);
if (nrvo)
tmp.adFlags |= Declaration.nrvo;
tmp.adFlags |= Declaration.nrvo;
if (sd.hasCopyCtor && destinationType)
{
// https://issues.dlang.org/show_bug.cgi?id=22619
@ -893,6 +929,7 @@ private Expression callCpCtor(Scope* sc, Expression e, Type destinationType, boo
*/
Expression valueNoDtor(Expression e)
{
//printf("valueNoDtor() %s\n", toChars(e));
auto ex = lastComma(e);
if (auto ce = ex.isCallExp())
@ -10938,6 +10975,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
}
else if (sd.hasMoveCtor && !e2x.isCallExp() && !e2x.isStructLiteralExp())
{
// #move
/* The !e2x.isCallExp() is because it is already an rvalue
and the move constructor is unnecessary:
struct S {

View file

@ -940,7 +940,7 @@ private extern(C++) final class Semantic3Visitor : Visitor
* If NRVO is not possible, all returned lvalues should call their postblits.
*/
if (!funcdecl.isNRVO())
exp = doCopyOrMove(sc2, exp, f.next, true);
exp = doCopyOrMove(sc2, exp, f.next, true, true);
if (tret.hasPointers())
checkReturnEscape(*sc2, exp, false);

View file

@ -139,6 +139,57 @@ void test7()
StringTest s = StringTest(null);
}
/********************************/
// https://github.com/dlang/dmd/issues/20562
struct S8
{
int a,b;
this(S8) { printf("this(S)\n"); b = 4; }
this(ref S8) { printf("this(ref S)\n"); assert(0); }
}
S8 returnRval(ref S8 arg)
{
static if (0)
{
/* __copytmp2 = 0 ;
_D7rvalue51S6__ctorMFNcKSQxQrZQg call (arg param #__copytmp2);
* __HID1 streq 1 __copytmp2;
__HID1;
*/
return arg;
}
else static if (1)
{
/* * __HID1 streq 1 * arg;
__HID1;
*/
return __rvalue(arg); // should move-construct the NRVO value
}
else
{
/* * t = 0 ;
t;
_TMP0 = t;
_D7rvalue51S6__ctorMFNcSQwQqZQg call (arg param _TMP0);
t;
*/
S8 t = __rvalue(arg);
return t;
}
}
void test8()
{
S8 s;
S8 t = returnRval(s);
printf("t.b: %d\n", t.b);
assert(t.b == 4);
}
/********************************/
int main()
@ -150,6 +201,7 @@ int main()
test5();
test6();
test7();
test8();
return 0;
}