https://gcc.gnu.org/g:90e53ecdbfcc482ad3d0090658427de6d44a5d49
commit r15-8009-g90e53ecdbfcc482ad3d0090658427de6d44a5d49 Author: Simon Martin <si...@nasilyan.com> Date: Wed Mar 12 20:15:39 2025 +0100 c++: Look through capture proxy from outer lambda instead of erroring out [PR110584] We've been rejecting this valid code since r8-4571: === cut here === void foo (float); int main () { constexpr float x = 0; (void) [&] () { foo (x); (void) [] () { foo (x); }; }; } === cut here === The problem is that when processing X in the inner lambda, process_outer_var_ref errors out even though it does find the constant capture from the enclosing lambda. This patch makes sure that process_outer_var_ref properly looks through normal capture proxies, if any. PR c++/110584 gcc/cp/ChangeLog: * cp-tree.h (strip_normal_capture_proxy): Declare. * lambda.cc (strip_normal_capture_proxy): New function to look through normal capture proxies. (build_capture_proxy): Use it. * semantics.cc (process_outer_var_ref): Likewise. gcc/testsuite/ChangeLog: * g++.dg/cpp0x/lambda/lambda-nested10.C: New test. Diff: --- gcc/cp/cp-tree.h | 1 + gcc/cp/lambda.cc | 14 ++++++- gcc/cp/semantics.cc | 8 ++-- .../g++.dg/cpp0x/lambda/lambda-nested10.C | 46 ++++++++++++++++++++++ 4 files changed, 62 insertions(+), 7 deletions(-) diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index a839ad6d28ac..07500fa2b21a 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -8114,6 +8114,7 @@ extern void insert_capture_proxy (tree); extern void insert_pending_capture_proxies (void); extern bool is_capture_proxy (tree); extern bool is_normal_capture_proxy (tree); +extern tree strip_normal_capture_proxy (tree); extern bool is_constant_capture_proxy (tree); extern void register_capture_members (tree); extern tree lambda_expr_this_capture (tree, int); diff --git a/gcc/cp/lambda.cc b/gcc/cp/lambda.cc index da075b988059..ed70bb0ba8e0 100644 --- a/gcc/cp/lambda.cc +++ b/gcc/cp/lambda.cc @@ -295,6 +295,17 @@ is_normal_capture_proxy (tree decl) && DECL_CAPTURED_VARIABLE (decl)); } +/* If DECL is a normal capture proxy, return the variable it captures. + Otherwise, just return DECL. */ + +tree +strip_normal_capture_proxy (tree decl) +{ + while (is_normal_capture_proxy (decl)) + decl = DECL_CAPTURED_VARIABLE (decl); + return decl; +} + /* Returns true iff DECL is a capture proxy for a normal capture of a constant variable. */ @@ -469,8 +480,7 @@ build_capture_proxy (tree member, tree init) STRIP_NOPS (init); gcc_assert (VAR_P (init) || TREE_CODE (init) == PARM_DECL); - while (is_normal_capture_proxy (init)) - init = DECL_CAPTURED_VARIABLE (init); + init = strip_normal_capture_proxy (init); retrofit_lang_decl (var); DECL_CAPTURED_VARIABLE (var) = init; } diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc index 7c7d3e3c4326..6e10893262dd 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -4528,6 +4528,7 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain, bool odr_use) tree lambda_stack = NULL_TREE; tree lambda_expr = NULL_TREE; tree initializer = convert_from_reference (decl); + tree var = strip_normal_capture_proxy (decl); /* Mark it as used now even if the use is ill-formed. */ if (!mark_used (decl, complain)) @@ -4539,9 +4540,6 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain, bool odr_use) if (containing_function && LAMBDA_FUNCTION_P (containing_function)) { /* Check whether we've already built a proxy. */ - tree var = decl; - while (is_normal_capture_proxy (var)) - var = DECL_CAPTURED_VARIABLE (var); tree d = retrieve_local_specialization (var); if (d && d != decl && is_capture_proxy (d)) @@ -4601,8 +4599,8 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain, bool odr_use) /* Only an odr-use of an outer automatic variable causes an error, and a constant variable can decay to a prvalue constant without odr-use. So don't complain yet. */ - else if (!odr_use && decl_constant_var_p (decl)) - return decl; + else if (!odr_use && decl_constant_var_p (var)) + return var; else if (lambda_expr) { if (complain & tf_error) diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-nested10.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-nested10.C new file mode 100644 index 000000000000..aced567c153b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-nested10.C @@ -0,0 +1,46 @@ +// PR c++/110584 +// { dg-do "run" { target c++11 } } + +void foo (int i) { + if (i != 0) + __builtin_abort (); +} + +int main () { + const int x = 0; + + // We would error out on this. + (void) [&] () { + foo (x); + (void) [] () { + foo (x); + }; + } (); + // As well as those. + (void) [&] () { + (void) [] () { + foo (x); + }; + } (); + (void) [&x] () { + (void) [] () { + foo (x); + }; + } (); + // But those would work already. + (void) [] () { + (void) [&] () { + foo (x); + }; + } (); + (void) [&] () { + (void) [&] () { + foo (x); + }; + } (); + (void) [=] () { + (void) [] () { + foo (x); + }; + } (); +}