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 <[email protected]>
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