From 117c64266405e244da4dae3ae7b60905af63b955 Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Fri, 11 Jun 2021 15:50:34 +0200 Subject: [PATCH] c++: Add C++23 consteval if support - P1938R3 [PR100974] The following patch implements consteval if support. There is a new IF_STMT_CONSTEVAL_P flag on IF_STMT and IF_COND is boolean_false_node to match the non-manifestly constant evaluation behavior, while constexpr evaluation special-cases it. Perhaps cleaner would be to set the condition to __builtin_is_constant_evaluated () call but we need the IF_STMT_CONSTEVAL_P flag anyway and the IL would be larger. And I'm not changing the libstdc++ side, where perhaps we could change std::is_constant_evaluated definition for #ifdef __cpp_if_consteval case to if consteval { return true; } else { return false; } but we need to keep it defined to __builtin_is_constant_evaluated () for C++20 or older. 2021-06-11 Jakub Jelinek PR c++/100974 gcc/c-family/ * c-cppbuiltin.c (c_cpp_builtins): Predefine __cpp_if_consteval for -std=c++2b for P1938R3 consteval if support. gcc/cp/ * cp-tree.h (struct saved_scope): Add consteval_if_p member. Formatting fix for the discarded_stmt comment. (in_consteval_if_p, IF_STMT_CONSTEVAL_P): Define. * parser.c (cp_parser_lambda_expression): Temporarily disable in_consteval_if_p when parsing lambda body. (cp_parser_selection_statement): Parse consteval if. * decl.c (struct named_label_entry): Add in_consteval_if member. (level_for_consteval_if): New function. (poplevel_named_label_1, check_previous_goto_1, check_goto): Handle consteval if. * constexpr.c (cxx_eval_builtin_function_call): Clarify in comment why CP_BUILT_IN_IS_CONSTANT_EVALUATED needs to *non_constant_p for !ctx->manifestly_const_eval. (cxx_eval_conditional_expression): For IF_STMT_CONSTEVAL_P evaluate condition as if it was __builtin_is_constant_evaluated call. (potential_constant_expression_1): For IF_STMT_CONSTEVAL_P always recurse on both branches. * cp-gimplify.c (genericize_if_stmt): Genericize IF_STMT_CONSTEVAL_P as the else branch. * pt.c (tsubst_expr) : Copy IF_STMT_CONSTEVAL_P. Temporarily set in_consteval_if_p when recursing on IF_STMT_CONSTEVAL_P then branch. (tsubst_lambda_expr): Temporarily disable in_consteval_if_p when instantiating lambda body. * call.c (immediate_invocation_p): Return false when in_consteval_if_p. gcc/testsuite/ * g++.dg/cpp23/consteval-if1.C: New test. * g++.dg/cpp23/consteval-if2.C: New test. * g++.dg/cpp23/consteval-if3.C: New test. * g++.dg/cpp23/consteval-if4.C: New test. * g++.dg/cpp23/consteval-if5.C: New test. * g++.dg/cpp23/consteval-if6.C: New test. * g++.dg/cpp23/consteval-if7.C: New test. * g++.dg/cpp23/consteval-if8.C: New test. * g++.dg/cpp23/consteval-if9.C: New test. * g++.dg/cpp23/consteval-if10.C: New test. * g++.dg/cpp23/feat-cxx2b.C: Add __cpp_if_consteval tests. --- gcc/c-family/c-cppbuiltin.c | 1 + gcc/cp/call.c | 1 + gcc/cp/constexpr.c | 36 ++++++-- gcc/cp/cp-gimplify.c | 8 +- gcc/cp/cp-tree.h | 10 ++- gcc/cp/decl.c | 27 +++++- gcc/cp/parser.c | 102 ++++++++++++++++++++++ gcc/cp/pt.c | 13 +++ gcc/testsuite/g++.dg/cpp23/consteval-if1.C | 103 ++++++++++++++++++++++ gcc/testsuite/g++.dg/cpp23/consteval-if10.C | 36 ++++++++ gcc/testsuite/g++.dg/cpp23/consteval-if2.C | 129 ++++++++++++++++++++++++++++ gcc/testsuite/g++.dg/cpp23/consteval-if3.C | 73 ++++++++++++++++ gcc/testsuite/g++.dg/cpp23/consteval-if4.C | 44 ++++++++++ gcc/testsuite/g++.dg/cpp23/consteval-if5.C | 14 +++ gcc/testsuite/g++.dg/cpp23/consteval-if6.C | 16 ++++ gcc/testsuite/g++.dg/cpp23/consteval-if7.C | 16 ++++ gcc/testsuite/g++.dg/cpp23/consteval-if8.C | 14 +++ gcc/testsuite/g++.dg/cpp23/consteval-if9.C | 11 +++ gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C | 6 ++ 19 files changed, 650 insertions(+), 10 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp23/consteval-if1.C create mode 100644 gcc/testsuite/g++.dg/cpp23/consteval-if10.C create mode 100644 gcc/testsuite/g++.dg/cpp23/consteval-if2.C create mode 100644 gcc/testsuite/g++.dg/cpp23/consteval-if3.C create mode 100644 gcc/testsuite/g++.dg/cpp23/consteval-if4.C create mode 100644 gcc/testsuite/g++.dg/cpp23/consteval-if5.C create mode 100644 gcc/testsuite/g++.dg/cpp23/consteval-if6.C create mode 100644 gcc/testsuite/g++.dg/cpp23/consteval-if7.C create mode 100644 gcc/testsuite/g++.dg/cpp23/consteval-if8.C create mode 100644 gcc/testsuite/g++.dg/cpp23/consteval-if9.C diff --git a/gcc/c-family/c-cppbuiltin.c b/gcc/c-family/c-cppbuiltin.c index 42b7604..f79f939 100644 --- a/gcc/c-family/c-cppbuiltin.c +++ b/gcc/c-family/c-cppbuiltin.c @@ -1029,6 +1029,7 @@ c_cpp_builtins (cpp_reader *pfile) { /* Set feature test macros for C++23. */ cpp_define (pfile, "__cpp_size_t_suffix=202011L"); + cpp_define (pfile, "__cpp_if_consteval=202106L"); } if (flag_concepts) { diff --git a/gcc/cp/call.c b/gcc/cp/call.c index d2f6ca8..9f03534 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -8840,6 +8840,7 @@ immediate_invocation_p (tree fn, int nargs) || !DECL_IMMEDIATE_FUNCTION_P (current_function_decl)) && (current_binding_level->kind != sk_function_parms || !current_binding_level->immediate_fn_ctx_p) + && !in_consteval_if_p /* As an exception, we defer std::source_location::current () invocations until genericization because LWG3396 mandates special behavior for it. */ diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 01b0c42..4f1c3d6 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -1315,7 +1315,10 @@ cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun, } /* For __builtin_is_constant_evaluated, defer it if not - ctx->manifestly_const_eval, otherwise fold it to true. */ + ctx->manifestly_const_eval (as sometimes we try to constant evaluate + without manifestly_const_eval even expressions or parts thereof which + will later be manifestly const_eval evaluated), otherwise fold it to + true. */ if (fndecl_built_in_p (fun, CP_BUILT_IN_IS_CONSTANT_EVALUATED, BUILT_IN_FRONTEND)) { @@ -3298,6 +3301,22 @@ cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t, /*lval*/false, non_constant_p, overflow_p); VERIFY_CONSTANT (val); + if (TREE_CODE (t) == IF_STMT && IF_STMT_CONSTEVAL_P (t)) + { + /* Evaluate the condition as if it was + if (__builtin_is_constant_evaluated ()), i.e. defer it if not + ctx->manifestly_const_eval (as sometimes we try to constant evaluate + without manifestly_const_eval even expressions or parts thereof which + will later be manifestly const_eval evaluated), otherwise fold it to + true. */ + if (ctx->manifestly_const_eval) + val = boolean_true_node; + else + { + *non_constant_p = true; + return t; + } + } /* Don't VERIFY_CONSTANT the other operands. */ if (integer_zerop (val)) val = TREE_OPERAND (t, 2); @@ -8809,10 +8828,17 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, return false; if (!processing_template_decl) tmp = cxx_eval_outermost_constant_expr (tmp, true); - if (integer_zerop (tmp)) - return RECUR (TREE_OPERAND (t, 2), want_rval); - else if (TREE_CODE (tmp) == INTEGER_CST) - return RECUR (TREE_OPERAND (t, 1), want_rval); + /* potential_constant_expression* isn't told if it is called for + manifestly_const_eval or not, so for consteval if always + process both branches as if the condition is not a known + constant. */ + if (TREE_CODE (t) != IF_STMT || !IF_STMT_CONSTEVAL_P (t)) + { + if (integer_zerop (tmp)) + return RECUR (TREE_OPERAND (t, 2), want_rval); + else if (TREE_CODE (tmp) == INTEGER_CST) + return RECUR (TREE_OPERAND (t, 1), want_rval); + } tmp = *jump_target; for (i = 1; i < 3; ++i) { diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c index 9079a5b..96d91b6 100644 --- a/gcc/cp/cp-gimplify.c +++ b/gcc/cp/cp-gimplify.c @@ -161,7 +161,13 @@ genericize_if_stmt (tree *stmt_p) if (!else_) else_ = build_empty_stmt (locus); - if (integer_nonzerop (cond) && !TREE_SIDE_EFFECTS (else_)) + /* consteval if has been verified not to have the then_/else_ blocks + entered by gotos/case labels from elsewhere, and as then_ block + can contain unfolded immediate function calls, we have to discard + the then_ block regardless of whether else_ has side-effects or not. */ + if (IF_STMT_CONSTEVAL_P (stmt)) + stmt = else_; + else if (integer_nonzerop (cond) && !TREE_SIDE_EFFECTS (else_)) stmt = then_; else if (integer_zerop (cond) && !TREE_SIDE_EFFECTS (then_)) stmt = else_; diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 9ac8b52..36f99cc 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -478,6 +478,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX]; AGGR_INIT_ZERO_FIRST (in AGGR_INIT_EXPR) CONSTRUCTOR_MUTABLE_POISON (in CONSTRUCTOR) OVL_HIDDEN_P (in OVERLOAD) + IF_STMT_CONSTEVAL_P (in IF_STMT) SWITCH_STMT_NO_BREAK_P (in SWITCH_STMT) LAMBDA_EXPR_CAPTURE_OPTIMIZED (in LAMBDA_EXPR) IMPLICIT_CONV_EXPR_BRACED_INIT (in IMPLICIT_CONV_EXPR) @@ -1813,9 +1814,12 @@ struct GTY(()) saved_scope { BOOL_BITFIELD x_processing_explicit_instantiation : 1; BOOL_BITFIELD need_pop_function_context : 1; -/* Nonzero if we are parsing the discarded statement of a constexpr - if-statement. */ + /* Nonzero if we are parsing the discarded statement of a constexpr + if-statement. */ BOOL_BITFIELD discarded_stmt : 1; + /* Nonzero if we are parsing or instantiating the compound-statement + of consteval if statement. */ + BOOL_BITFIELD consteval_if_p : 1; int unevaluated_operand; int inhibit_evaluation_warnings; @@ -1879,6 +1883,7 @@ extern GTY(()) struct saved_scope *scope_chain; #define processing_explicit_instantiation scope_chain->x_processing_explicit_instantiation #define in_discarded_stmt scope_chain->discarded_stmt +#define in_consteval_if_p scope_chain->consteval_if_p #define current_ref_temp_count scope_chain->ref_temp_count @@ -5211,6 +5216,7 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter) #define ELSE_CLAUSE(NODE) TREE_OPERAND (IF_STMT_CHECK (NODE), 2) #define IF_SCOPE(NODE) TREE_OPERAND (IF_STMT_CHECK (NODE), 3) #define IF_STMT_CONSTEXPR_P(NODE) TREE_LANG_FLAG_0 (IF_STMT_CHECK (NODE)) +#define IF_STMT_CONSTEVAL_P(NODE) TREE_LANG_FLAG_2 (IF_STMT_CHECK (NODE)) /* Like PACK_EXPANSION_EXTRA_ARGS, for constexpr if. IF_SCOPE is used while building an IF_STMT; IF_STMT_EXTRA_ARGS is used after it is complete. */ diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index cbf647d..da254d8 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -222,6 +222,7 @@ struct GTY((for_user)) named_label_entry { bool in_omp_scope; bool in_transaction_scope; bool in_constexpr_if; + bool in_consteval_if; }; #define named_labels cp_function_chain->x_named_labels @@ -491,6 +492,16 @@ level_for_constexpr_if (cp_binding_level *b) && IF_STMT_CONSTEXPR_P (b->this_entity)); } +/* True if B is the level for the condition of a consteval if. */ + +static bool +level_for_consteval_if (cp_binding_level *b) +{ + return (b->kind == sk_cond && b->this_entity + && TREE_CODE (b->this_entity) == IF_STMT + && IF_STMT_CONSTEVAL_P (b->this_entity)); +} + /* Update data for defined and undefined labels when leaving a scope. */ int @@ -530,6 +541,8 @@ poplevel_named_label_1 (named_label_entry **slot, cp_binding_level *bl) case sk_block: if (level_for_constexpr_if (bl->level_chain)) ent->in_constexpr_if = true; + else if (level_for_consteval_if (bl->level_chain)) + ent->in_consteval_if = true; break; default: break; @@ -3391,6 +3404,7 @@ check_previous_goto_1 (tree decl, cp_binding_level* level, tree names, bool complained = false; int identified = 0; bool saw_eh = false, saw_omp = false, saw_tm = false, saw_cxif = false; + bool saw_ceif = false; if (exited_omp) { @@ -3470,6 +3484,12 @@ check_previous_goto_1 (tree decl, cp_binding_level* level, tree names, loc = EXPR_LOCATION (b->level_chain->this_entity); saw_cxif = true; } + else if (!saw_ceif && level_for_consteval_if (b->level_chain)) + { + inf = G_(" enters % statement"); + loc = EXPR_LOCATION (b->level_chain->this_entity); + saw_ceif = true; + } break; default: @@ -3551,12 +3571,13 @@ check_goto (tree decl) unsigned ix; if (ent->in_try_scope || ent->in_catch_scope || ent->in_transaction_scope - || ent->in_constexpr_if + || ent->in_constexpr_if || ent->in_consteval_if || ent->in_omp_scope || !vec_safe_is_empty (ent->bad_decls)) { diagnostic_t diag_kind = DK_PERMERROR; if (ent->in_try_scope || ent->in_catch_scope || ent->in_constexpr_if - || ent->in_transaction_scope || ent->in_omp_scope) + || ent->in_consteval_if || ent->in_transaction_scope + || ent->in_omp_scope) diag_kind = DK_ERROR; complained = identify_goto (decl, DECL_SOURCE_LOCATION (decl), &input_location, diag_kind); @@ -3602,6 +3623,8 @@ check_goto (tree decl) inform (input_location, " enters synchronized or atomic statement"); else if (ent->in_constexpr_if) inform (input_location, " enters % statement"); + else if (ent->in_consteval_if) + inform (input_location, " enters % statement"); } if (ent->in_omp_scope) diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index d59a829..686f98b 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -10902,6 +10902,11 @@ cp_parser_lambda_expression (cp_parser* parser) bool discarded = in_discarded_stmt; in_discarded_stmt = 0; + /* Similarly the body of a lambda in immediate function context is not + in immediate function context. */ + bool save_in_consteval_if_p = in_consteval_if_p; + in_consteval_if_p = false; + /* By virtue of defining a local class, a lambda expression has access to the private variables of enclosing classes. */ @@ -10932,6 +10937,7 @@ cp_parser_lambda_expression (cp_parser* parser) finish_struct (type, /*attributes=*/NULL_TREE); + in_consteval_if_p = save_in_consteval_if_p; in_discarded_stmt = discarded; parser->num_template_parameter_lists = saved_num_template_parameter_lists; @@ -12324,6 +12330,102 @@ cp_parser_selection_statement (cp_parser* parser, bool *if_p, "% only available with " "%<-std=c++17%> or %<-std=gnu++17%>"); } + int ce = 0; + if (keyword == RID_IF && !cx) + { + if (cp_lexer_next_token_is_keyword (parser->lexer, + RID_CONSTEVAL)) + ce = 1; + else if (cp_lexer_next_token_is (parser->lexer, CPP_NOT) + && cp_lexer_nth_token_is_keyword (parser->lexer, 2, + RID_CONSTEVAL)) + { + ce = -1; + cp_lexer_consume_token (parser->lexer); + } + } + if (ce) + { + cp_token *tok = cp_lexer_consume_token (parser->lexer); + if (cxx_dialect < cxx23) + pedwarn (tok->location, OPT_Wc__23_extensions, + "% only available with " + "%<-std=c++2b%> or %<-std=gnu++2b%>"); + + bool save_in_consteval_if_p = in_consteval_if_p; + statement = begin_if_stmt (); + IF_STMT_CONSTEVAL_P (statement) = true; + condition = finish_if_stmt_cond (boolean_false_node, statement); + + gcc_rich_location richloc = tok->location; + bool non_compound_stmt_p = false; + if (cp_lexer_next_token_is_not (parser->lexer, CPP_OPEN_BRACE)) + { + non_compound_stmt_p = true; + richloc.add_fixit_insert_after (tok->location, "{"); + } + + in_consteval_if_p |= ce > 0; + cp_parser_implicitly_scoped_statement (parser, NULL, guard_tinfo); + + if (non_compound_stmt_p) + { + location_t before_loc + = cp_lexer_peek_token (parser->lexer)->location; + richloc.add_fixit_insert_before (before_loc, "}"); + error_at (&richloc, + "% requires compound statement"); + non_compound_stmt_p = false; + } + + finish_then_clause (statement); + + /* If the next token is `else', parse the else-clause. */ + if (cp_lexer_next_token_is_keyword (parser->lexer, + RID_ELSE)) + { + cp_token *else_tok = cp_lexer_peek_token (parser->lexer); + gcc_rich_location else_richloc = else_tok->location; + guard_tinfo = get_token_indent_info (else_tok); + /* Consume the `else' keyword. */ + cp_lexer_consume_token (parser->lexer); + + begin_else_clause (statement); + + if (cp_lexer_next_token_is_not (parser->lexer, CPP_OPEN_BRACE)) + { + non_compound_stmt_p = true; + else_richloc.add_fixit_insert_after (else_tok->location, + "{"); + } + + in_consteval_if_p = save_in_consteval_if_p | (ce < 0); + cp_parser_implicitly_scoped_statement (parser, NULL, + guard_tinfo); + + if (non_compound_stmt_p) + { + location_t before_loc + = cp_lexer_peek_token (parser->lexer)->location; + else_richloc.add_fixit_insert_before (before_loc, "}"); + error_at (&else_richloc, + "% requires compound statement"); + } + + finish_else_clause (statement); + } + + in_consteval_if_p = save_in_consteval_if_p; + if (ce < 0) + { + std::swap (THEN_CLAUSE (statement), ELSE_CLAUSE (statement)); + if (THEN_CLAUSE (statement) == NULL_TREE) + THEN_CLAUSE (statement) = build_empty_stmt (tok->location); + } + + finish_if_stmt (statement); + return statement; + } /* Look for the `('. */ matching_parens parens; diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index d87382d..b53df9e 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -18413,6 +18413,7 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl, case IF_STMT: stmt = begin_if_stmt (); IF_STMT_CONSTEXPR_P (stmt) = IF_STMT_CONSTEXPR_P (t); + IF_STMT_CONSTEVAL_P (stmt) = IF_STMT_CONSTEVAL_P (t); if (IF_STMT_CONSTEXPR_P (t)) args = add_extra_args (IF_STMT_EXTRA_ARGS (t), args); tmp = RECUR (IF_COND (t)); @@ -18433,6 +18434,13 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl, } if (IF_STMT_CONSTEXPR_P (t) && integer_zerop (tmp)) /* Don't instantiate the THEN_CLAUSE. */; + else if (IF_STMT_CONSTEVAL_P (t)) + { + bool save_in_consteval_if_p = in_consteval_if_p; + in_consteval_if_p = true; + RECUR (THEN_CLAUSE (t)); + in_consteval_if_p = save_in_consteval_if_p; + } else { tree folded = fold_non_dependent_expr (tmp, complain); @@ -19385,6 +19393,9 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) local_specialization_stack s (lss_copy); + bool save_in_consteval_if_p = in_consteval_if_p; + in_consteval_if_p = false; + tree body = start_lambda_function (fn, r); /* Now record them for lookup_init_capture_pack. */ @@ -19425,6 +19436,8 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) finish_lambda_function (body); + in_consteval_if_p = save_in_consteval_if_p; + if (nested) pop_function_context (); else diff --git a/gcc/testsuite/g++.dg/cpp23/consteval-if1.C b/gcc/testsuite/g++.dg/cpp23/consteval-if1.C new file mode 100644 index 0000000..cff9946 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/consteval-if1.C @@ -0,0 +1,103 @@ +// P1938R3 +// { dg-do run { target c++20 } } +// { dg-options "" } + +extern "C" void abort (); + +namespace std { + constexpr inline bool + is_constant_evaluated () noexcept + { + if consteval { // { dg-warning "'if consteval' only available with" "" { target c++20_only } } + return true; + } else { + return false; + } + } +} + +consteval int foo (int x) { return x; } +consteval int bar () { return 2; } + +constexpr int +baz (int x) +{ + int r = 0; + if consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } + { + r += foo (x); + } + else + { + r += bar (); + } + if ! consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } + { + r += 2 * bar (); + } + else + { + r += foo (8 * x); + } + if (std::is_constant_evaluated ()) + r = -r; + if consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } + { + r += foo (32 * x); + } + if not consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } + { + r += 32 * bar (); + } + return r; +} + +template +constexpr int +qux (T x) +{ + T r = 0; + if consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } + { + r += foo (x); + } + else + { + r += bar (); + } + if ! consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } + { + r += 2 * bar (); + } + else + { + r += foo (8 * x); + } + if (std::is_constant_evaluated ()) + r = -r; + if consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } + { + r += foo (32 * x); + } + if not consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } + { + r += 32 * bar (); + } + return r; +} + +constexpr int a = baz (1); +static_assert (a == 23); +int b = baz (1); +constexpr int c = qux (1); +static_assert (c == 23); +int d = qux (1); + +int +main () +{ + if (b != 23 || d != 23) + abort (); + if (baz (1) != 70 || qux (1) != 70 || qux (1LL) != 70) + abort (); +} diff --git a/gcc/testsuite/g++.dg/cpp23/consteval-if10.C b/gcc/testsuite/g++.dg/cpp23/consteval-if10.C new file mode 100644 index 0000000..4c0523f --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/consteval-if10.C @@ -0,0 +1,36 @@ +// P1938R3 +// { dg-do compile { target c++20 } } +// { dg-options "" } + +consteval int foo (int x) { return x; } + +constexpr int +bar (int x) +{ + int r = 0; + if consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } + { + auto y = [=] { foo (x); }; // { dg-error "'x' is not a constant expression" } + y (); + } + return r; +} + +template +constexpr T +baz (T x) +{ + T r = 0; + if consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } + { + auto y = [=] { foo (x); }; // { dg-error "'x' is not a constant expression" } + y (); + } + return r; +} + +int +qux (int x) +{ + return baz (x); +} diff --git a/gcc/testsuite/g++.dg/cpp23/consteval-if2.C b/gcc/testsuite/g++.dg/cpp23/consteval-if2.C new file mode 100644 index 0000000..f7053b9 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/consteval-if2.C @@ -0,0 +1,129 @@ +// P1938R3 +// { dg-do compile { target c++20 } } +// { dg-options "" } + +constexpr bool f() +{ + if consteval (true) {} // { dg-error "'if consteval' requires compound statement" } + // { dg-error "expected" "" { target *-*-* } .-1 } + // { dg-warning "'if consteval' only available with" "" { target c++20_only } .-2 } + if not consteval (false) {} // { dg-error "'if consteval' requires compound statement" } + // { dg-error "expected" "" { target *-*-* } .-1 } + // { dg-warning "'if consteval' only available with" "" { target c++20_only } .-2 } + if consteval if (true) {} // { dg-error "'if consteval' requires compound statement" } + // { dg-warning "'if consteval' only available with" "" { target c++20_only } .-1 } + if ! consteval {} else ; // { dg-error "'if consteval' requires compound statement" } + // { dg-warning "'if consteval' only available with" "" { target c++20_only } .-1 } + if consteval {} else if (true) {} // { dg-error "'if consteval' requires compound statement" } + // { dg-warning "'if consteval' only available with" "" { target c++20_only } .-1 } + if (true) + if consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } + { + } + else ; // { dg-error "'if consteval' requires compound statement" } + return false; +} + +consteval int foo (int x) { return x; } +consteval int bar () { return 2; } + +constexpr int +baz (int x) +{ + int r = 0; + if not consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } + { + r += foo (x); // { dg-error "'x' is not a constant expression" } + } + else + { + r += bar (); + } + if consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } + { + r += 2 * bar (); + } + else + { + r += foo (8 * x); // { dg-error "'x' is not a constant expression" } + } + if ! consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } + { + r += foo (32 * x);// { dg-error "'x' is not a constant expression" } + } + if consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } + { + r += 32 * bar (); + } + return r; +} + +template +constexpr int +qux (int x) +{ + int r = 0; + if not consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } + { + r += foo (x); // { dg-error "'x' is not a constant expression" } + } + else + { + r += bar (); + } + if consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } + { + r += 2 * bar (); + } + else + { + r += foo (8 * x); // { dg-error "is not a constant expression" } + } + if ! consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } + { + r += foo (32 * x);// { dg-error "is not a constant expression" } + } + if consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } + { + r += 32 * bar (); + } + return r; +} + +template +constexpr T +corge (T x) +{ + T r = 0; + if not consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } + { + r += foo (x); // { dg-error "'x' is not a constant expression" } + } + else + { + r += bar (); + } + if consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } + { + r += 2 * bar (); + } + else + { + r += foo (8 * x); // { dg-error "is not a constant expression" } + } + if ! consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } + { + r += foo (32 * x);// { dg-error "is not a constant expression" } + } + if consteval // { dg-warning "'if consteval' only available with" "" { target c++20_only } } + { + r += 32 * bar (); + } + return r; +} + +int +garply (int x) +{ + return corge (x); +} diff --git a/gcc/testsuite/g++.dg/cpp23/consteval-if3.C b/gcc/testsuite/g++.dg/cpp23/consteval-if3.C new file mode 100644 index 0000000..9c7e3aa --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/consteval-if3.C @@ -0,0 +1,73 @@ +// P1938R3 +// { dg-do run { target c++20 } } +// { dg-options "" } + +constexpr inline bool +is_constant_evaluated () noexcept +{ + if consteval { return true; } else { return false; } // { dg-warning "'if consteval' only available with" "" { target c++20_only } } +} + +template struct X { int v = N; }; +X x; // type X +int y = 4; +int a = is_constant_evaluated () ? y : 1; // initializes a to 1 +int b = is_constant_evaluated () ? 2 : y; // initializes b to 2 +int c = y + (is_constant_evaluated () ? 2 : y); // initializes c to 2*y +int d = is_constant_evaluated (); // initializes d to 1 +int e = d + is_constant_evaluated (); // initializes e to 1 + 0 + +struct false_type { static constexpr bool value = false; }; +struct true_type { static constexpr bool value = true; }; +template +struct is_same : false_type {}; +template +struct is_same : true_type {}; + +constexpr int +foo (int x) +{ + const int n = is_constant_evaluated () ? 13 : 17; // n == 13 + int m = is_constant_evaluated () ? 13 : 17; // m might be 13 or 17 (see below) + char arr[n] = {}; // char[13] + return m + sizeof (arr) + x; +} + +constexpr int +bar () +{ + const int n = is_constant_evaluated() ? 13 : 17; + X x1; + X x2; + static_assert (is_same::value, "x1/x2's type"); + return x1.v + x2.v; +} + +int p = foo (0); // m == 13; initialized to 26 +int q = p + foo (0); // m == 17 for this call; initialized to 56 +static_assert (bar () == 26, "bar"); + +struct S { int a, b; }; + +S s = { is_constant_evaluated () ? 2 : 3, y }; +S t = { is_constant_evaluated () ? 2 : 3, 4 }; + +static_assert (is_same >::value, "x's type"); + +int +main () +{ + if (a != 1 || b != 2 || c != 8 || d != 1 || e != 1 || p != 26 || q != 56) + __builtin_abort (); + if (s.a != 3 || s.b != 4 || t.a != 2 || t.b != 4) + __builtin_abort (); + if (foo (y) != 34) + __builtin_abort (); +#if __cplusplus >= 201703L + if constexpr (foo (0) != 26) + __builtin_abort (); +#endif + constexpr int w = foo (0); + if (w != 26) + __builtin_abort (); +} diff --git a/gcc/testsuite/g++.dg/cpp23/consteval-if4.C b/gcc/testsuite/g++.dg/cpp23/consteval-if4.C new file mode 100644 index 0000000..2760c1c --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/consteval-if4.C @@ -0,0 +1,44 @@ +// { dg-do compile { target c++20 } } +// { dg-options "-w" } + +void f() +{ + goto l; // { dg-message "from here" } + if consteval // { dg-message "enters 'consteval if'" } + { + l:; // { dg-error "jump to label" } + } +} + +void g() +{ + goto l; // { dg-message "from here" } + if not consteval // { dg-message "enters 'consteval if'" } + { + l:; // { dg-error "jump to label" } + } +} + +void h() +{ + goto l; // { dg-message "from here" } + if consteval // { dg-message "enters 'consteval if'" } + { + } + else + { + l:; // { dg-error "jump to label" } + } +} + +void i() +{ + goto l; // { dg-message "from here" } + if not consteval // { dg-message "enters 'consteval if'" } + { + } + else + { + l:; // { dg-error "jump to label" } + } +} diff --git a/gcc/testsuite/g++.dg/cpp23/consteval-if5.C b/gcc/testsuite/g++.dg/cpp23/consteval-if5.C new file mode 100644 index 0000000..b1c6192 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/consteval-if5.C @@ -0,0 +1,14 @@ +// { dg-do compile { target c++20 } } +// { dg-options "-w" } + +void f() +{ + if consteval // { dg-message "enters 'consteval if'" } + { + goto l; // { dg-message "from here" } + } + else + { + l:; // { dg-error "jump to label" } + } +} diff --git a/gcc/testsuite/g++.dg/cpp23/consteval-if6.C b/gcc/testsuite/g++.dg/cpp23/consteval-if6.C new file mode 100644 index 0000000..2b92ccc --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/consteval-if6.C @@ -0,0 +1,16 @@ +// { dg-do compile { target c++20 } } +// { dg-options "-w" } + +void f() +{ + if consteval + { + goto l; + l:; + } + else + { + goto l2; + l2:; + } +} diff --git a/gcc/testsuite/g++.dg/cpp23/consteval-if7.C b/gcc/testsuite/g++.dg/cpp23/consteval-if7.C new file mode 100644 index 0000000..ab9da08 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/consteval-if7.C @@ -0,0 +1,16 @@ +// { dg-do compile { target c++20 } } +// { dg-options "-w" } + +void f() +{ + if not consteval + { + l:; + goto l; + } + else + { + l2:; + goto l2; + } +} diff --git a/gcc/testsuite/g++.dg/cpp23/consteval-if8.C b/gcc/testsuite/g++.dg/cpp23/consteval-if8.C new file mode 100644 index 0000000..36adaf9 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/consteval-if8.C @@ -0,0 +1,14 @@ +// { dg-do compile { target c++20 } } +// { dg-options "-w" } + +void f() +{ + if consteval + { + l:; // { dg-error "jump to label" } + } + else + { + goto l; // { dg-message "from here" } + } +} diff --git a/gcc/testsuite/g++.dg/cpp23/consteval-if9.C b/gcc/testsuite/g++.dg/cpp23/consteval-if9.C new file mode 100644 index 0000000..1c7db1a --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/consteval-if9.C @@ -0,0 +1,11 @@ +// { dg-do compile { target c++20 } } +// { dg-options "-w" } + +constexpr void f(int i) +{ + switch (i) + if consteval // { dg-message "enters 'consteval if'" } + { + case 42:; // { dg-error "jump to case label" } + } +} diff --git a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C index 283a8ba..7070f59 100644 --- a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C +++ b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C @@ -545,3 +545,9 @@ #elif __cpp_size_t_suffix != 202011 # error "__cpp_size_t_suffix != 202011" #endif + +#ifndef __cpp_if_consteval +# error "__cpp_if_consteval" +#elif __cpp_if_consteval != 202106 +# error "__cpp_if_consteval != 202106" +#endif -- 2.7.4