On 3/25/25 10:17 AM, yxj-github-437 wrote:
This patch would like to avoid the ICE when template lambdas call with
default parameters in unevaluated context. For example as blow:

      1   │ template <class T>
      2   │ void foo(T x) {
      3   │   sizeof []<int=0>(T=x) { return 0; }();
      4   │ }
      5   │
      6   │ void test {
      7   │   foo(0);
      8   │ }

when compile with -fsyntax-only -std=c++20, it will have ICE similar as below

test.cc: In instantiation of ‘void foo(T) [with T = int]’:
test.cc:7:6:   required from here
      6 |   foo(0);
        |   ~~~^~~
test.cc:3:38: internal compiler error: in tsubst_expr, at cp/pt.cc:21919
      2 |   sizeof []<int=0>(T=x) { return 0; }();
        |          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~

For this example, the template lambda will build with an independent unevaluated
context. When convert default arguments, handling `int x` will be in a no 
unevaluated
operand context, the code `gcc_assert (cp_unevaluated_operand)` will make ICE.
So just remove this assert, and the code will get an effective error 
information:
"‘x’ is not captured".

Without the sizeof we get the better error "parameter 'x' cannot appear
in this context"; capturing or not isn't the reason it's ill-formed.

It seems like this code:

             /* Check to see if DECL is a local variable in a context
                where that is forbidden.  */
             if ((parser->local_variables_forbidden_p & LOCAL_VARS_FORBIDDEN)
                 && local_variable_p (decl)
                 /* DR 2082 permits local variables in unevaluated contexts
                    within a default argument.  */
                 && !cp_unevaluated_operand)

is confused by the sizeof; I guess we want to cp_evaluated for default
arguments like we do for template arguments.

Jason

Thanks, to fix this bug should indeed be handled when lambda parsing.
I will make the following modifications.

-- >8 --

This patch would like to avoid the ICE when template lambdas call with
default parameters in unevaluated context. The bug is the same as
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=119385. For example as blow:

     1   | template <class T>
     2   | void foo(T x) {
     3   |   sizeof []<int=0>(T=x) { return 0; }();
     4   | }
     5   |
     6   | void test {
     7   |   foo(0);
     8   | }

when compile with -fsyntax-only -std=c++20, it will have ICE similar as below

test.cc: In instantiation of 'void foo(T) [with T = int]':
test.cc:7:6:   required from here
     6 |   foo(0);
       |   ~~~^~~
test.cc:3:38: internal compiler error: in tsubst_expr, at cp/pt.cc:21919
     2 |   sizeof []<int=0>(T=x) { return 0; }();
       |          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~

And if without the template code `<int=0>`, the code will pass compile, it's 
wrong.

When parsing lambda, the sizeof will affect the lambda internal unevaluated 
operand
being handled. So consider save/restore cp_unevaluated_operand.

gcc/cp/ChangeLog:

        * parser.cc (cp_parser_lambda_expression): Save/restore
        cp_unevaluated_operand when parser lambda.

gcc/testsuite/ChangeLog:

        * g++.dg/cpp2a/lambda-uneval25.C: New test.
---
  gcc/cp/parser.cc                             |  4 ++++
  gcc/testsuite/g++.dg/cpp2a/lambda-uneval25.C | 11 +++++++++++
  2 files changed, 15 insertions(+)
  create mode 100644 gcc/testsuite/g++.dg/cpp2a/lambda-uneval25.C

diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 57a461042bf..9cc51f57fa7 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -11754,6 +11754,8 @@ cp_parser_lambda_expression (cp_parser* parser)
      /* Inside the class, surrounding template-parameter-lists do not apply.  
*/
      unsigned int saved_num_template_parameter_lists
          = parser->num_template_parameter_lists;
+    /* Inside the lambda, outside unevaluated context do not apply.   */
+    int saved_cp_unevaluated_operand = cp_unevaluated_operand;

Instead of following the surrounding pattern, please use cp_evaluated. That avoids the need for any change in the other two places.

      unsigned char in_statement = parser->in_statement;
      bool in_switch_statement_p = parser->in_switch_statement_p;
      bool fully_implicit_function_template_p
@@ -11765,6 +11767,7 @@ cp_parser_lambda_expression (cp_parser* parser)
      bool saved_omp_array_section_p = parser->omp_array_section_p;
parser->num_template_parameter_lists = 0;
+    cp_unevaluated_operand = 0;
      parser->in_statement = 0;
      parser->in_switch_statement_p = false;
      parser->fully_implicit_function_template_p = false;
@@ -11814,6 +11817,7 @@ cp_parser_lambda_expression (cp_parser* parser)
      in_discarded_stmt = discarded;
parser->num_template_parameter_lists = saved_num_template_parameter_lists;
+    cp_unevaluated_operand = saved_cp_unevaluated_operand;
      parser->in_statement = in_statement;
      parser->in_switch_statement_p = in_switch_statement_p;
      parser->fully_implicit_function_template_p
diff --git a/gcc/testsuite/g++.dg/cpp2a/lambda-uneval25.C 
b/gcc/testsuite/g++.dg/cpp2a/lambda-uneval25.C
new file mode 100644
index 00000000000..7fdd44d3ddd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/lambda-uneval25.C
@@ -0,0 +1,11 @@
+// { dg-do compile { target c++20 } }
+
+template <class T>
+void foo(T x) {
+  sizeof []<int=0>(T=x) { return 0; }(); // { dg-error "may not appear" }
+  sizeof [](T=x) { return 0; }(); // { dg-error "may not appear" }
+};
+
+void test() {
+  foo(0);
+}

Reply via email to