On 2/15/26 12:13 AM, Patrick Palka wrote:
On Sat, 14 Feb 2026, Jason Merrill wrote:

On 2/13/26 12:56 AM, Patrick Palka wrote:


On Tue, 10 Feb 2026, Jason Merrill wrote:

On 2/9/26 8:01 AM, Patrick Palka wrote:
On Sun, 8 Feb 2026, Jason Merrill wrote:

On 2/8/26 1:44 AM, Patrick Palka wrote:
Bootstrapped and regtested on x86_64-pc-linux-gnu, does this
look OK for stage1 perhaps?

FWIW I couldn't get a structured binding (which are also implemented
via
DECL_VALUE_EXPR) testcase to misbehave, because for e.g.

      template<class T>
      void f(T t) {
        auto [x,y] = t;
        ...
      }

we lower it to:

      struct A D.2730;
      int x [value-expr: D.2730.n];
      int y [value-expr: D.2730.m];
      ...
      A::A (&D.2730, t);

i.e. the value-exprs are in terms of a local copy of the invisiref
parm
so they don't need adjustment.  Whereas for this PR the value-exprs
are
directly in terms of the invisiref parm.

For auto& [x,y] = t; we do:

      struct A & D.2720;
      int x [value-expr: D.2720->n];
      int y [value-expr: D.2720->m];
      D.2720 = t;

-- >8 --

Here the lambda has a by-value capture of non-trivial type, which
in turn makes the closure type non-trivial.  This means its by-value
'this' parameter, which gets deduced to the closure type, becomes an
invisiref parameter, and so when lowering the operator() body we
need to
adjust uses of 'this' by adding implicit dereferences.

But the GIMPLE dump for operator() shows that we miss some
adjustments:

      bool main()::<lambda(this
auto:1)>::operator()<main()::<lambda(this
auto:1)> > (struct ._anon_0 & self)
      {
        bool D.3091;
        struct ._anon_0 & self.1;
        struct A data [value-expr: self.__data]; // should be
self->__data

        self.1 = self;
        _1 = self.1.__data.n; // same
        D.3091 = _1 == 42;
        return D.3091;
      }

Apparently this is because cp_genericize_r, which is responsible for
the
invisiref use adjustments, never walks DECL_VALUE_EXPR.  This patch
makes
us walk it.

        PR c++/121500

gcc/cp/ChangeLog:

        * cp-gimplify.cc (cp_genericize_r): Walk DECL_VALUE_EXPR.

gcc/testsuite/ChangeLog:

        * g++.dg/cpp23/explicit-obj-lambda20.C: New test.
---
     gcc/cp/cp-gimplify.cc                            | 10 ++++++++++
     .../g++.dg/cpp23/explicit-obj-lambda20.C         | 16
++++++++++++++++
     2 files changed, 26 insertions(+)
     create mode 100644
gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda20.C

diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc
index 9d96ce99ea92..11fa6cff40ba 100644
--- a/gcc/cp/cp-gimplify.cc
+++ b/gcc/cp/cp-gimplify.cc
@@ -1914,6 +1914,16 @@ cp_genericize_r (tree *stmt_p, int
*walk_subtrees,
void *data)
           return NULL_TREE;
         }
     +  if (TREE_CODE (stmt) == VAR_DECL
+      || TREE_CODE (stmt) == PARM_DECL
+      || TREE_CODE (stmt) == RESULT_DECL)
+    if (tree ve = DECL_VALUE_EXPR (stmt))

Usually this is conditional on DECL_HAS_VALUE_EXPR (but why doesn't
decl_value_expr_lookup check the flag?)

Fixed.  Good question...


