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
> 
> 

Reply via email to