fix Issue 20567 returning the result of a constructor should be NRVO (#20568)

This commit is contained in:
Walter Bright 2024-12-18 00:48:52 -08:00 committed by GitHub
parent 85fe931daa
commit b7a3a1942c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 53 additions and 19 deletions

View file

@ -95,6 +95,7 @@ extern (C++) abstract class Declaration : Dsymbol
enum ignoreRead = 2; // ignore any reads of AliasDeclaration enum ignoreRead = 2; // ignore any reads of AliasDeclaration
enum nounderscore = 4; // don't prepend _ to mangled name enum nounderscore = 4; // don't prepend _ to mangled name
enum hidden = 8; // don't print this in .di files enum hidden = 8; // don't print this in .di files
enum nrvo = 0x10; /// forward to fd.nrvo_var when generating code
// overridden symbol with pragma(mangle, "...") // overridden symbol with pragma(mangle, "...")
const(char)[] mangleOverride; const(char)[] mangleOverride;

View file

@ -632,7 +632,7 @@ elem* toElem(Expression e, ref IRState irs)
if (se.var.toParent2()) if (se.var.toParent2())
fd = se.var.toParent2().isFuncDeclaration(); fd = se.var.toParent2().isFuncDeclaration();
const bool nrvo = fd && fd.isNRVO() && fd.nrvo_var == se.var; const bool nrvo = fd && (fd.isNRVO() && fd.nrvo_var == se.var || se.var.adFlags & Declaration.nrvo && fd.shidden);
if (nrvo) if (nrvo)
s = fd.shidden; s = fd.shidden;

View file

@ -816,21 +816,22 @@ extern(D) bool arrayExpressionSemantic(
* Params: * Params:
* sc = the scope where the expression is encountered * sc = the scope where the expression is encountered
* e = the expression the needs to be moved or copied (source) * e = the expression the needs to be moved or copied (source)
* t = if the struct defines a copy constructor, the type of the destination * 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
* *
* Returns: * Returns:
* The expression that copy constructs or moves the value. * The expression that copy constructs or moves the value.
*/ */
extern (D) Expression doCopyOrMove(Scope *sc, Expression e, Type t = null) extern (D) Expression doCopyOrMove(Scope *sc, Expression e, Type t, bool nrvo)
{ {
if (auto ce = e.isCondExp()) if (auto ce = e.isCondExp())
{ {
ce.e1 = doCopyOrMove(sc, ce.e1); ce.e1 = doCopyOrMove(sc, ce.e1, null, nrvo);
ce.e2 = doCopyOrMove(sc, ce.e2); ce.e2 = doCopyOrMove(sc, ce.e2, null, nrvo);
} }
else else
{ {
e = e.isLvalue() ? callCpCtor(sc, e, t) : valueNoDtor(e); e = e.isLvalue() ? callCpCtor(sc, e, t, nrvo) : valueNoDtor(e);
} }
return e; return e;
} }
@ -839,12 +840,13 @@ extern (D) Expression doCopyOrMove(Scope *sc, Expression e, Type t = null)
* If e is an instance of a struct, and that struct has a copy constructor, * If e is an instance of a struct, and that struct has a copy constructor,
* rewrite e as: * rewrite e as:
* (tmp = e),tmp * (tmp = e),tmp
* Input: * Params:
* sc = just used to specify the scope of created temporary variable * sc = just used to specify the scope of created temporary variable
* destinationType = the type of the object on which the copy constructor is called; * destinationType = the type of the object on which the copy constructor is called;
* may be null if the struct defines a postblit * may be null if the struct defines a postblit
* nrvo = true if the generated copy can be treated as NRVO
*/ */
private Expression callCpCtor(Scope* sc, Expression e, Type destinationType) private Expression callCpCtor(Scope* sc, Expression e, Type destinationType, bool nrvo)
{ {
//printf("callCpCtor(e: %s et: %s destinationType: %s\n", toChars(e), toChars(e.type), toChars(destinationType)); //printf("callCpCtor(e: %s et: %s destinationType: %s\n", toChars(e), toChars(e.type), toChars(destinationType));
auto ts = e.type.baseElemOf().isTypeStruct(); auto ts = e.type.baseElemOf().isTypeStruct();
@ -861,7 +863,9 @@ private Expression callCpCtor(Scope* sc, Expression e, Type destinationType)
* This is not the most efficient, ideally tmp would be constructed * This is not the most efficient, ideally tmp would be constructed
* directly onto the stack. * directly onto the stack.
*/ */
auto tmp = copyToTemp(STC.rvalue, "__copytmp", e); VarDeclaration tmp = copyToTemp(STC.rvalue, "__copytmp", e);
if (nrvo)
tmp.adFlags |= Declaration.nrvo;
if (sd.hasCopyCtor && destinationType) if (sd.hasCopyCtor && destinationType)
{ {
// https://issues.dlang.org/show_bug.cgi?id=22619 // https://issues.dlang.org/show_bug.cgi?id=22619
@ -2707,7 +2711,7 @@ private Type arrayExpressionToCommonType(Scope* sc, ref Expressions exps)
continue; continue;
} }
e = doCopyOrMove(sc, e); e = doCopyOrMove(sc, e, null, false);
if (!foundType && t0 && !t0.equals(e.type)) if (!foundType && t0 && !t0.equals(e.type))
{ {
@ -3678,7 +3682,7 @@ private bool functionParameters(const ref Loc loc, Scope* sc,
*/ */
Type tv = arg.type.baseElemOf(); Type tv = arg.type.baseElemOf();
if (!isRef && tv.ty == Tstruct) if (!isRef && tv.ty == Tstruct)
arg = doCopyOrMove(sc, arg, parameter ? parameter.type : null); arg = doCopyOrMove(sc, arg, parameter ? parameter.type : null, false);
} }
(*arguments)[i] = arg; (*arguments)[i] = arg;
@ -12026,7 +12030,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
ce.trusted = true; ce.trusted = true;
exp = new CatElemAssignExp(exp.loc, exp.type, exp.e1, ecast); exp = new CatElemAssignExp(exp.loc, exp.type, exp.e1, ecast);
exp.e2 = doCopyOrMove(sc, exp.e2); exp.e2 = doCopyOrMove(sc, exp.e2, null, false);
} }
else if (tb1.ty == Tarray && else if (tb1.ty == Tarray &&
(tb1next.ty == Tchar || tb1next.ty == Twchar) && (tb1next.ty == Tchar || tb1next.ty == Twchar) &&
@ -12651,7 +12655,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{ {
if (exp.e1.op == EXP.arrayLiteral) if (exp.e1.op == EXP.arrayLiteral)
{ {
exp.e2 = doCopyOrMove(sc, exp.e2); exp.e2 = doCopyOrMove(sc, exp.e2, null, false);
// https://issues.dlang.org/show_bug.cgi?id=14686 // https://issues.dlang.org/show_bug.cgi?id=14686
// Postblit call appears in AST, and this is // Postblit call appears in AST, and this is
// finally translated to an ArrayLiteralExp in below optimize(). // finally translated to an ArrayLiteralExp in below optimize().
@ -12690,7 +12694,7 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor
{ {
if (exp.e2.op == EXP.arrayLiteral) if (exp.e2.op == EXP.arrayLiteral)
{ {
exp.e1 = doCopyOrMove(sc, exp.e1); exp.e1 = doCopyOrMove(sc, exp.e1, null, false);
} }
else if (exp.e2.op == EXP.string_) else if (exp.e2.op == EXP.string_)
{ {
@ -16552,7 +16556,7 @@ private bool fit(StructDeclaration sd, const ref Loc loc, Scope* sc, Expressions
if (e.op == EXP.error) if (e.op == EXP.error)
return false; return false;
(*elements)[i] = doCopyOrMove(sc, e); (*elements)[i] = doCopyOrMove(sc, e, null, false);
} }
return true; return true;
} }

View file

@ -6567,6 +6567,8 @@ public:
enum : int32_t { hidden = 8 }; enum : int32_t { hidden = 8 };
enum : int32_t { nrvo = 16 };
_d_dynamicArray< const char > mangleOverride; _d_dynamicArray< const char > mangleOverride;
const char* kind() const override; const char* kind() const override;
uinteger_t size(const Loc& loc) final override; uinteger_t size(const Loc& loc) final override;

View file

@ -2962,6 +2962,7 @@ extern (D) void checkMain(FuncDeclaration fd)
*/ */
extern (D) bool checkNRVO(FuncDeclaration fd) extern (D) bool checkNRVO(FuncDeclaration fd)
{ {
//printf("checkNRVO*() %s\n", fd.ident.toChars());
if (!fd.isNRVO() || fd.returns is null) if (!fd.isNRVO() || fd.returns is null)
return false; return false;

View file

@ -1630,7 +1630,7 @@ Expressions* resolveStructLiteralNamedArgs(StructDeclaration sd, Type t, Scope*
continue; continue;
} }
elems[fieldi] = doCopyOrMove(sc, ex); elems[fieldi] = doCopyOrMove(sc, ex, null, false);
++fieldi; ++fieldi;
} }
if (errors) if (errors)

View file

@ -534,7 +534,7 @@ void Statement_toIR(Statement s, ref IRState irs, StmtState* stmtstate)
void visitReturn(ReturnStatement s) void visitReturn(ReturnStatement s)
{ {
//printf("s2ir.ReturnStatement: %s\n", s.toChars()); //printf("s2ir.ReturnStatement: %s\n", toChars(s.exp));
BlockState *blx = irs.blx; BlockState *blx = irs.blx;
BC bc; BC bc;

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 NRVO is not possible, all returned lvalues should call their postblits.
*/ */
if (!funcdecl.isNRVO()) if (!funcdecl.isNRVO())
exp = doCopyOrMove(sc2, exp, f.next); exp = doCopyOrMove(sc2, exp, f.next, true);
if (tret.hasPointers()) if (tret.hasPointers())
checkReturnEscape(*sc2, exp, false); checkReturnEscape(*sc2, exp, false);

View file

@ -2460,7 +2460,7 @@ Statement statementSemanticVisit(Statement s, Scope* sc)
/* https://dlang.org/spec/statement.html#return-statement /* https://dlang.org/spec/statement.html#return-statement
*/ */
//printf("ReturnStatement.dsymbolSemantic() %p, %s\n", rs, rs.toChars()); //printf("ReturnStatement.dsymbolSemantic() %s\n", toChars(rs));
FuncDeclaration fd = sc.parent.isFuncDeclaration(); FuncDeclaration fd = sc.parent.isFuncDeclaration();
if (fd.fes) if (fd.fes)

View file

@ -22,9 +22,35 @@ void test1()
assert(&r == s1ptr); assert(&r == s1ptr);
} }
/***************************************************/
// https://github.com/dlang/dmd/issues/20567
struct S2
{
int x;
this(ref S2 s) { x = s.x; }
}
S2 returnRval(ref S2 arg1, ref S2 arg2, int i)
{
return i ? arg1 : arg2;
}
void test2()
{
S2 s1, s2;
s1.x = 3;
s2.x = 4;
S2 s = returnRval(s1, s2, 0);
assert(s.x == 4);
s = returnRval(s1, s2, 1);
assert(s.x == 3);
}
/***************************************************/ /***************************************************/
void main() void main()
{ {
test1(); test1();
test2();
} }