Hi Jason, On Wed Mar 5, 2025 at 9:40 PM CET, Jason Merrill wrote: > On 3/5/25 10:39 AM, Simon Martin wrote: >> 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? > >> @@ -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; > > We also want to strip the proxy from the return value. Doh, of course, sorry for overlooking this :-/ I checked if I could come up with a test case that would fail with this mistake but failed to. Anyway, I attach another revision that fixes the issue, and was successfully tested on x86_64-pc-linux-gnu. OK for trunk? Simon
From 77c5c7b4d7a0e8e466ba9bcc6d852b3a5a88aa20 Mon Sep 17 00:00:00 2001 From: Simon Martin <si...@nasilyan.com> Date: Thu, 6 Mar 2025 16:12:18 +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 | 8 ++-- .../g++.dg/cpp0x/lambda/lambda-nested10.C | 46 +++++++++++++++++++ 4 files changed, 62 insertions(+), 7 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..6e10893262d 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 00000000000..aced567c153 --- /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