On 4/15/25 9:39 AM, 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?
OK.
-- >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();
+}