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 > > > > gcc/cp/ChangeLog: > > > > * cp-tree.h (CONSTEVAL_BLOCK_P): Define. > > (finish_static_assert): Adjust declaration. > > * parser.cc (cp_parser_lambda_expression): New parameter for > > consteval blocks. Use it. > > (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_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. > > --- > > gcc/cp/cp-tree.h | 7 +- > > gcc/cp/parser.cc | 134 ++++++++++++++---- > > gcc/cp/pt.cc | 3 +- > > gcc/cp/semantics.cc | 16 ++- > > gcc/testsuite/g++.dg/cpp26/consteval-block1.C | 92 ++++++++++++ > > 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 ++ > > 11 files changed, 543 insertions(+), 30 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 > > > > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h > > index 68102501569..a365237a769 100644 > > --- a/gcc/cp/cp-tree.h > > +++ b/gcc/cp/cp-tree.h > > @@ -453,6 +453,7 @@ 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) > > This is in the _0 block... > > > 1: IDENTIFIER_KIND_BIT_1 (in IDENTIFIER_NODE) > > TI_PENDING_TEMPLATE_FLAG. > > TEMPLATE_PARMS_FOR_INLINE. > > @@ -1433,6 +1434,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_1 (STATIC_ASSERT_CHECK (NODE)) > > ...and then this uses _1. Please change one or the other.
Oop, fixed. > > +++ 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). 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 gcc/cp/ChangeLog: * cp-tree.h (CONSTEVAL_BLOCK_P, LAMBDA_EXPR_CONSTEVAL_BLOCK_P): Define. (in_consteval_block_p): Declare. (finish_static_assert): Adjust declaration. * 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. (in_consteval_block_p): 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/cp-tree.h | 13 +- gcc/cp/parser.cc | 144 ++++++++++++++---- gcc/cp/pt.cc | 3 +- gcc/cp/semantics.cc | 26 +++- 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 +++++ 12 files changed, 594 insertions(+), 33 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/cp-tree.h b/gcc/cp/cp-tree.h index 68102501569..191b045ce3b 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)) @@ -7758,6 +7768,7 @@ extern location_t defparse_location (tree); extern void maybe_show_extern_c_location (void); extern bool literal_integer_zerop (const_tree); extern tree attr_chainon (tree, tree); +extern bool in_consteval_block_p (); /* in pt.cc */ extern tree canonical_type_parameter (tree); @@ -8237,7 +8248,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 *); diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 0d9ed2ea82b..34a3fe86b29 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); @@ -11739,10 +11739,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; @@ -11751,6 +11755,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) { @@ -11794,9 +11799,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 @@ -11868,7 +11876,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; @@ -12251,10 +12260,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 @@ -12357,6 +12369,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, @@ -16294,6 +16318,65 @@ 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)); +} + +/* Return true if we are in a consteval block. */ + +bool +in_consteval_block_p () +{ + tree lam = current_lambda_expr (); + return lam && LAMBDA_EXPR_CONSTEVAL_BLOCK_P (lam); +} + +/* 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: @@ -16301,18 +16384,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. */ @@ -16389,6 +16472,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); @@ -17692,9 +17777,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 { @@ -28824,12 +28907,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 @@ -28848,12 +28939,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) @@ -28952,6 +29038,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 d63fa68058b..64928a50b7c 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -19590,7 +19590,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 ee77d31f097..ca2dbf282d7 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -3992,9 +3992,13 @@ 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...". */ + if (in_consteval_block_p ()) + 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 +12602,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 +12637,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 +12649,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); + 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..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" } + +auto l = []() -> void { + consteval { __func__; } // { dg-error "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-error "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-error "outside of function scope" } + { consteval { __func__; } } // { dg-error "outside of function scope" } + __func__; // { dg-bogus "outside of function scope" } + []() mutable consteval -> void { __func__; } (); // { dg-bogus "outside of function scope" } +} base-commit: 0b6ae75a304e58463796d1058a39cbe483283dee -- 2.50.1