Hi Jason, On Wed Mar 5, 2025 at 12:03 AM CET, Jason Merrill wrote: > On 2/18/25 5:12 AM, Simon Martin wrote: >> 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 capture
>> from the enclosing lambda. >> >> This patch changes process_outer_var_ref to accept and return the outer >> proxy if it finds any. >> >> Successfully tested on x86_64-pc-linux-gnu. >> >> PR c++/110584 >> >> gcc/cp/ChangeLog: >> >> * semantics.cc (process_outer_var_ref): Use capture from >> enclosing lambda, if any. >> >> gcc/testsuite/ChangeLog: >> >> * g++.dg/cpp0x/lambda/lambda-nested10.C: New test. >> >> --- >> gcc/cp/semantics.cc | 4 ++ >> .../g++.dg/cpp0x/lambda/lambda-nested10.C | 46 +++++++++++++++++++ >> 2 files changed, 50 insertions(+) >> create mode 100644 gcc/testsuite/g++.dg/cpp0x/lambda/lambda-nested10.C >> >> diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc >> index 7c7d3e3c432..7bbc82f7dc1 100644 >> --- a/gcc/cp/semantics.cc >> +++ b/gcc/cp/semantics.cc >> @@ -4598,6 +4598,10 @@ process_outer_var_ref (tree decl, tsubst_flags_t >> complain, bool odr_use) >> if (!odr_use && context == containing_function) >> decl = add_default_capture (lambda_stack, >> /*id=*/DECL_NAME (decl), initializer); >> + /* When doing lambda capture, if we found a capture in an enclosing >> lambda, >> + we can use it. */ >> + else if (!odr_use && is_capture_proxy (decl)) >> + return decl; > > What makes this OK is not that it's a capture, but that the captured > variable is constant, so it should have satisfied this branch: > >> /* 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; > > is_constant_capture_proxy might be useful here. And/or a new > strip_normal_capture function (that I'm surprised doesn't exist yet). Yeah, that makes a lot of sense, thanks. This is what the attached updated patch does, successfully tested on x86_64-pc-linux-gnu. OK for trunk? Simon
From d9e83d2c9dc6fae4a15d47e694fbe689e8a413c1 Mon Sep 17 00:00:00 2001 From: Simon Martin <si...@nasilyan.com> Date: Mon, 17 Feb 2025 18:53:22 +0100 Subject: [PATCH] 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. Successfully tested on x86_64-pc-linux-gnu. 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. --- gcc/cp/cp-tree.h | 1 + gcc/cp/lambda.cc | 14 +++++- gcc/cp/semantics.cc | 6 +-- .../g++.dg/cpp0x/lambda/lambda-nested10.C | 46 +++++++++++++++++++ 4 files changed, 61 insertions(+), 6 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp0x/lambda/lambda-nested10.C diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 583d0496364..b1f1b21ade2 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 da075b98805..ed70bb0ba8e 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 7c7d3e3c432..1b7e7b35300 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -4539,9 +4539,7 @@ 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 var = strip_normal_capture_proxy (decl); tree d = retrieve_local_specialization (var); if (d && d != decl && is_capture_proxy (d)) @@ -4601,7 +4599,7 @@ 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)) + else if (!odr_use && decl_constant_var_p (strip_normal_capture_proxy (decl))) return decl; else if (lambda_expr) { 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 00000000000..2dd9dd4955e --- /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); + }; + } (); +} -- 2.44.0