https://gcc.gnu.org/g:303cc73182db5ed367b184df813cd50864c55f83
commit r15-7118-g303cc73182db5ed367b184df813cd50864c55f83 Author: Patrick Palka <ppa...@redhat.com> Date: Tue Jan 21 21:57:02 2025 -0500 c++: 'this' capture clobbered during recursive inst [PR116756] 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 <ja...@redhat.com> Diff: --- gcc/cp/lambda.cc | 6 ++++++ gcc/cp/parser.cc | 3 --- gcc/cp/pt.cc | 11 +---------- gcc/testsuite/g++.dg/cpp1z/constexpr-if-lambda7.C | 24 +++++++++++++++++++++++ 4 files changed, 31 insertions(+), 13 deletions(-) diff --git a/gcc/cp/lambda.cc b/gcc/cp/lambda.cc index be8a0fe01cba..4ee8f6c745da 100644 --- a/gcc/cp/lambda.cc +++ b/gcc/cp/lambda.cc @@ -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) diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 398fd8538e2f..a9eddd1a6da5 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -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. */ diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 6a5d65026157..28e05490d06c 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -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 index 000000000000..8304c8f22e37 --- /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(); +}