On Tue, Apr 15, 2025 at 11:39:21PM +1000, Nathaniel Shead wrote:
> 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?

To clarify; this subset of tests passes both with and without your patch
for PR119764 applied.

> 
> -- >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