]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: Look through capture proxy from outer lambda instead of erroring out [PR110584]
authorSimon Martin <simon@nasilyan.com>
Wed, 12 Mar 2025 19:15:39 +0000 (20:15 +0100)
committerSimon Martin <simon@nasilyan.com>
Wed, 12 Mar 2025 19:16:01 +0000 (20:16 +0100)
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.

gcc/cp/cp-tree.h
gcc/cp/lambda.cc
gcc/cp/semantics.cc
gcc/testsuite/g++.dg/cpp0x/lambda/lambda-nested10.C [new file with mode: 0644]

index a839ad6d28acc76737835b3add8a9ca098cb7a45..07500fa2b21ab087fde1a3a545416da70257dcd4 100644 (file)
@@ -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);
index da075b988059ac9b6cbc5fab2c79d25fb01cd616..ed70bb0ba8e0a81e2144008a2b453bd598b70bfa 100644 (file)
@@ -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;
     }
index 7c7d3e3c43267844dae233e729a4812df34192df..6e10893262ddc711b735de2a96629a66bac80a6b 100644 (file)
@@ -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 (file)
index 0000000..aced567
--- /dev/null
@@ -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);
+    };
+  } ();
+}