+      {
+       cp_walk_tree (&ve, cp_genericize_r, data, NULL);
+       SET_DECL_VALUE_EXPR (stmt, ve);
+       p_set->add (ve);

Won't the tree walk do this already?  If not, needs a comment.

Whoops, I got confused by the fact that DECL_VALUE_EXPR doesn't return
an
lvalue, but still we can just handle it like we handle any other
subtree.
Fixed.  I also slightly simplified the testcase to use a simple-capture
instead of init-capture.

OK.

Thanks; for stage1, right?

This patch for stage 1; for current trunk I think we could include it limited
to C++23 and above.

Since we support xobj lambdas as an extension in earlier modes, better
might be to limit it to decls within xobj lambda bodies?  Like so:

OK.

-- >8 --

Subject: [PATCH] c++: non-trivial by-value deducing this lambda [PR121500]

Here the lambda has a by-value capture of non-trivial type, which
in turn makes the closure type non-trivial.  This means its by-value
'this' parameter, which gets deduced to the closure type, becomes an
invisiref parameter, and so when lowering the operator() body we need to
adjust uses of 'this' by adding implicit dereferences.

But the GIMPLE dump for operator() shows that we miss some adjustments:

   bool main()::<lambda(this auto:1)>::operator()<main()::<lambda(this auto:1)> > 
(struct ._anon_0 & self)
   {
     bool D.3091;
     struct ._anon_0 & self.1;
     struct A a [value-expr: self.__a]; // should be self->__a

     self.1 = self;
     _1 = self.1.__a.n; // same
     D.3091 = _1 == 42;
     return D.3091;
   }

Apparently this is because cp_genericize_r, which is responsible for the
invisiref use adjustments, never walks DECL_VALUE_EXPR.  This patch makes
us walk it.  For stage4, restrict the walking to xobj lambdas.

        PR c++/121500

gcc/cp/ChangeLog:

        * cp-gimplify.cc (cp_genericize_r): Walk DECL_VALUE_EXPR within
        an xobj lambda.

gcc/testsuite/ChangeLog:

        * g++.dg/cpp23/explicit-obj-lambda20.C: New test.
---
  gcc/cp/cp-gimplify.cc                           | 16 ++++++++++++++++
  .../g++.dg/cpp23/explicit-obj-lambda20.C        | 17 +++++++++++++++++
  2 files changed, 33 insertions(+)
  create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda20.C

diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc
index cf25cbe2eefb..eb30d780d6c0 100644
--- a/gcc/cp/cp-gimplify.cc
+++ b/gcc/cp/cp-gimplify.cc
@@ -1922,6 +1922,22 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void 
*data)
        return NULL_TREE;
      }
+ if ((TREE_CODE (stmt) == VAR_DECL
+       || TREE_CODE (stmt) == PARM_DECL
+       || TREE_CODE (stmt) == RESULT_DECL)
+      && DECL_HAS_VALUE_EXPR_P (stmt)
+      /* Walk DECL_VALUE_EXPR mainly for benefit of xobj lambdas so that we
+        adjust any invisiref object parm uses within the capture proxies.
+        TODO: For GCC 17 do this walking unconditionally.  */
+      && current_function_decl
+      && DECL_XOBJ_MEMBER_FUNCTION_P (current_function_decl)
+      && LAMBDA_FUNCTION_P (current_function_decl))
+    {
+      tree ve = DECL_VALUE_EXPR (stmt);
+      cp_walk_tree (&ve, cp_genericize_r, data, NULL);
+      SET_DECL_VALUE_EXPR (stmt, ve);
+    }
+
    switch (TREE_CODE (stmt))
      {
      case ADDR_EXPR:
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda20.C 
b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda20.C
new file mode 100644
index 000000000000..4274ff02b76d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda20.C
@@ -0,0 +1,17 @@
+// PR c++/121500
+// { dg-do run { target c++23 } }
+
+struct A {
+  A() = default;
+  A(const A& other) : n(other.n) { }
+  int n = 42;
+};
+
+int main() {
+  A a;
+  auto l = [a](this auto self) {
+    return a.n == 42;
+  };
+  if (!l())
+    __builtin_abort();
+}

Reply via email to