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?

> 
> > -- >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();
> > +}
> 
> 

Reply via email to