On 1/10/25 2:20 PM, Patrick Palka wrote:
Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look
OK for trunk?
The documentation for LAMBDA_EXPR_THIS_CAPTURE seems outdated because
it says the field is only used at parse time, but apparently it's also
used at instantiation time.
Non-'this' captures don't seem to be affected, because there is no
corresponding LAMBDA_EXPR field that gets clobbered, and instead their
uses get resolved via the local specialization mechanism which is
recursion aware.
The bug also disappears if we explicitly use this in the openSeries call,
i.e. this->openSeries(...), because that sidesteps the use of
maybe_resolve_dummy / LAMBDA_EXPR_THIS_CAPTURE for resolving the
implicit object, and instead gets resolved via the local mechanism
specialization.
Maybe this suggests that there's a better way to fix this, but I'm not
sure...
That does sound like an interesting direction. Maybe for a generic
lambda, LAMBDA_EXPR_THIS_CAPTURE could just refer to the captured
parameter, and we use retrieve_local_specialization to find the proxy?
-- >8 --
Here during instantiation of lambda::op() [with I = 0] we substitute
into the call self(self, cst<1>{}) which requires recursive instantiation
of the same lambda::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 inner lambda::op()'s capture proxy instead of the outer
lambda::op(), and the original value is never restored.
So later during substitution into the openSeries call in the outer
lambda::op() maybe_resolve_dummy uses the 'this' proxy belonging to the
inner lambda::op(), which leads to an context mismatch ICE during
gimplification of the proxy.
This patch naively fixes this by making us restore LAMBDA_EXPR_THIS_CAPTURE
after instantiating a lambda's op().
PR c++/116756
gcc/cp/ChangeLog:
* pt.cc (instantiate_body): Restore LAMBDA_EXPR_THIS_CAPTURE
after instantiating a lambda's op().
gcc/testsuite/ChangeLog:
* g++.dg/cpp1z/constexpr-if-lambda7.C: New test.
---
gcc/cp/pt.cc | 11 +++++++++
.../g++.dg/cpp1z/constexpr-if-lambda7.C | 24 +++++++++++++++++++
2 files changed, 35 insertions(+)
create mode 100644 gcc/testsuite/g++.dg/cpp1z/constexpr-if-lambda7.C
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index b22129d8a46..a141de56446 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -27512,6 +27512,13 @@ instantiate_body (tree pattern, tree args, tree d,
bool nested_p)
local_specialization_stack lss (push_to_top ? lss_blank : lss_copy);
tree block = NULL_TREE;
+ tree saved_this_capture = NULL_TREE;
+ if (LAMBDA_FUNCTION_P (d))
+ /* Save/restore the 'this' capture, which gets clobbered by tsubst_stmt,
+ which causes problems in case of recursive op() instantiation. */
+ saved_this_capture
+ = LAMBDA_EXPR_THIS_CAPTURE (CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (d)));
+
/* Set up context. */
if (nested_p)
block = push_stmt_list ();
@@ -27555,6 +27562,10 @@ instantiate_body (tree pattern, tree args, tree d,
bool nested_p)
if (DECL_OMP_DECLARE_REDUCTION_P (code_pattern))
cp_check_omp_declare_reduction (d);
+
+ if (LAMBDA_FUNCTION_P (d))
+ LAMBDA_EXPR_THIS_CAPTURE (CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (d)))
+ = saved_this_capture;
}
/* We're not deferring instantiation any more. */
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
index 00000000000..8304c8f22e3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-if-lambda7.C
@@ -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();
+}