On Tue, Apr 15, 2025 at 09:16:46AM -0400, Jason Merrill wrote:
> On 4/15/25 2:56 AM, Nathaniel Shead wrote:
> > On Mon, Apr 14, 2025 at 05:33:05PM -0400, Jason Merrill wrote:
> > > On 4/13/25 6:32 AM, Nathaniel Shead wrote:
> > > > Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk?
> > > > 
> > > > -- >8 --
> > > > 
> > > > Currently, pruned lambda captures are still leftover in the function's
> > > > BLOCK and topmost BIND_EXPR; this doesn't cause any issues for normal
> > > > compilation, but does break modules streaming as we try to reconstruct a
> > > > FIELD_DECL that no longer exists on the type itself.
> > > > 
> > > >         PR c++/119755
> > > > 
> > > > gcc/cp/ChangeLog:
> > > > 
> > > >         * lambda.cc (prune_lambda_captures): Remove pruned capture from
> > > >         function's BLOCK_VARS and BIND_EXPR_VARS.
> > > > 
> > > > gcc/testsuite/ChangeLog:
> > > > 
> > > >         * g++.dg/modules/lambda-10_a.H: New test.
> > > >         * g++.dg/modules/lambda-10_b.C: New test.
> > > > 
> > > > Signed-off-by: Nathaniel Shead <nathanielosh...@gmail.com>
> > > > ---
> > > >    gcc/cp/lambda.cc                           | 22 
> > > > ++++++++++++++++++++++
> > > >    gcc/testsuite/g++.dg/modules/lambda-10_a.H | 17 +++++++++++++++++
> > > >    gcc/testsuite/g++.dg/modules/lambda-10_b.C |  7 +++++++
> > > >    3 files changed, 46 insertions(+)
> > > >    create mode 100644 gcc/testsuite/g++.dg/modules/lambda-10_a.H
> > > >    create mode 100644 gcc/testsuite/g++.dg/modules/lambda-10_b.C
> > > > 
> > > > diff --git a/gcc/cp/lambda.cc b/gcc/cp/lambda.cc
> > > > index f0a54b60275..d01bb04cd32 100644
> > > > --- a/gcc/cp/lambda.cc
> > > > +++ b/gcc/cp/lambda.cc
> > > > @@ -1858,6 +1858,14 @@ prune_lambda_captures (tree body)
> > > >      cp_walk_tree_without_duplicates (&body, mark_const_cap_r, 
> > > > &const_vars);
> > > > +  tree bind_expr = expr_single (DECL_SAVED_TREE (lambda_function 
> > > > (lam)));
> > > > +  gcc_assert (bind_expr
> > > > +             && (TREE_CODE (bind_expr) == BIND_EXPR
> > > > +                 /* FIXME: In a noexcept lambda we never prune captures
> > > > +                    (PR119764); when we do we need to handle this case
> > > > +                    for modules streaming.  */
> > > 
> > > The attached patch seems to fix that, with the result that your patch
> > > crashes.
> > > 
> > 
> > Thanks.  And yup, crashing was deliberate here as I wasn't 100% sure
> > what the tree would look like for this case after an appropriate fix.
> > 
> > One quick question about your patch, since it could in theory affect ABI
> > (the size of the lambdas change) should the pruning of such lambdas be
> > dependent on an ABI version check?
> 
> Indeed, perhaps this is too late in the 15 cycle for such a change.
> 
> > Otherwise here's an updated patch that relies on your patch.
> > Bootstrapped and regtested on x86_64-pc-linux-gnu, OK for trunk along
> > with yours?  (Or if the potential ABI concerns mean that your change
> > isn't appropriate for GCC15, would the old version of my patch still be
> > OK for GCC15 to get 'import std' working again for C++26?)
> 
> For 15 please adjust this patch to be more fault-tolerant:
> 
> > -- >8 --
> > 
> > Currently, pruned lambda captures are still leftover in the function's
> > BLOCK and topmost BIND_EXPR; this doesn't cause any issues for normal
> > compilation, but does break modules streaming as we try to reconstruct a
> > FIELD_DECL that no longer exists on the type itself.
> > 
> >     PR c++/119755
> > 
> > gcc/cp/ChangeLog:
> > 
> >     * lambda.cc (prune_lambda_captures): Remove pruned capture from
> >     function's BLOCK_VARS and BIND_EXPR_VARS.
> > 
> > gcc/testsuite/ChangeLog:
> > 
> >     * g++.dg/modules/lambda-10_a.H: New test.
> >     * g++.dg/modules/lambda-10_b.C: New test.
> > 
> > Signed-off-by: Nathaniel Shead <nathanielosh...@gmail.com>
> > Reviewed-by: Jason Merrill <ja...@redhat.com>
> > ---
> >   gcc/cp/lambda.cc                           | 19 +++++++++++++++++++
> >   gcc/testsuite/g++.dg/modules/lambda-10_a.H | 17 +++++++++++++++++
> >   gcc/testsuite/g++.dg/modules/lambda-10_b.C |  7 +++++++
> >   3 files changed, 43 insertions(+)
> >   create mode 100644 gcc/testsuite/g++.dg/modules/lambda-10_a.H
> >   create mode 100644 gcc/testsuite/g++.dg/modules/lambda-10_b.C
> > 
> > diff --git a/gcc/cp/lambda.cc b/gcc/cp/lambda.cc
> > index c6308b941d3..7bb88a900d5 100644
> > --- a/gcc/cp/lambda.cc
> > +++ b/gcc/cp/lambda.cc
> > @@ -1862,6 +1862,11 @@ prune_lambda_captures (tree body)
> >     cp_walk_tree_without_duplicates (&body, mark_const_cap_r, &const_vars);
> > +  tree bind_expr = expr_single (DECL_SAVED_TREE (lambda_function (lam)));
> > +  if (bind_expr && TREE_CODE (bind_expr) == MUST_NOT_THROW_EXPR)
> > +    bind_expr = expr_single (TREE_OPERAND (bind_expr, 0));
> > +  gcc_assert (bind_expr && TREE_CODE (bind_expr) == BIND_EXPR);
> 
> i.e. here clear bind_expr if it isn't a BIND_EXPR...
> 
> >     tree *fieldp = &TYPE_FIELDS (LAMBDA_EXPR_CLOSURE (lam));
> >     for (tree *capp = &LAMBDA_EXPR_CAPTURE_LIST (lam); *capp; )
> >       {
> > @@ -1883,6 +1888,20 @@ prune_lambda_captures (tree body)
> >             fieldp = &DECL_CHAIN (*fieldp);
> >           *fieldp = DECL_CHAIN (*fieldp);
> > +         /* And out of the bindings for the function.  */
> > +         tree *blockp = &BLOCK_VARS (current_binding_level->blocks);
> > +         while (*blockp != DECL_EXPR_DECL (**use))
> > +           blockp = &DECL_CHAIN (*blockp);
> > +         *blockp = DECL_CHAIN (*blockp);
> > +
> > +         /* And maybe out of the vars declared in the containing
> > +            BIND_EXPR, if it's listed there.  */
> > +         tree *bindp = &BIND_EXPR_VARS (bind_expr);
> > +         while (*bindp && *bindp != DECL_EXPR_DECL (**use))
> > +           bindp = &DECL_CHAIN (*bindp);
> > +         if (*bindp)
> > +           *bindp = DECL_CHAIN (*bindp);
> 
> ...and only do this if bind_expr is set.
> 
> Then it should work with or without my patch, right?

Right; here's a slight variation on that (taking advantage of the fact
that the bug means that 'expr_single' will return NULL_TREE if the
variables got incorrectly pushed into that statement list).  Tested so
far on dg.exp=*lambda* modules.exp=*lambda*, OK for trunk if full
bootstrap+regtest passes?

-- >8 --

Currently, pruned lambda captures are still leftover in the function's
BLOCK and topmost BIND_EXPR; this doesn't cause any issues for normal
compilation, but does break modules streaming as we try to reconstruct a
FIELD_DECL that no longer exists on the type itself.

        PR c++/119755

gcc/cp/ChangeLog:

        * lambda.cc (prune_lambda_captures): Remove pruned capture from
        function's BLOCK_VARS and BIND_EXPR_VARS.

gcc/testsuite/ChangeLog:

        * g++.dg/modules/lambda-10_a.H: New test.
        * g++.dg/modules/lambda-10_b.C: New test.

Signed-off-by: Nathaniel Shead <nathanielosh...@gmail.com>
Reviewed-by: Jason Merrill <ja...@redhat.com>
---
 gcc/cp/lambda.cc                           | 24 ++++++++++++++++++++++
 gcc/testsuite/g++.dg/modules/lambda-10_a.H | 17 +++++++++++++++
 gcc/testsuite/g++.dg/modules/lambda-10_b.C |  7 +++++++
 3 files changed, 48 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/modules/lambda-10_a.H
 create mode 100644 gcc/testsuite/g++.dg/modules/lambda-10_b.C

diff --git a/gcc/cp/lambda.cc b/gcc/cp/lambda.cc
index f0a54b60275..b2e0ecdd67e 100644
--- a/gcc/cp/lambda.cc
+++ b/gcc/cp/lambda.cc
@@ -1858,6 +1858,13 @@ prune_lambda_captures (tree body)
 
   cp_walk_tree_without_duplicates (&body, mark_const_cap_r, &const_vars);
 
+  tree bind_expr = expr_single (DECL_SAVED_TREE (lambda_function (lam)));
+  if (bind_expr && TREE_CODE (bind_expr) == MUST_NOT_THROW_EXPR)
+    bind_expr = expr_single (TREE_OPERAND (bind_expr, 0));
+  /* FIXME: We don't currently handle noexcept lambda captures correctly,
+     so bind_expr may not be set; see PR c++/119764.  */
+  gcc_assert (!bind_expr || TREE_CODE (bind_expr) == BIND_EXPR);
+
   tree *fieldp = &TYPE_FIELDS (LAMBDA_EXPR_CLOSURE (lam));
   for (tree *capp = &LAMBDA_EXPR_CAPTURE_LIST (lam); *capp; )
     {
@@ -1879,6 +1886,23 @@ prune_lambda_captures (tree body)
                fieldp = &DECL_CHAIN (*fieldp);
              *fieldp = DECL_CHAIN (*fieldp);
 
+             /* And out of the bindings for the function.  */
+             tree *blockp = &BLOCK_VARS (current_binding_level->blocks);
+             while (*blockp != DECL_EXPR_DECL (**use))
+               blockp = &DECL_CHAIN (*blockp);
+             *blockp = DECL_CHAIN (*blockp);
+
+             /* And maybe out of the vars declared in the containing
+                BIND_EXPR, if it's listed there.  */
+             if (bind_expr)
+               {
+                 tree *bindp = &BIND_EXPR_VARS (bind_expr);
+                 while (*bindp && *bindp != DECL_EXPR_DECL (**use))
+                   bindp = &DECL_CHAIN (*bindp);
+                 if (*bindp)
+                   *bindp = DECL_CHAIN (*bindp);
+               }
+
              /* And remove the capture proxy declaration.  */
              **use = void_node;
              continue;
diff --git a/gcc/testsuite/g++.dg/modules/lambda-10_a.H 
b/gcc/testsuite/g++.dg/modules/lambda-10_a.H
new file mode 100644
index 00000000000..1ad1a808c07
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/lambda-10_a.H
@@ -0,0 +1,17 @@
+// PR c++/119755
+// { dg-additional-options "-fmodule-header" }
+// { dg-module-cmi {} }
+
+template <typename _Out> void format(_Out) {
+  constexpr int __term = 1;
+  [&] { __term; };
+  [&] { const int outer = __term; { __term; } };
+  [&]() noexcept { __term; };
+  [&]() noexcept { const int outer = __term; { __term; } };
+  [&](auto) { int n[__term]; }(0);
+  [&](auto) noexcept { int n[__term]; }(0);
+}
+
+inline void vformat() {
+  format(0);
+}
diff --git a/gcc/testsuite/g++.dg/modules/lambda-10_b.C 
b/gcc/testsuite/g++.dg/modules/lambda-10_b.C
new file mode 100644
index 00000000000..3556bcee02c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/lambda-10_b.C
@@ -0,0 +1,7 @@
+// PR c++/119755
+// { dg-additional-options "-fmodules" }
+
+import "lambda-10_a.H";
+int main() {
+  vformat();
+}
-- 
2.47.0

Reply via email to