On 7/30/25 7:03 PM, Marek Polacek wrote:
On Wed, Jul 30, 2025 at 03:30:06PM -0400, Jason Merrill wrote:
On 7/25/25 3:59 PM, Marek Polacek wrote:
On Tue, Jul 22, 2025 at 04:52:53PM -0400, Jason Merrill wrote:
On 7/21/25 6:28 PM, Marek Polacek wrote:
On Mon, Jul 21, 2025 at 11:26:08AM -0400, Jason Merrill wrote:
On 7/18/25 5:11 PM, Marek Polacek wrote:
On Thu, Jul 17, 2025 at 06:44:08PM -0400, Jason Merrill wrote:
On 7/17/25 6:25 PM, Jakub Jelinek wrote:
On Thu, Jul 17, 2025 at 05:31:27PM -0400, Jason Merrill wrote:
On 7/16/25 10:49 AM, Marek Polacek wrote:
Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

-- >8 --
This patch implements consteval blocks, as specified by P2996.
They aren't very useful without define_aggregate, but having
a reviewed implementation on trunk would be great.

consteval {} can be anywhere where a member-declaration or
block-declaration can be.  The expression corresponding to it is:

        [] -> void static consteval compound-statement ()

and it must be a constant expression.

I've used cp_parser_lambda_expression to take care of most of the
parsing.  Since a consteval block can find itself in a template, we
need a vehicle to carry the block for instantiation.  Rather than
inventing a new tree, I'm using STMT_EXPR.

If we need something to trigger immediate evaluation, why not a
STATIC_ASSERT as the proposal suggests?

I thought a STMT_EXPR might be a more appropriate means due to static_assert's
true/false nature.  But using a STATIC_ASSERT does simplify the code, so
I've made that change.

Isn't it a problem that static_assert constant evaluates it multiple times
(at least when it isn't constant)?

Hmm, perhaps, for stateful evaluation like define_aggregate.  But we could
address that with a flag on the STATIC_ASSERT like this patch puts on the
STMT_EXPR.

Thanks.  What do you think about this?  I didn't have to change the
tests at all.

Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

-- >8 --
This patch implements consteval blocks, as specified by P2996.
They aren't very useful without define_aggregate, but having
a reviewed implementation on trunk would be great.

consteval {} can be anywhere where a member-declaration or
block-declaration can be.  The expression corresponding to it is:

      [] -> void static consteval compound-statement ()

and it must be a constant expression.

I've used cp_parser_lambda_expression to take care of most of the
parsing.  Since a consteval block can find itself in a template, we
need a vehicle to carry the block for instantiation.  Rather than
inventing a new tree, I'm using STATIC_ASSERT.

A consteval block can't return a value but that is checked by virtue
of the lambda having a void return type.

        PR c++/120775

+++ b/gcc/testsuite/g++.dg/cpp26/consteval-block1.C
...
+/* __func__ won't be set.  Make sure we don't crash.  */
+consteval { __func__; }
+struct F {
+  consteval { __func__; }
+};
+template<typename>
+struct TF {
+  consteval { __func__; }
+};

If it isn't set (per
https://eel.is/c++draft/expr.prim.lambda.closure#16), we should get the
usual "not defined outside function scope" diagnostic.

Ah right.  But then I need another flag (LAMBDA_EXPR_CONSTEVAL_BLOCK_P)
because I can have a consteval block in a lambda and a lambda in
a consteval block etc.  But I guess I will need such a flag anyway
for Reflection later.  That should be OK because LAMBDA_EXPR flags
aren't that scarce (and we have tree_lambda_expr too).

Agreed, but...

    tree
    finish_fname (tree id)
    {
-  tree decl;
-
-  decl = fname_decl (input_location, C_RID_CODE (id), id);
+  tree decl = fname_decl (input_location, C_RID_CODE (id), id);
+  /* [expr.prim.lambda.closure]/16 "Unless the compound-statement is that
+     of a consteval-block-declaration, a variable __func__ is implicitly
+     defined...".  */
+  if (in_consteval_block_p ())
+    pedwarn (input_location, 0, "%qD is not defined outside of function scope",
+            decl);

Rather than this I think we want cp_make_fname_decl to look through
consteval block lambdas (with a variant of current_nonlambda_function that
only skips consteval block lambdas?), so...

If I do that then we won't warn, because current_function_decl has to
be null in fname_decl for us to get then warning.  And overriding
current_function_decl in finish_fname doesn't work:

    consteval { __func__; }
    consteval { __func__; }

We push __func__ to top level, then finish_function -> finish_fname_decls
clears fname_vars, then the second block pushes __func__ again, and we
error.

But I can tweak current_nonlambda_function like below and then we get
the correct warnings.

diff --git a/gcc/testsuite/g++.dg/cpp26/consteval-block8.C 
b/gcc/testsuite/g++.dg/cpp26/consteval-block8.C
new file mode 100644
index 00000000000..7695338b367
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/consteval-block8.C
@@ -0,0 +1,38 @@
+// { dg-do compile { target c++26 } }
+// Test consteval blocks, as specified by P2996.
+
+/* __func__ won't be set.  Make sure we warn.  */
+consteval { __func__; }          // { dg-error "outside of function scope" }
+consteval { { __func__; } }      // { dg-error "outside of function scope" }
+consteval { []() mutable consteval -> void { __func__; } (); }      // { dg-bogus 
"outside of function scope" }
+consteval { []() mutable consteval -> void { consteval { __func__; } } (); } // { 
dg-error "outside of function scope" }

