]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: 'this' capture clobbered during recursive inst [PR116756]
authorPatrick Palka <ppalka@redhat.com>
Wed, 22 Jan 2025 02:57:02 +0000 (21:57 -0500)
committerPatrick Palka <ppalka@redhat.com>
Wed, 22 Jan 2025 02:57:02 +0000 (21:57 -0500)
Here during instantiation of generic lambda's op() [with I = 0] we
substitute into the call self(self, cst<1>{}) which requires recursive
instantiation of the same op() [with I = 1] (which isn't deferred due to
lambda's deduced return type.  During this recursive instantiation, the
DECL_EXPR case of tsubst_stmt clobbers LAMBDA_EXPR_THIS_CAPTURE to point
to the child op()'s specialized capture proxy instead of the parent's,
and the original value is never restored.

So later when substituting into the openSeries call in the parent op()
maybe_resolve_dummy uses the 'this' proxy belonging to the child op(),
which leads to a context mismatch ICE during gimplification of the
proxy.

An earlier version of this patch fixed this by making instantiate_body
save/restore LAMBDA_EXPR_THIS_CAPTURE during a lambda op() instantiation.
But it seems cleaner to avoid overwriting LAMBDA_EXPR_THIS_CAPTURE in the
first place by making it point to the non-specialized capture proxy, and
instead call retrieve_local_specialization as needed, which is what this
patch implements.  It's natural then to not clear LAMBDA_EXPR_THIS_CAPTURE
after parsing/regenerating a lambda.

PR c++/116756

gcc/cp/ChangeLog:

* lambda.cc (lambda_expr_this_capture): Call
retrieve_local_specialization on the result of
LAMBDA_EXPR_THIS_CAPTURE for a generic lambda.
* parser.cc (cp_parser_lambda_expression): Don't clear
LAMBDA_EXPR_THIS_CAPTURE.
* pt.cc (tsubst_stmt) <case DECL_EXPR>: Don't overwrite
LAMBDA_EXPR_THIS_CAPTURE with the specialized capture.
(tsubst_lambda_expr): Don't clear LAMBDA_EXPR_THIS_CAPTURE
afterward.

gcc/testsuite/ChangeLog:

* g++.dg/cpp1z/constexpr-if-lambda7.C: New test.

Reviewed-by: Jason Merrill <jason@redhat.com>
gcc/cp/lambda.cc
gcc/cp/parser.cc
gcc/cp/pt.cc
gcc/testsuite/g++.dg/cpp1z/constexpr-if-lambda7.C [new file with mode: 0644]

index be8a0fe01cba2f527359690f999b486c73edb102..4ee8f6c745da7a8f0b77d28f05129a549eb9da32 100644 (file)
@@ -785,6 +785,12 @@ lambda_expr_this_capture (tree lambda, int add_capture_p)
   tree result;
 
   tree this_capture = LAMBDA_EXPR_THIS_CAPTURE (lambda);
+  if (this_capture)
+    if (tree spec = retrieve_local_specialization (this_capture))
+      {
+       gcc_checking_assert (generic_lambda_fn_p (lambda_function (lambda)));
+       this_capture = spec;
+      }
 
   /* In unevaluated context this isn't an odr-use, so don't capture.  */
   if (cp_unevaluated_operand)
index 398fd8538e2f39d323508d04c56048b9906767bb..a9eddd1a6da580c0588f4ae5072cc81c28c4b549 100644 (file)
@@ -11825,9 +11825,6 @@ cp_parser_lambda_expression (cp_parser* parser)
     parser->omp_array_section_p = saved_omp_array_section_p;
   }
 
-  /* This field is only used during parsing of the lambda.  */
-  LAMBDA_EXPR_THIS_CAPTURE (lambda_expr) = NULL_TREE;
-
   /* This lambda shouldn't have any proxies left at this point.  */
   gcc_assert (LAMBDA_EXPR_PENDING_PROXIES (lambda_expr) == NULL);
   /* And now that we're done, push proxies for an enclosing lambda.  */
index 6a5d650261572ab2adde4d534fe11b61db6a479f..28e05490d06c997d40d45db9b27c1db2ae1ef094 100644 (file)
@@ -18940,12 +18940,6 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t complain, tree in_decl)
                else if (is_capture_proxy (DECL_EXPR_DECL (t)))
                  {
                    DECL_CONTEXT (decl) = current_function_decl;
-                   if (DECL_NAME (decl) == this_identifier)
-                     {
-                       tree lam = DECL_CONTEXT (current_function_decl);
-                       lam = CLASSTYPE_LAMBDA_EXPR (lam);
-                       LAMBDA_EXPR_THIS_CAPTURE (lam) = decl;
-                     }
                    insert_capture_proxy (decl);
                  }
                else if (DECL_IMPLICIT_TYPEDEF_P (t))
@@ -20148,8 +20142,7 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
     LAMBDA_EXPR_REGEN_INFO (r)
       = build_template_info (t, preserve_args (args));
 
-  gcc_assert (LAMBDA_EXPR_THIS_CAPTURE (t) == NULL_TREE
-             && LAMBDA_EXPR_PENDING_PROXIES (t) == NULL);
+  gcc_assert (LAMBDA_EXPR_PENDING_PROXIES (t) == NULL);
 
   vec<tree,va_gc>* field_packs = NULL;
   unsigned name_independent_cnt = 0;
@@ -20364,8 +20357,6 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
       LAMBDA_EXPR_CAPTURE_LIST (r)
        = nreverse (LAMBDA_EXPR_CAPTURE_LIST (r));
 
-      LAMBDA_EXPR_THIS_CAPTURE (r) = NULL_TREE;
-
       maybe_add_lambda_conv_op (type);
     }
 
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-if-lambda7.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-if-lambda7.C
new file mode 100644 (file)
index 0000000..8304c8f
--- /dev/null
@@ -0,0 +1,24 @@
+// PR c++/116756
+// { dg-do compile { target c++17 } }
+
+template<int N> struct cst { static constexpr int value = N; };
+
+struct Store {
+  void openDF() {
+    auto lambda = [this](auto& self, auto I) {
+      if constexpr (I.value == 0) {
+        auto next = [&self] { self(self, cst<1>{}); };
+        openSeries(next);
+      } else {
+        openSeries(0);
+      }
+    };
+    lambda(lambda, cst<0>{});
+  }
+  template<class T> void openSeries(T) { }
+};
+
+int main() {
+  Store store;
+  store.openDF();
+}