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.
-- >8 --
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 | 17 +++++++++++++++++
2 files changed, 27 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..5ccfeefa6982 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)
+ && DECL_HAS_VALUE_EXPR_P (stmt))
+ {
+ 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();
+}
--
2.53.0.40.g3e0db84c88