c++: Prune lambda captures from more places [PR119755]

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 <nathanieloshead@gmail.com>
Reviewed-by: Jason Merrill <jason@redhat.com>
This commit is contained in:
Nathaniel Shead 2025-04-13 12:20:37 +10:00
parent 674b0875a9
commit a6f4178d0d
3 changed files with 48 additions and 0 deletions

View file

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

View file

@ -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);
}

View file

@ -0,0 +1,7 @@
// PR c++/119755
// { dg-additional-options "-fmodules" }
import "lambda-10_a.H";
int main() {
vformat();
}