On 10/8/26 8:37 PM, Marek Polacek wrote:
Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

OK.
-- >8 --
Here we crash on

   auto foo = [] {} = {};

on the assert in build_over_call:

   gcc_assert (TREE_CODE (arg) != TARGET_EXPR
              || force_elide
              /* It's from binding the ref parm to a packed field. */
              || convs[0]->need_temporary_p
              || ... )

which makes sure that we are initializing directly from class prvalues
rather than copying them.  The problem is that we created
*&TARGET_EXPR <D.2704, {}> in

       if (is_really_empty_class (type, /*ignore_vptr*/true))
         {
           /* Avoid copying empty classes, but ensure op= returns an lvalue even
              if the object argument isn't one.  */
           to = force_lvalue (to, complain);
           val = build2 (COMPOUND_EXPR, type, arg, to);
           suppress_warning (val, OPT_Wunused);
         }

where we want an lvalue, but then in cp_build_addr_expr_1 (Let &* cancel out
to simplify resulting code) we get &TARGET_EXPR and then build_over_call
strips the & and we end up with a prvalue.

The test is actually ([]{}).operator=({}) where binding the []{}
object materializes a temporary, operator= returns an lvalue and foo is
copy-initialized from that lvalue.

My fix is to follow what cp_build_modify_expr (and other places) do:
build up a COMPOUND_EXPR and use the TARGET_EXPR_SLOT so that the
TARGET_EXPR can't become a prvalue.

        PR c++/127710

gcc/cp/ChangeLog:

        * cvt.cc (force_lvalue): For a TARGET_EXPR, build
        (TARGET_EXPR<...>, slot).

gcc/testsuite/ChangeLog:

        * g++.dg/cpp2a/class-copy1.C: New test.
        * g++.dg/cpp2a/lambda-assign1.C: New test.
---
  gcc/cp/cvt.cc                               | 10 ++++++-
  gcc/testsuite/g++.dg/cpp2a/class-copy1.C    | 29 +++++++++++++++++++++
  gcc/testsuite/g++.dg/cpp2a/lambda-assign1.C | 14 ++++++++++
  3 files changed, 52 insertions(+), 1 deletion(-)
  create mode 100644 gcc/testsuite/g++.dg/cpp2a/class-copy1.C
  create mode 100644 gcc/testsuite/g++.dg/cpp2a/lambda-assign1.C

diff --git a/gcc/cp/cvt.cc b/gcc/cp/cvt.cc
index ff54647c5b1..845719c2043 100644
--- a/gcc/cp/cvt.cc
+++ b/gcc/cp/cvt.cc
@@ -580,7 +580,15 @@ force_rvalue (tree expr, tsubst_flags_t complain)
  tree
  force_lvalue (tree expr, tsubst_flags_t complain)
  {
-  if (!lvalue_p (expr))
+  if (lvalue_p (expr))
+    return expr;
+
+  /* If we build up *&TARGET_EXPR, we have an lvalue, but it could be
+     folded back to a prvalue by removing the *&.  */
+  if (TREE_CODE (expr) == TARGET_EXPR)
+    expr = build2 (COMPOUND_EXPR, TREE_TYPE (expr), expr,
+                  TARGET_EXPR_SLOT (expr));
+  else
      {
        expr = cp_build_addr_expr (expr, complain);
        expr = cp_build_indirect_ref (input_location, expr, RO_ARROW, complain);
diff --git a/gcc/testsuite/g++.dg/cpp2a/class-copy1.C 
b/gcc/testsuite/g++.dg/cpp2a/class-copy1.C
new file mode 100644
index 00000000000..95d7e028f74
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/class-copy1.C
@@ -0,0 +1,29 @@
+// PR c++/127710
+// { dg-do run { target c++20 } }
+
+struct S { int i; };
+struct A {
+  A() = default;
+  A(int i, char c) : i(i), c(c) {}
+  int i;
+  char c;
+};
+struct B : A { };
+// Try an empty closure as a base.
+struct W : decltype([] {}) { int i; };
+
+int
+main ()
+{
+  S s = S{} = S{1};
+  if (s.i != 1)
+    __builtin_abort ();
+
+  B b = B{} = B{{2, 'x'}};
+  if (b.i != 2 || b.c != 'x')
+    __builtin_abort ();
+
+  W w = W{} = W{{}, 3};
+  if (w.i != 3)
+    __builtin_abort ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/lambda-assign1.C 
b/gcc/testsuite/g++.dg/cpp2a/lambda-assign1.C
new file mode 100644
index 00000000000..007219a3ba4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/lambda-assign1.C
@@ -0,0 +1,14 @@
+// PR c++/127710
+// { dg-do compile { target c++20 } }
+
+auto l1 = [] {} = {};
+void
+g ()
+{
+  auto l2 = [] {} = {};
+  // [expr.prim.lambda.closure]/17: The closure type associated with
+  // a lambda-expression [...] has a deleted copy assignment operator
+  // if the lambda-expression has a lambda-capture.
+  int i = 9;
+  auto l3 = [i] {} = {};  // { dg-error "use of deleted function" }
+}

base-commit: fb8a2387e35a92e5339df5f7dd434ad14f043d09

Reply via email to