On 11/2/21 14:06, Patrick Palka wrote:
Here when determining the type of the FIELD_DECL for the by-value
capture of 'i' in the inner lambda, we incorrectly give it the
type const int instead of int since the effective initializer is
the proxy for the outer capture, and this proxy is const qualified
since the outer lambda is non-mutable.

This patch fixes this by handling capturing of capture proxies specially
in lambda_capture_field_type, namely we instead consider the type of
their FIELD_DECL which unlike the proxy has the true cv-quals of the
captured entity.

Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for
trunk?

        PR c++/94376

gcc/cp/ChangeLog:

        * lambda.c (lambda_capture_field_type): Simplify by handling the
        is_this case first and specially.  When capturing by-value a
        capture proxy, consider the type of the corresponding field
        instead.

gcc/testsuite/ChangeLog:

        * g++.dg/cpp0x/lambda/lambda-nested9.C: New test.
---
  gcc/cp/lambda.c                               | 19 +++++++--
  .../g++.dg/cpp0x/lambda/lambda-nested9.C      | 41 +++++++++++++++++++
  2 files changed, 56 insertions(+), 4 deletions(-)
  create mode 100644 gcc/testsuite/g++.dg/cpp0x/lambda/lambda-nested9.C

diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c
index 2e9d38bbe83..f16c121dc78 100644
--- a/gcc/cp/lambda.c
+++ b/gcc/cp/lambda.c
@@ -196,7 +196,9 @@ lambda_capture_field_type (tree expr, bool explicit_init_p,
    tree type;
    bool is_this = is_this_parameter (tree_strip_nop_conversions (expr));
- if (!is_this && explicit_init_p)
+  if (is_this)
+    type = TREE_TYPE (expr);
+  else if (explicit_init_p)
      {
        tree auto_node = make_auto ();
@@ -210,7 +212,7 @@ lambda_capture_field_type (tree expr, bool explicit_init_p,
        else
        type = do_auto_deduction (type, expr, auto_node);
      }
-  else if (!is_this && type_dependent_expression_p (expr))
+  else if (type_dependent_expression_p (expr))
      {
        type = cxx_make_type (DECLTYPE_TYPE);
        DECLTYPE_TYPE_EXPR (type) = expr;
@@ -220,10 +222,19 @@ lambda_capture_field_type (tree expr, bool 
explicit_init_p,
      }
    else
      {
+      if (!by_reference_p && is_capture_proxy (expr))
+       {
+         /* When capturing by-value another capture proxy from an enclosing
+            lambda, consider the type of the corresponding field instead,
+            as the proxy may be const-qualifed if the enclosing lambda is
+            non-mutable (PR94376).  */
+         gcc_assert (TREE_CODE (DECL_VALUE_EXPR (expr)) == COMPONENT_REF);
+         expr = TREE_OPERAND (DECL_VALUE_EXPR (expr), 1);

Is there a reason not to use DECL_CAPTURED_VARIABLE?

+       }
+
        type = non_reference (unlowered_expr_type (expr));
- if (!is_this
-         && (by_reference_p || TREE_CODE (type) == FUNCTION_TYPE))
+      if (by_reference_p || TREE_CODE (type) == FUNCTION_TYPE)
        type = build_reference_type (type);
      }
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-nested9.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-nested9.C
new file mode 100644
index 00000000000..ff7da3b0ce1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-nested9.C
@@ -0,0 +1,41 @@
+// PR c++/94376
+// { dg-do compile { target c++11 } }
+
+int main() {
+  // We used to incorrectly reject the first two cases.
+  int i = 0;
+  [=] () {
+    [=] () mutable {
+      ++i;
+    };
+  };
+
+#if __cpp_init_captures
+  [j=0] () {
+    [=] () mutable {
+      ++j;
+    };
+  };
+#endif
+
+  [=] () {
+    [&] () mutable {
+      ++i; // { dg-error "read-only" }
+    };
+  };
+
+  const int j = 0;
+  [=] () {
+    [=] () mutable {
+      ++j; // { dg-error "read-only" }
+    };
+  };
+
+#if __cpp_init_captures
+  [j=0] () {
+    [&] () mutable {
+      ++j; // { dg-error "read-only" }
+    };
+  };
+#endif
+}


Reply via email to