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 <jakub@redhat.com>
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) <case IF_STMT>: 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.
{
/* 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)
{
|| !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. */
}
/* 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))
{
/*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);
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)
{
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_;
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)
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;
#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
#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. */
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
&& 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
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;
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)
{
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 %<consteval if%> statement");
+ loc = EXPR_LOCATION (b->level_chain->this_entity);
+ saw_ceif = true;
+ }
break;
default:
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);
inform (input_location, " enters synchronized or atomic statement");
else if (ent->in_constexpr_if)
inform (input_location, " enters %<constexpr if%> statement");
+ else if (ent->in_consteval_if)
+ inform (input_location, " enters %<consteval if%> statement");
}
if (ent->in_omp_scope)
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. */
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;
"%<if constexpr%> 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,
+ "%<if consteval%> 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,
+ "%<if consteval%> 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,
+ "%<if consteval%> 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;
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));
}
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);
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. */
finish_lambda_function (body);
+ in_consteval_if_p = save_in_consteval_if_p;
+
if (nested)
pop_function_context ();
else
--- /dev/null
+// 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 <typename T>
+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<int> (1);
+
+int
+main ()
+{
+ if (b != 23 || d != 23)
+ abort ();
+ if (baz (1) != 70 || qux (1) != 70 || qux (1LL) != 70)
+ abort ();
+}
--- /dev/null
+// 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 <typename T>
+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);
+}
--- /dev/null
+// 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 <typename T>
+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 <typename T>
+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);
+}
--- /dev/null
+// 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<int N> struct X { int v = N; };
+X<is_constant_evaluated ()> x; // type X<true>
+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<class T, class U>
+struct is_same : false_type {};
+template<class T>
+struct is_same<T, T> : 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<n> x1;
+ X<is_constant_evaluated() ? 13 : 17> x2;
+ static_assert (is_same<decltype (x1), decltype (x2)>::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<decltype (x), X<true> >::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 ();
+}
--- /dev/null
+// { 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" }
+ }
+}
--- /dev/null
+// { 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" }
+ }
+}
--- /dev/null
+// { dg-do compile { target c++20 } }
+// { dg-options "-w" }
+
+void f()
+{
+ if consteval
+ {
+ goto l;
+ l:;
+ }
+ else
+ {
+ goto l2;
+ l2:;
+ }
+}
--- /dev/null
+// { dg-do compile { target c++20 } }
+// { dg-options "-w" }
+
+void f()
+{
+ if not consteval
+ {
+ l:;
+ goto l;
+ }
+ else
+ {
+ l2:;
+ goto l2;
+ }
+}
--- /dev/null
+// { 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" }
+ }
+}
--- /dev/null
+// { 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" }
+ }
+}
#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