On Fri, 4 Apr 2025, Jason Merrill wrote: > Tested x86_64-pc-linux-gnu, applying to trunk. > > -- 8< -- > > Since r10-7441 we set processing_template_decl in a requires-expression so > that we can use tsubst_expr to evaluate the requirements, but that confuses > lambdas terribly; begin_lambda_type silently returns error_mark_node and we > continue into other failures. This patch clears processing_template_decl > again while we're defining the closure and op() function, so it only remains > set while parsing the introducer (i.e. any init-captures) and building the > resulting object. This properly avoids trying to create another lambda in > tsubst_lambda_expr.
I wonder if we couldn't just set current_template_parms to a dummy template parameter list if it's empty when parsing a requires-expr? > > PR c++/99546 > PR c++/113925 > PR c++/106976 > PR c++/109961 > PR c++/117336 > > gcc/cp/ChangeLog: > > * lambda.cc (build_lambda_object): Handle fake > requires-expr processing_template_decl. > * parser.cc (cp_parser_lambda_expression): Likewise. > > gcc/testsuite/ChangeLog: > > * g++.dg/cpp2a/lambda-requires2.C: New test. > * g++.dg/cpp2a/lambda-requires3.C: New test. > * g++.dg/cpp2a/lambda-requires4.C: New test. > * g++.dg/cpp2a/lambda-requires5.C: New test. > --- > gcc/cp/lambda.cc | 8 +++- > gcc/cp/parser.cc | 41 ++++++++++++------- > gcc/testsuite/g++.dg/cpp2a/lambda-requires2.C | 8 ++++ > gcc/testsuite/g++.dg/cpp2a/lambda-requires3.C | 6 +++ > gcc/testsuite/g++.dg/cpp2a/lambda-requires4.C | 6 +++ > gcc/testsuite/g++.dg/cpp2a/lambda-requires5.C | 10 +++++ > 6 files changed, 64 insertions(+), 15 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/cpp2a/lambda-requires2.C > create mode 100644 gcc/testsuite/g++.dg/cpp2a/lambda-requires3.C > create mode 100644 gcc/testsuite/g++.dg/cpp2a/lambda-requires4.C > create mode 100644 gcc/testsuite/g++.dg/cpp2a/lambda-requires5.C > > diff --git a/gcc/cp/lambda.cc b/gcc/cp/lambda.cc > index ed70bb0ba8e..f0a54b60275 100644 > --- a/gcc/cp/lambda.cc > +++ b/gcc/cp/lambda.cc > @@ -59,7 +59,13 @@ build_lambda_object (tree lambda_expr) > vec<constructor_elt, va_gc> *elts = NULL; > tree node, expr, type; > > - if (processing_template_decl || lambda_expr == error_mark_node) > + if (processing_template_decl && !in_template_context > + && current_binding_level->requires_expression) > + /* As in cp_parser_lambda_expression, don't get confused by > + cp_parser_requires_expression setting processing_template_decl. In > that > + case we want to return the result of finish_compound_literal, to avoid > + tsubst_lambda_expr. */; > + else if (processing_template_decl || lambda_expr == error_mark_node) > return lambda_expr; > > /* Make sure any error messages refer to the lambda-introducer. */ > diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc > index 53e6237f6c4..812a7c5ae7d 100644 > --- a/gcc/cp/parser.cc > +++ b/gcc/cp/parser.cc > @@ -11736,21 +11736,34 @@ cp_parser_lambda_expression (cp_parser* parser) > if (cp_parser_error_occurred (parser)) > return error_mark_node; > > - type = begin_lambda_type (lambda_expr); > - if (type == error_mark_node) > - return error_mark_node; > - > - record_lambda_scope (lambda_expr); > - record_lambda_scope_discriminator (lambda_expr); > - > - /* Do this again now that LAMBDA_EXPR_EXTRA_SCOPE is set. */ > - determine_visibility (TYPE_NAME (type)); > - > - /* Now that we've started the type, add the capture fields for any > - explicit captures. */ > - register_capture_members (LAMBDA_EXPR_CAPTURE_LIST (lambda_expr)); > - > { > + /* OK, this is a bit tricksy. cp_parser_requires_expression sets > + processing_template_decl to make checking more normal, but that > confuses > + lambda parsing terribly. In non-template context, we want to parse > the > + lambda once and not tsubst_lambda_expr. So in that case, clear > + processing_template_decl now, and restore it before the call to > + build_lambda_object; that way we end up with what looks like a > templatey > + functional cast to the closure type, which is suitable for the > + requires-expression tsubst_expr. This is PR99546 and friends. */ > + processing_template_decl_sentinel ptds (/*reset*/false); > + if (processing_template_decl && !in_template_context > + && current_binding_level->requires_expression) > + processing_template_decl = 0; > + > + type = begin_lambda_type (lambda_expr); > + if (type == error_mark_node) > + return error_mark_node; > + > + record_lambda_scope (lambda_expr); > + record_lambda_scope_discriminator (lambda_expr); > + > + /* Do this again now that LAMBDA_EXPR_EXTRA_SCOPE is set. */ > + determine_visibility (TYPE_NAME (type)); > + > + /* Now that we've started the type, add the capture fields for any > + explicit captures. */ > + register_capture_members (LAMBDA_EXPR_CAPTURE_LIST (lambda_expr)); > + > /* Inside the class, surrounding template-parameter-lists do not apply. > */ > unsigned int saved_num_template_parameter_lists > = parser->num_template_parameter_lists; > diff --git a/gcc/testsuite/g++.dg/cpp2a/lambda-requires2.C > b/gcc/testsuite/g++.dg/cpp2a/lambda-requires2.C > new file mode 100644 > index 00000000000..be5a71abefe > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp2a/lambda-requires2.C > @@ -0,0 +1,8 @@ > +// PR c++/99546 > +// { dg-do compile { target c++20 } } > + > +int main() { > + constexpr auto b = requires { []{}; }; > + static_assert(b); > + static_assert(!b); // { dg-error "assertion failed" } > +} > diff --git a/gcc/testsuite/g++.dg/cpp2a/lambda-requires3.C > b/gcc/testsuite/g++.dg/cpp2a/lambda-requires3.C > new file mode 100644 > index 00000000000..8c4ef064e52 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp2a/lambda-requires3.C > @@ -0,0 +1,6 @@ > +// PR c++/113925 > +// { dg-do compile { target c++20 } } > + > +template<bool B> > +struct b{}; > +static_assert(requires { b<([]()consteval{ return true; }())>{}; }); > diff --git a/gcc/testsuite/g++.dg/cpp2a/lambda-requires4.C > b/gcc/testsuite/g++.dg/cpp2a/lambda-requires4.C > new file mode 100644 > index 00000000000..f3bb041310c > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp2a/lambda-requires4.C > @@ -0,0 +1,6 @@ > +// PR c++/106976 > +// { dg-do compile { target c++20 } } > + > +struct S{ > + constexpr static auto s = requires { []; }; // { dg-error "expected '\{'" } > +}; > diff --git a/gcc/testsuite/g++.dg/cpp2a/lambda-requires5.C > b/gcc/testsuite/g++.dg/cpp2a/lambda-requires5.C > new file mode 100644 > index 00000000000..c81831381e3 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp2a/lambda-requires5.C > @@ -0,0 +1,10 @@ > +// PR c++/109961 > +// { dg-do compile { target c++20 } } > + > +auto a = requires{ > + []( int b ) consteval { > + if( b ) { > + throw b; > + } > + }( 0 ); > +}; > > base-commit: 2a36d22ab52d6ffce9a1fcaf7aca83336679e111 > -- > 2.49.0 > >