]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
d: RVO/NRVO not done when returning a copy constructor
authorIain Buclaw <ibuclaw@gdcproject.org>
Wed, 28 Jan 2026 11:45:26 +0000 (12:45 +0100)
committerIain Buclaw <ibuclaw@gdcproject.org>
Wed, 28 Jan 2026 14:50:00 +0000 (15:50 +0100)
RVO was already being done when returning a constructor call via a
temporary, which in the front-end AST looks like:

    return S.this((__tmp = {}, &__tmp), args...);

But this was not being done for copy construction calls, which instead
looks like:

    return __copytmp = {}, S.this(&__copytmp, V(1).s), __copytmp;

This pattern is now matched and the temporary gets set up as a
DECL_VALUE_EXPR of the RESULT_DECL.

PR d/123422

gcc/d/ChangeLog:

* expr.cc (ExprVisitor::visit (ArrayLiteralExp *)): Don't add
TARGET_EXPR around constructor.
* toir.cc (IRVisitor::visit (ReturnStatement *)): Recognize more
patterns for return value optimization.

gcc/testsuite/ChangeLog:

* gdc.dg/torture/pr123422.d: New test.

gcc/d/expr.cc
gcc/d/toir.cc
gcc/testsuite/gdc.dg/torture/pr123422.d [new file with mode: 0644]

index bc1f788d0221c637d3ce0490f12f05e591a8f27d..79d1839f0d6c7050c9bea1dc1523ccfefe029c50 100644 (file)
@@ -2675,7 +2675,7 @@ public:
        gcc_assert (tb->ty == TY::Tarray);
        ctor = force_target_expr (ctor);
        ctor = d_array_value (type, size_int (e->elements->length),
-                             build_address (force_target_expr (ctor)));
+                             build_address (ctor));
        this->result_ = compound_expr (saved_elems, ctor);
       }
     else
index a58b5963c0258323e3d6c8ffefbc586afb1cdaa1..462977f817396beed7848c205ea59f79c33045a4 100644 (file)
@@ -1022,41 +1022,64 @@ public:
        /* Detect a call to a constructor function, or if returning a struct
           literal, write result directly into the return value.  */
        StructLiteralExp *sle = NULL;
+       DeclarationExp *de = NULL;
+       VarExp *ve = NULL;
        bool using_rvo_p = false;
 
        if (DotVarExp *dve = (s->exp->isCallExp ()
                              ? s->exp->isCallExp ()->e1->isDotVarExp ()
                              : NULL))
          {
+           /* Look for `var.__ctor(copytmp = {}, &copytmp)'  */
            if (dve->var->isCtorDeclaration ())
              {
                if (CommaExp *ce = dve->e1->isCommaExp ())
                  {
-                   /* Temporary initialized inside a return expression, and
-                      used as the return value.  Replace it with the hidden
-                       reference to allow RVO return.  */
-                   DeclarationExp *de = ce->e1->isDeclarationExp ();
-                   VarExp *ve = ce->e2->isVarExp ();
-                   if (de != NULL && ve != NULL
-                       && ve->var == de->declaration
-                       && ve->var->storage_class & STCtemp)
-                     {
-                       tree var = get_symbol_decl (ve->var);
-                       TREE_ADDRESSABLE (var) = 1;
-                       SET_DECL_VALUE_EXPR (var, decl);
-                       DECL_HAS_VALUE_EXPR_P (var) = 1;
-                       SET_DECL_LANG_NRVO (var, this->func_->shidden);
-                       using_rvo_p = true;
-                     }
+                   de = ce->e1->isDeclarationExp ();
+                   ve = ce->e2->isVarExp ();
                  }
                else
                  sle = dve->e1->isStructLiteralExp ();
              }
          }
+       else if (CommaExp *ce1 = s->exp->isCommaExp ())
+         {
+           /* Look for `copytmp = {}, copytmp.__ctor(), copytmp'  */
+           if (CommaExp *ce2 = ce1->e2->isCommaExp ())
+             {
+               DotVarExp *dve = ce2->e1->isCallExp ()
+                 ? ce2->e1->isCallExp ()->e1->isDotVarExp () : NULL;
+
+               if (dve && dve->var->isCtorDeclaration ())
+                 {
+                   de = ce1->e1->isDeclarationExp ();
+                   ve = ce2->e2->isVarExp ();
+                 }
+             }
+           else
+             {
+               de = ce1->e1->isDeclarationExp ();
+               ve = ce1->e2->isVarExp ();
+             }
+         }
        else
          sle = s->exp->isStructLiteralExp ();
 
-       if (sle != NULL)
+       if (de != NULL && ve != NULL
+           && ve->var == de->declaration
+           && ve->var->storage_class & STCtemp)
+         {
+           /* Temporary initialized inside a return expression, and
+              used as the return value.  Replace it with the hidden
+              reference to allow RVO return.  */
+           tree var = get_symbol_decl (ve->var);
+           TREE_ADDRESSABLE (var) = 1;
+           SET_DECL_VALUE_EXPR (var, decl);
+           DECL_HAS_VALUE_EXPR_P (var) = 1;
+           SET_DECL_LANG_NRVO (var, this->func_->shidden);
+           using_rvo_p = true;
+         }
+       else if (sle != NULL)
          {
            sle->sym = build_address (this->func_->shidden);
            using_rvo_p = true;
diff --git a/gcc/testsuite/gdc.dg/torture/pr123422.d b/gcc/testsuite/gdc.dg/torture/pr123422.d
new file mode 100644 (file)
index 0000000..468a953
--- /dev/null
@@ -0,0 +1,25 @@
+// { dg-do run }
+// { dg-skip-if "needs gcc/config.d" { ! d_runtime } }
+struct S123422
+{
+    S123422* ptr;
+    this(int) { ptr = &this; }
+    this(ref inout S123422) { ptr = &this; }
+}
+
+struct V123422
+{
+    S123422 s;
+    this(int) { s = S123422(1); }
+}
+
+S123422 foo()
+{
+    return V123422(1).s;
+}
+
+void main()
+{
+    S123422 s = foo();
+    assert(&s == s.ptr);
+}