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

Reply via email to