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... -- >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(); +} -- 2.48.0.rc1.35.g1b4e9a5f8b