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);
+    };
+  } ();
+}

Reply via email to