...this should work, lookup should find the __func__ for the normal lambda.
And similarly for several of the other examples in the testcase.

Fair enough.

Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

-- >8 --
This patch implements consteval blocks, as specified by P2996.
They aren't very useful without define_aggregate, but having
a reviewed implementation on trunk would be great.

consteval {} can be anywhere where a member-declaration or
block-declaration can be.  The expression corresponding to it is:

    [] -> void static consteval compound-statement ()

and it must be a constant expression.

I've used cp_parser_lambda_expression to take care of most of the
parsing.  Since a consteval block can find itself in a template, we
need a vehicle to carry the block for instantiation.  Rather than
inventing a new tree, I'm using STATIC_ASSERT.

A consteval block can't return a value but that is checked by virtue
of the lambda having a void return type.

@@ -12641,6 +12651,14 @@ finish_static_assert (tree condition, tree message, 
location_t location,
         return;
       }
+  /* Evaluate the consteval { }.  This must be done only once.  */
+  if (consteval_block_p)
+    {
+      condition = extract_call_expr (condition);

How about calling extract_call_expr in the void expr section of
cxx_eval_outermost_ instead of here?  I imagine other consteval member
functions could get similarly obfuscated by keep_unused_object_arg.

Might could.

Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?

OK.

-- >8 --
This patch implements consteval blocks, as specified by P2996.
They aren't very useful without define_aggregate, but having
a reviewed implementation on trunk would be great.

consteval {} can be anywhere where a member-declaration or
block-declaration can be.  The expression corresponding to it is:

   [] -> void static consteval compound-statement ()

and it must be a constant expression.

I've used cp_parser_lambda_expression to take care of most of the
parsing.  Since a consteval block can find itself in a template, we
need a vehicle to carry the block for instantiation.  Rather than
inventing a new tree, I'm using STATIC_ASSERT.

A consteval block can't return a value but that is checked by virtue
of the lambda having a void return type.

        PR c++/120775

gcc/cp/ChangeLog:

        * constexpr.cc (cxx_eval_outermost_constant_expr): Use
        extract_call_expr.
        * cp-tree.h (CONSTEVAL_BLOCK_P, LAMBDA_EXPR_CONSTEVAL_BLOCK_P): Define.
        (finish_static_assert): Adjust declaration.
        (current_nonlambda_function): Likewise.
        * lambda.cc (current_nonlambda_function): New parameter.  Only keep
        iterating if the function represents a consteval block.
        * parser.cc (cp_parser_lambda_expression): New parameter for
        consteval blocks.  Use it.  Set LAMBDA_EXPR_CONSTEVAL_BLOCK_P.
        (cp_parser_lambda_declarator_opt): Likewise.
        (build_empty_string): New.
        (cp_parser_next_tokens_are_consteval_block_p): New.
        (cp_parser_consteval_block): New.
        (cp_parser_block_declaration): Handle consteval blocks.
        (cp_parser_static_assert): Use build_empty_string.
        (cp_parser_member_declaration): Handle consteval blocks.
        * pt.cc (tsubst_stmt): Adjust a call to finish_static_assert.
        * semantics.cc (finish_fname): Warn for consteval blocks.
        (finish_static_assert): New parameter for consteval blocks.  Set
        CONSTEVAL_BLOCK_P.  Evaluate consteval blocks specially.

gcc/testsuite/ChangeLog:

        * g++.dg/cpp26/consteval-block1.C: New test.
        * g++.dg/cpp26/consteval-block2.C: New test.
        * g++.dg/cpp26/consteval-block3.C: New test.
        * g++.dg/cpp26/consteval-block4.C: New test.
        * g++.dg/cpp26/consteval-block5.C: New test.
        * g++.dg/cpp26/consteval-block6.C: New test.
        * g++.dg/cpp26/consteval-block7.C: New test.
        * g++.dg/cpp26/consteval-block8.C: New test.
---
  gcc/cp/constexpr.cc                           |   7 +-
  gcc/cp/cp-tree.h                              |  14 +-
  gcc/cp/lambda.cc                              |  12 +-
  gcc/cp/parser.cc                              | 135 ++++++++++++++----
  gcc/cp/pt.cc                                  |   3 +-
  gcc/cp/semantics.cc                           |  27 +++-
  gcc/testsuite/g++.dg/cpp26/consteval-block1.C |  82 +++++++++++
  gcc/testsuite/g++.dg/cpp26/consteval-block2.C |  49 +++++++
  gcc/testsuite/g++.dg/cpp26/consteval-block3.C |  41 ++++++
  gcc/testsuite/g++.dg/cpp26/consteval-block4.C |  41 ++++++
  gcc/testsuite/g++.dg/cpp26/consteval-block5.C |  70 +++++++++
  gcc/testsuite/g++.dg/cpp26/consteval-block6.C | 108 ++++++++++++++
  gcc/testsuite/g++.dg/cpp26/consteval-block7.C |  12 ++
  gcc/testsuite/g++.dg/cpp26/consteval-block8.C |  38 +++++
  14 files changed, 600 insertions(+), 39 deletions(-)
  create mode 100644 gcc/testsuite/g++.dg/cpp26/consteval-block1.C
  create mode 100644 gcc/testsuite/g++.dg/cpp26/consteval-block2.C
  create mode 100644 gcc/testsuite/g++.dg/cpp26/consteval-block3.C
  create mode 100644 gcc/testsuite/g++.dg/cpp26/consteval-block4.C
  create mode 100644 gcc/testsuite/g++.dg/cpp26/consteval-block5.C
  create mode 100644 gcc/testsuite/g++.dg/cpp26/consteval-block6.C
  create mode 100644 gcc/testsuite/g++.dg/cpp26/consteval-block7.C
  create mode 100644 gcc/testsuite/g++.dg/cpp26/consteval-block8.C

diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index f92beb17906..e051a50fe16 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -10329,11 +10329,14 @@ cxx_eval_outermost_constant_expr (tree t, bool 
allow_non_constant,
        {
          if (cxx_dialect < cxx20)
            return t;
-         if (TREE_CODE (t) != CALL_EXPR && TREE_CODE (t) != AGGR_INIT_EXPR)
+         /* We could have a COMPOUND_EXPR here coming from
+            keep_unused_object_arg.  */
+         tree x = extract_call_expr (t);
+         if (x == NULL_TREE || x == error_mark_node)
            return t;
          /* Calls to immediate functions returning void need to be
             evaluated.  */
-         tree fndecl = cp_get_callee_fndecl_nofold (t);
+         tree fndecl = cp_get_callee_fndecl_nofold (x);
          if (fndecl == NULL_TREE || !DECL_IMMEDIATE_FUNCTION_P (fndecl))
            return t;
          else
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 0ac3ecbd4c3..fb8e0d8d98e 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -453,6 +453,8 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
        RETURN_EXPR_LOCAL_ADDR_P (in RETURN_EXPR)
        PACK_INDEX_PARENTHESIZED_P (in PACK_INDEX_*)
        MUST_NOT_THROW_NOEXCEPT_P (in MUST_NOT_THROW_EXPR)
+      CONSTEVAL_BLOCK_P (in STATIC_ASSERT)
+      LAMBDA_EXPR_CONSTEVAL_BLOCK_P (in LAMBDA_EXPR)
     1: IDENTIFIER_KIND_BIT_1 (in IDENTIFIER_NODE)
        TI_PENDING_TEMPLATE_FLAG.
        TEMPLATE_PARMS_FOR_INLINE.
@@ -1433,6 +1435,10 @@ struct GTY (()) tree_deferred_noexcept {
  #define STATIC_ASSERT_SOURCE_LOCATION(NODE) \
    (((struct tree_static_assert *)STATIC_ASSERT_CHECK (NODE))->location)
+/* True if this static assert represents a C++26 consteval block. */
+#define CONSTEVAL_BLOCK_P(NODE) \
+  TREE_LANG_FLAG_0 (STATIC_ASSERT_CHECK (NODE))
+
  struct GTY (()) tree_static_assert {
    struct tree_base base;
    tree condition;
@@ -1547,6 +1553,10 @@ enum cp_lambda_default_capture_mode_type {
  #define LAMBDA_EXPR_THIS_CAPTURE(NODE) \
    (((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->this_capture)
+/* True iff this lambda was created for a consteval block. */
+#define LAMBDA_EXPR_CONSTEVAL_BLOCK_P(NODE) \
+  TREE_LANG_FLAG_0 (LAMBDA_EXPR_CHECK (NODE))
+
  /* True iff uses of a const variable capture were optimized away.  */
  #define LAMBDA_EXPR_CAPTURE_OPTIMIZED(NODE) \
    TREE_LANG_FLAG_2 (LAMBDA_EXPR_CHECK (NODE))
@@ -8243,7 +8253,7 @@ extern bool cxx_omp_create_clause_info            (tree, 
tree, bool, bool,
                                                 bool, bool);
  extern tree baselink_for_fns                    (tree);
  extern void finish_static_assert                (tree, tree, location_t,
-                                                bool, bool);
+                                                bool, bool, bool = false);
  extern tree finish_decltype_type                (tree, bool, tsubst_flags_t);
  extern tree fold_builtin_is_corresponding_member (location_t, int, tree *);
  extern tree fold_builtin_is_pointer_inverconvertible_with_class (location_t, 
int, tree *);
@@ -8268,7 +8278,7 @@ extern void register_capture_members              (tree);
  extern tree lambda_expr_this_capture            (tree, int);
  extern void maybe_generic_this_capture                (tree, tree);
  extern tree maybe_resolve_dummy                       (tree, bool);
-extern tree current_nonlambda_function         (void);
+extern tree current_nonlambda_function         (bool = false);
  extern tree nonlambda_method_basetype         (void);
  extern tree current_nonlambda_scope           (bool = false);
  extern tree current_lambda_expr                       (void);
diff --git a/gcc/cp/lambda.cc b/gcc/cp/lambda.cc
index ecf55eb94d0..c798967f8e1 100644
--- a/gcc/cp/lambda.cc
+++ b/gcc/cp/lambda.cc
@@ -1038,13 +1038,19 @@ maybe_generic_this_capture (tree object, tree fns)
        }
  }
-/* Returns the innermost non-lambda function. */
+/* Returns the innermost non-lambda function.  If ONLY_SKIP_CONSTEVAL_BLOCK_P,
+   we only skip lambda functions that represent consteval blocks.  */
tree
-current_nonlambda_function (void)
+current_nonlambda_function (bool only_skip_consteval_block_p/*=false*/)
  {
    tree fn = current_function_decl;
-  while (fn && LAMBDA_FUNCTION_P (fn))
+  tree lam;
+  while (fn && LAMBDA_FUNCTION_P (fn)
+        && (!only_skip_consteval_block_p
+            /* Only keep going if FN represents a consteval block.  */
+            || ((lam = CLASSTYPE_LAMBDA_EXPR (CP_DECL_CONTEXT (fn)))
+                && LAMBDA_EXPR_CONSTEVAL_BLOCK_P (lam))))
      fn = decl_function_context (fn);
    return fn;
  }
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index eb66427a85d..860f3f0edaf 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -2576,11 +2576,11 @@ static cp_expr cp_parser_constant_expression
  static cp_expr cp_parser_builtin_offsetof
    (cp_parser *);
  static cp_expr cp_parser_lambda_expression
-  (cp_parser *);
+  (cp_parser *, bool = false);
  static void cp_parser_lambda_introducer
    (cp_parser *, tree);
  static bool cp_parser_lambda_declarator_opt
-  (cp_parser *, tree);
+  (cp_parser *, tree, bool = false);
  static void cp_parser_lambda_body
    (cp_parser *, tree);
@@ -11744,10 +11744,14 @@ cp_parser_trait (cp_parser* parser, const cp_trait* trait)
       lambda-introducer < template-parameter-list > requires-clause [opt]
         lambda-declarator [opt] compound-statement
+ If CONSTEVAL_BLOCK_P is true, we are parsing a consteval block, which
+   is syntactic sugar for a consteval lambda.
+
     Returns a representation of the expression.  */
static cp_expr
-cp_parser_lambda_expression (cp_parser* parser)
+cp_parser_lambda_expression (cp_parser* parser,
+                            bool consteval_block_p/*=false*/)
  {
    tree lambda_expr = build_lambda_expr ();
    tree type;
@@ -11756,6 +11760,7 @@ cp_parser_lambda_expression (cp_parser* parser)
    cp_token_position start = 0;
LAMBDA_EXPR_LOCATION (lambda_expr) = token->location;
+  LAMBDA_EXPR_CONSTEVAL_BLOCK_P (lambda_expr) = consteval_block_p;
if (cxx_dialect >= cxx20)
      {
@@ -11799,9 +11804,12 @@ cp_parser_lambda_expression (cp_parser* parser)
       it now.  */
    push_deferring_access_checks (dk_no_deferred);
- cp_parser_lambda_introducer (parser, lambda_expr);
-  if (cp_parser_error_occurred (parser))
-    return error_mark_node;
+  if (!consteval_block_p)
+    {
+      cp_parser_lambda_introducer (parser, lambda_expr);
+      if (cp_parser_error_occurred (parser))
+       return error_mark_node;
+    }
{
      /* OK, this is a bit tricksy.  cp_parser_requires_expression sets
@@ -11873,7 +11881,8 @@ cp_parser_lambda_expression (cp_parser* parser)
      if (cp_parser_start_tentative_firewall (parser))
        start = token;
- ok &= cp_parser_lambda_declarator_opt (parser, lambda_expr);
+    ok &= cp_parser_lambda_declarator_opt (parser, lambda_expr,
+                                          consteval_block_p);
if (ok && cp_parser_error_occurred (parser))
        ok = false;
@@ -12256,10 +12265,13 @@ cp_parser_lambda_introducer (cp_parser* parser, tree 
lambda_expr)
       decl-specifier-seq [opt] noexcept-specifier [opt]
         attribute-specifier-seq [opt] trailing-return-type [opt]
- LAMBDA_EXPR is the current representation of the lambda expression. */
+   LAMBDA_EXPR is the current representation of the lambda expression.
+   If CONSTEVAL_BLOCK_P is true, we are parsing a consteval block, which
+   is syntactic sugar for a consteval lambda.  */
static bool
-cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
+cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr,
+                                bool consteval_block_p/*=false*/)
  {
    /* 5.1.1.4 of the standard says:
         If a lambda-expression does not include a lambda-declarator, it is as 
if
@@ -12362,6 +12374,18 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, 
tree lambda_expr)
                                  CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR,
                                  &lambda_specs, &declares_class_or_enum);
+ /* [dcl.pre] For a consteval-block-declaration D, the expression E
+     corresponding to D is:
+       [] -> void static consteval compound-statement ()
+     Make it so.  */
+  if (consteval_block_p)
+    {
+      return_type = void_type_node;
+      lambda_specs.storage_class = sc_static;
+      set_and_check_decl_spec_loc (&lambda_specs, ds_consteval,
+                                  cp_lexer_peek_token (parser->lexer));
+    }
+
    if (omitted_parms_loc && lambda_specs.any_specifiers_p)
      {
        pedwarn (omitted_parms_loc, OPT_Wc__23_extensions,
@@ -16300,6 +16324,56 @@ cp_parser_toplevel_declaration (cp_parser* parser)
      cp_parser_declaration (parser, NULL_TREE);
  }
+/* Build an empty string for static_assert. */
+
+static tree
+build_empty_string ()
+{
+  tree message = build_string (1, "");
+  TREE_TYPE (message) = char_array_type_node;
+  fix_string_type (message);
+  return message;
+}
+
+/* Return true iff the next tokens start a C++26 consteval block.  */
+
+static bool
+cp_parser_next_tokens_are_consteval_block_p (cp_parser *parser)
+{
+  return (cxx_dialect >= cxx26
+         && cp_lexer_next_token_is_keyword (parser->lexer, RID_CONSTEVAL)
+         && cp_lexer_nth_token_is (parser->lexer, 2, CPP_OPEN_BRACE));
+}
+
+/* Parse a consteval-block-declaration.
+
+   consteval-block-declaration:
+     consteval compound-statement
+
+   If MEMBER_P, this consteval block is a member declaration.  */
+
+static void
+cp_parser_consteval_block (cp_parser *parser, bool member_p)
+{
+  const location_t loc = cp_lexer_peek_token (parser->lexer)->location;
+  /* Consume the 'consteval'.  */
+  cp_lexer_consume_token (parser->lexer);
+
+  /* We know the next token is '{'.  Let cp_parser_lambda_body handle it.  */
+  cp_expr lam = cp_parser_lambda_expression (parser,
+                                            /*consteval_block_p=*/true);
+  if (!cp_parser_error_occurred (parser))
+    {
+      releasing_vec args;
+      tree call = finish_call_expr (lam, &args,
+                                   /*disallow_virtual=*/false,
+                                   /*koenig_p=*/false,
+                                   tf_warning_or_error);
+      finish_static_assert (call, build_empty_string (), loc, member_p,
+                           /*show_expr_p=*/false, /*consteval_block_p=*/true);
+    }
+}
+
  /* Parse a block-declaration.
block-declaration:
@@ -16307,18 +16381,18 @@ cp_parser_toplevel_declaration (cp_parser* parser)
       asm-definition
       namespace-alias-definition
       using-declaration
+     using-enum-declaration
       using-directive
+     static_assert-declaration
+     consteval-block-declaration
+     alias-declaration
+     opaque-enum-declaration
GNU Extension: block-declaration:
       __extension__ block-declaration
- C++0x Extension:
-
-   block-declaration:
-     static_assert-declaration
-
     If STATEMENT_P is TRUE, then this block-declaration is occurring as
     part of a declaration-statement.  */
@@ -16396,6 +16470,8 @@ cp_parser_block_declaration (cp_parser *parser,
    /* If the next token is `static_assert' we have a static assertion.  */
    else if (token1->keyword == RID_STATIC_ASSERT)
      cp_parser_static_assert (parser, /*member_p=*/false);
+  else if (cp_parser_next_tokens_are_consteval_block_p (parser))
+    cp_parser_consteval_block (parser, /*member_p=*/false);
    else
      {
        size_t attr_idx = cp_parser_skip_std_attribute_spec_seq (parser, 1);
@@ -17699,9 +17775,7 @@ cp_parser_static_assert (cp_parser *parser, bool 
member_p)
                 "only available with %<-std=c++17%> or %<-std=gnu++17%>");
        /* Eat the ')'  */
        cp_lexer_consume_token (parser->lexer);
-      message = build_string (1, "");
-      TREE_TYPE (message) = char_array_type_node;
-      fix_string_type (message);
+      message = build_empty_string ();
      }
    else
      {
@@ -28831,12 +28905,20 @@ cp_parser_member_specification_opt (cp_parser* parser)
  /* Parse a member-declaration.
member-declaration:
-     decl-specifier-seq [opt] member-declarator-list [opt] ;
-     function-definition ; [opt]
-     :: [opt] nested-name-specifier template [opt] unqualified-id ;
+     attribute-specifier-seq [opt] decl-specifier-seq [opt]
+       member-declarator-list [opt] ;
+     function-definition
+     friend-type-declaration
       using-declaration
+     using-enum-declaration
+     static_assert-declaration
+     consteval-block-declaration
       template-declaration
+     explicit-specialization
+     deduction-guide
       alias-declaration
+     opaque-enum-declaration
+     empty-declaration
member-declarator-list:
       member-declarator
@@ -28855,12 +28937,7 @@ cp_parser_member_specification_opt (cp_parser* parser)
     member-declarator:
       declarator attributes [opt] pure-specifier [opt]
       declarator attributes [opt] constant-initializer [opt]
-     identifier [opt] attributes [opt] : constant-expression
-
-   C++0x Extensions:
-
-   member-declaration:
-     static_assert-declaration  */
+     identifier [opt] attributes [opt] : constant-expression  */
static void
  cp_parser_member_declaration (cp_parser* parser)
@@ -28960,6 +29037,12 @@ cp_parser_member_declaration (cp_parser* parser)
        return;
      }
+ if (cp_parser_next_tokens_are_consteval_block_p (parser))
+    {
+      cp_parser_consteval_block (parser, /*member_p=*/true);
+      return;
+    }
+
    parser->colon_corrects_to_scope_p = false;
cp_omp_declare_simd_data odsd;
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 0b7a05c3455..acfeb816592 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -19601,7 +19601,8 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t 
complain, tree in_decl)
finish_static_assert (condition, message,
                                STATIC_ASSERT_SOURCE_LOCATION (t),
-                             /*member_p=*/false, /*show_expr_p=*/true);
+                             /*member_p=*/false, /*show_expr_p=*/true,
+                             CONSTEVAL_BLOCK_P (t));
        }
        break;
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 52ecac4e4cc..be79b5078f6 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -3992,9 +3992,15 @@ finish_compound_literal (tree type, tree 
compound_literal,
  tree
  finish_fname (tree id)
  {
-  tree decl;
-
-  decl = fname_decl (input_location, C_RID_CODE (id), id);
+  tree decl = fname_decl (input_location, C_RID_CODE (id), id);
+  /* [expr.prim.lambda.closure]/16 "Unless the compound-statement is that
+     of a consteval-block-declaration, a variable __func__ is implicitly
+     defined...".  We could be in a consteval block in a function, though,
+     and then we shouldn't warn.  */
+  if (current_function_decl
+      && !current_nonlambda_function (/*only_skip_consteval_block_p=*/true))
+    pedwarn (input_location, 0, "%qD is not defined outside of function scope",
+            decl);
    if (processing_template_decl && current_function_decl
        && decl != error_mark_node)
      decl = DECL_NAME (decl);
@@ -12598,11 +12604,14 @@ cexpr_str::extract (location_t location, const char * & 
msg, int &len)
     CONDITION and the message text MESSAGE.  LOCATION is the location
     of the static assertion in the source code.  When MEMBER_P, this
     static assertion is a member of a class.  If SHOW_EXPR_P is true,
-   print the condition (because it was instantiation-dependent).  */
+   print the condition (because it was instantiation-dependent).
+   If CONSTEVAL_BLOCK_P is true, this static assertion represents
+   a consteval block.  */
void
  finish_static_assert (tree condition, tree message, location_t location,
-                     bool member_p, bool show_expr_p)
+                     bool member_p, bool show_expr_p,
+                     bool consteval_block_p/*=false*/)
  {
    tsubst_flags_t complain = tf_warning_or_error;
@@ -12630,6 +12639,7 @@ finish_static_assert (tree condition, tree message, location_t location,
        STATIC_ASSERT_CONDITION (assertion) = orig_condition;
        STATIC_ASSERT_MESSAGE (assertion) = cstr.message;
        STATIC_ASSERT_SOURCE_LOCATION (assertion) = location;
+      CONSTEVAL_BLOCK_P (assertion) = consteval_block_p;
if (member_p)
          maybe_add_class_template_decl_list (current_class_type,
@@ -12641,6 +12651,13 @@ finish_static_assert (tree condition, tree message, 
location_t location,
        return;
      }
+ /* Evaluate the consteval { }. This must be done only once. */
+  if (consteval_block_p)
+    {
+      cxx_constant_value (condition);
+      return;
+    }
+
    /* Fold the expression and convert it to a boolean value. */
    condition = contextual_conv_bool (condition, complain);
    condition = fold_non_dependent_expr (condition, complain,
diff --git a/gcc/testsuite/g++.dg/cpp26/consteval-block1.C 
b/gcc/testsuite/g++.dg/cpp26/consteval-block1.C
new file mode 100644
index 00000000000..9e2cf22737e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/consteval-block1.C
@@ -0,0 +1,82 @@
+// { dg-do compile { target c++26 } }
+// Test consteval blocks, as specified by P2996.
+
+constexpr int fn () { return 42; }
+struct M {
+  static consteval void foo () {}
+};
+
+consteval { }
+consteval { fn (); }
+consteval { M::foo (); }
+consteval { auto x = fn (); return; }
+consteval {
+  [](int i) { return i; }(5);
+}
+auto lam = [] { };
+consteval { lam (); }
+
+struct S {
+  consteval { }
+};
+
+struct S2 {
+  consteval { fn(); }
+};
+
+class C {
+  consteval { }
+};
+
+class C2 {
+  consteval { M::foo (); }
+};
+
+union U {
+  consteval { }
+};
+
+template<typename>
+struct TS {
+  consteval { }
+};
+
+template<typename... Ts>
+struct TS2 {
+  consteval {
+    (Ts::foo (), ...);
+  }
+};
+
+TS2<M> ts2;
+
+void
+g ()
+{
+  consteval { }
+}
+
+template<typename>
+void
+tg ()
+{
+  consteval { }
+}
+
+void die ();
+constexpr int
+bar (int i)
+{
+  if (i != 42)
+    die ();
+  return 0;
+}
+
+void
+foo ()
+{
+  constexpr int r = 42;
+  consteval {
+    bar (r);
+  }
+}
diff --git a/gcc/testsuite/g++.dg/cpp26/consteval-block2.C 
b/gcc/testsuite/g++.dg/cpp26/consteval-block2.C
new file mode 100644
index 00000000000..895fcb63867
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/consteval-block2.C
@@ -0,0 +1,49 @@
+// { dg-do compile { target c++26 } }
+// Test consteval blocks, as specified by P2996.
+
+void fn ();
+
+consteval { fn (); }       // { dg-error "call to non-.constexpr. function" }
+consteval { return 42; }    // { dg-error "return-statement with a value" }
+
+struct S {
+  consteval {
+    fn ();                 // { dg-error "call to non-.constexpr. function" }
+  }
+  consteval {
+    return 42;             // { dg-error "return-statement with a value" }
+  }
+};
+
+template<typename T>
+constexpr void foo (T t) { return t; } // { dg-error "return-statement with a 
value" }
+
+template<int N>
+struct R {
+  consteval { foo (N); }
+};
+
+R<1> r;
+
+template<typename T>
+constexpr void foo2 (T t) { return t; }        // { dg-error "return-statement with 
a value" }
+
+template<int N>
+void
+f ()
+{
+  consteval { foo2 (1); }
+}
+
+constexpr int bar (int) { return 0; }
+
+void
+g ()
+{
+  f<1>();
+
+  int r = 42;
+  consteval {
+    bar (r);         // { dg-error ".r. is not captured" }
+  }
+}
diff --git a/gcc/testsuite/g++.dg/cpp26/consteval-block3.C 
b/gcc/testsuite/g++.dg/cpp26/consteval-block3.C
new file mode 100644
index 00000000000..c1221c3019f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/consteval-block3.C
@@ -0,0 +1,41 @@
+// { dg-do compile { target c++26 } }
+// Test consteval blocks, as specified by P2996.
+// Test that we actually evaluate the consteval block.
+
+void bar () { }
+
+template<int N>
+constexpr void
+fn ()
+{
+  if (N > 0)
+    bar ();                    // { dg-error "call to non-.constexpr. 
function" }
+}
+
+template<int N>
+struct S {
+  consteval { fn<N>(); } // { dg-error "called in a constant expression" }
+};
+
+S<1> s;
+
+template<int N>
+constexpr void
+fn2 ()
+{
+  if (N > 0)
+    bar ();                    // { dg-error "call to non-.constexpr. 
function" }
+}
+
+template<int N>
+void
+g ()
+{
+  consteval { fn2<N>(); }        // { dg-error "called in a constant 
expression" }
+}
+
+void
+f ()
+{
+  g<1>();
+}
diff --git a/gcc/testsuite/g++.dg/cpp26/consteval-block4.C 
b/gcc/testsuite/g++.dg/cpp26/consteval-block4.C
new file mode 100644
index 00000000000..be95e170ee1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/consteval-block4.C
@@ -0,0 +1,41 @@
+// { dg-do compile { target c++26 } }
+// Test consteval blocks, as specified by P2996.
+// Test that we actually evaluate the consteval block.
+
+void bar () { }
+
+template<int N>
+constexpr void
+fn ()
+{
+  if (N > 0)
+    bar ();
+}
+
+template<int N>
+struct S {
+  consteval { fn<N>(); }
+};
+
+S<0> s;
+
+template<int N>
+constexpr void
+fn2 ()
+{
+  if (N > 0)
+    bar ();
+}
+
+template<int N>
+void
+g ()
+{
+  consteval { fn2<N>(); }
+}
+
+void
+f ()
+{
+  g<0>();
+}
diff --git a/gcc/testsuite/g++.dg/cpp26/consteval-block5.C 
b/gcc/testsuite/g++.dg/cpp26/consteval-block5.C
new file mode 100644
index 00000000000..462cebe6cc9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/consteval-block5.C
@@ -0,0 +1,70 @@
+// { dg-do compile { target c++26 } }
+// Test consteval blocks, as specified by P2996.
+
+void bar () { }
+
+template<int N>
+constexpr void
+fn ()
+{
+  if (N > 0)
+    bar ();
+}
+
+template<typename>
+struct S {
+  consteval { fn<1>(); }
+};
+
+template<>
+struct S<int> {
+  consteval { fn<0>(); }
+};
+
+S<int> s1;
+
+template<typename T>
+struct S<T*> {
+  consteval { fn<0>(); }
+};
+
+S<int *> s2;
+
+template<typename T, int N>
+struct W {
+  consteval { T t; fn<N - 1>(); }
+};
+
+template<typename T>
+struct W<T, 0> {
+  consteval { T t; fn<0>(); }
+};
+
+template<>
+struct W<char, 0> {
+  consteval { fn<0>(); }
+};
+
+W<int, 0> w1;
+W<int, 1> w2;
+W<char, 0> w3;
+
+template<typename>
+void
+f ()
+{
+  consteval { fn<1>(); }
+}
+
+template<>
+void
+f<int> ()
+{
+  consteval { fn<0>(); }
+}
+
+void
+g ()
+{
+  f<int> ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp26/consteval-block6.C 
b/gcc/testsuite/g++.dg/cpp26/consteval-block6.C
new file mode 100644
index 00000000000..ca90b3e376f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/consteval-block6.C
@@ -0,0 +1,108 @@
+// { dg-do compile { target c++26 } }
+// Test consteval blocks, as specified by P2996.
+
+void die () {}
+
+template<int N>
+constexpr void
+fn ()
+{
+  if (N > 0)
+    die ();
+}
+
+template<int N>
+void
+fn2 ()
+{
+  struct S {
+    consteval {
+      fn<N>();
+    }
+  };
+}
+
+template<int N>
+struct A {
+  struct B {
+    consteval {
+      fn<N>();
+    }
+  };
+  template<int M>
+  struct C {
+    consteval {
+      fn<N + M>();
+    }
+  };
+};
+
+template<int N>
+struct D {
+  constexpr static int i = 0;
+  struct E {
+    consteval {
+      fn<i>();
+    }
+  };
+};
+
+A<0>::B b;
+A<0>::C<0> c;
+D<0>::E e;
+
+void
+f ()
+{
+  fn2<0>();
+}
+
+static constexpr int j = 0;
+const int x = 0;
+
+consteval {
+  fn<j>();
+  consteval {
+    fn<j + j>();
+    consteval {
+      fn<j + j + j>();
+      consteval {
+       fn<j + j + x>();
+       consteval {
+         fn<j + x>();
+       }
+      }
+    }
+  }
+}
+
+struct R { constexpr R() {} };
+
+template<int N>
+constexpr auto X = N;
+
+consteval {
+  R{};
+  constexpr auto x = 0;
+  fn<x>();
+  fn<X<0>>();
+  if consteval
+    {
+      fn<j>();
+    }
+  else
+    {
+      die ();
+    }
+}
+
+template<typename T>
+struct G {
+  consteval {
+    using U = T[3];
+    U arr{};
+    int i = arr[2];
+  }
+};
+
+G<int> g;
diff --git a/gcc/testsuite/g++.dg/cpp26/consteval-block7.C 
b/gcc/testsuite/g++.dg/cpp26/consteval-block7.C
new file mode 100644
index 00000000000..231682f8adf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/consteval-block7.C
@@ -0,0 +1,12 @@
+// { dg-do compile { target c++26 } }
+// Test consteval blocks, as specified by P2996.
+
+consteval {
+  template <class T> // { dg-error "template declaration cannot appear at block 
scope" }
+  struct X { };
+
+  template <class T> // { dg-error "template declaration cannot appear at block 
scope" }
+  concept C = true;
+
+  return; // OK
+}
diff --git a/gcc/testsuite/g++.dg/cpp26/consteval-block8.C 
b/gcc/testsuite/g++.dg/cpp26/consteval-block8.C
new file mode 100644
index 00000000000..ad164fdf926
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp26/consteval-block8.C
@@ -0,0 +1,38 @@
+// { dg-do compile { target c++26 } }
+// Test consteval blocks, as specified by P2996.
+
+/* __func__ won't be set.  Make sure we warn.  */
+consteval { __func__; }          // { dg-error "outside of function scope" }
+consteval { { __func__; } }      // { dg-error "outside of function scope" }
+consteval { []() mutable consteval -> void { __func__; } (); }      // { dg-bogus 
"outside of function scope" }
+consteval { []() mutable consteval -> void { consteval { __func__; } } (); } // { 
dg-bogus "outside of function scope" }
+
+auto l = []() -> void {
+  consteval { __func__; } // { dg-bogus "outside of function scope" }
+};
+
+struct F {
+  consteval { __func__; } // { dg-error "outside of function scope" }
+};
+template<typename>
+struct TF {
+  consteval { __func__; } // { dg-error "outside of function scope" }
+};
+
+void
+g ()
+{
+  consteval { __func__; } // { dg-bogus "outside of function scope" }
+  // Not a consteval-block-declaration.
+  []() mutable consteval -> void { __func__; } (); // { dg-bogus "outside of 
function scope" }
+}
+
+template<typename>
+void
+f ()
+{
+  consteval { __func__; } // { dg-bogus "outside of function scope" }
+  { consteval { __func__; } } // { dg-bogus "outside of function scope" }
+  __func__; // { dg-bogus "outside of function scope" }
+  []() mutable consteval -> void { __func__; } (); // { dg-bogus "outside of 
function scope" }
+}

base-commit: ec7244e6e09654a2e720d60ceb0f24c6d66c44f7

Reply via email to