* [PATCH] c++: Add C++23 consteval if support - P1938R3 [PR100974]
@ 2021-06-10 8:34 Jakub Jelinek
2021-06-10 10:24 ` Jonathan Wakely
2021-06-10 14:09 ` Jason Merrill
0 siblings, 2 replies; 10+ messages in thread
From: Jakub Jelinek @ 2021-06-10 8:34 UTC (permalink / raw)
To: Jason Merrill; +Cc: gcc-patches, Jonathan Wakely
Hi!
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.
I'm not 100% sure whether lambda body is enclosed by the surrounding
statement, I'm assuming it is not - see consteval-if10.C testcase.
Also, the paper doesn't contain the exact __cpp_if_consteval value,
but https://github.com/cplusplus/draft/pull/4660/files mentions 202106L
which this patch uses.
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.
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
2021-06-10 Jakub Jelinek <jakub@redhat.com>
PR c++/100974 - P1938R3 - if consteval
gcc/c-family/
* c-cppbuiltin.c (c_cpp_builtins): Predefine __cpp_if_consteval for
-std=c++2b.
gcc/cp/
* cp-tree.h (struct saved_scope): Add immediate_fn_ctx_p
member.
(in_immediate_fn_ctx_p, IF_STMT_CONSTEVAL_P): Define.
* parser.c (cp_parser_lambda_expression): Temporarily disable
in_immediate_fn_ctx_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_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_immediate_fn_ctx_p when recursing on
IF_STMT_CONSTEVAL_P then branch.
(tsubst_lambda_expr): Temporarily disable
in_immediate_fn_ctx_p when instantiating lambda body.
* call.c (immediate_invocation_p): Return false when
in_immediate_fn_ctx_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.jj 2021-06-09 21:54:39.433194531 +0200
+++ gcc/c-family/c-cppbuiltin.c 2021-06-10 09:49:35.776558874 +0200
@@ -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)
{
--- gcc/cp/cp-tree.h.jj 2021-06-09 21:54:39.474193964 +0200
+++ gcc/cp/cp-tree.h 2021-06-10 09:49:35.795558610 +0200
@@ -478,6 +478,7 @@ extern GTY(()) tree cp_global_trees[CPTI
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)
@@ -1816,6 +1817,7 @@ struct GTY(()) saved_scope {
/* Nonzero if we are parsing the discarded statement of a constexpr
if-statement. */
BOOL_BITFIELD discarded_stmt : 1;
+ BOOL_BITFIELD immediate_fn_ctx_p : 1;
int unevaluated_operand;
int inhibit_evaluation_warnings;
@@ -1879,6 +1881,7 @@ extern GTY(()) struct saved_scope *scope
#define processing_explicit_instantiation scope_chain->x_processing_explicit_instantiation
#define in_discarded_stmt scope_chain->discarded_stmt
+#define in_immediate_fn_ctx_p scope_chain->immediate_fn_ctx_p
#define current_ref_temp_count scope_chain->ref_temp_count
@@ -5211,6 +5214,7 @@ more_aggr_init_expr_args_p (const aggr_i
#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. */
--- gcc/cp/parser.c.jj 2021-06-09 21:54:39.482193853 +0200
+++ gcc/cp/parser.c 2021-06-10 10:09:23.753052980 +0200
@@ -10902,6 +10902,11 @@ cp_parser_lambda_expression (cp_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 immediate_fn_ctx_p = in_immediate_fn_ctx_p;
+ in_immediate_fn_ctx_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*
finish_struct (type, /*attributes=*/NULL_TREE);
+ in_immediate_fn_ctx_p = immediate_fn_ctx_p;
in_discarded_stmt = discarded;
parser->num_template_parameter_lists = saved_num_template_parameter_lists;
@@ -12324,6 +12330,73 @@ cp_parser_selection_statement (cp_parser
"%<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 immediate_fn_ctx_p = in_immediate_fn_ctx_p;
+ statement = begin_if_stmt ();
+ IF_STMT_CONSTEVAL_P (statement) = true;
+ condition = finish_if_stmt_cond (boolean_false_node, statement);
+
+ if (cp_lexer_next_token_is_not (parser->lexer, CPP_OPEN_BRACE))
+ error ("%<if consteval%> requires compound statement");
+
+ in_immediate_fn_ctx_p |= ce > 0;
+ cp_parser_implicitly_scoped_statement (parser, NULL, guard_tinfo);
+
+ 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);
+ 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))
+ error ("%<if consteval%> requires compound statement");
+
+ in_immediate_fn_ctx_p = immediate_fn_ctx_p | (ce < 0);
+ cp_parser_implicitly_scoped_statement (parser, NULL,
+ guard_tinfo);
+
+ finish_else_clause (statement);
+ }
+
+ in_immediate_fn_ctx_p = immediate_fn_ctx_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;
--- gcc/cp/decl.c.jj 2021-06-09 21:54:39.477193922 +0200
+++ gcc/cp/decl.c 2021-06-10 09:49:35.872557540 +0200
@@ -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
&& 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_entr
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_bin
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_bin
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:
@@ -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 %<constexpr if%> statement");
+ else if (ent->in_consteval_if)
+ inform (input_location, " enters %<consteval if%> statement");
}
if (ent->in_omp_scope)
--- gcc/cp/constexpr.c.jj 2021-06-09 21:54:39.456194213 +0200
+++ gcc/cp/constexpr.c 2021-06-10 09:49:35.881557415 +0200
@@ -3299,6 +3299,18 @@ cxx_eval_conditional_expression (const c
non_constant_p, overflow_p);
VERIFY_CONSTANT (val);
/* Don't VERIFY_CONSTANT the other operands. */
+ if (TREE_CODE (t) == IF_STMT && IF_STMT_CONSTEVAL_P (t))
+ {
+ /* Evaluate the condition as if it was
+ if (__builtin_is_constant_evaluated ()). */
+ if (ctx->manifestly_const_eval)
+ val = boolean_true_node;
+ else
+ {
+ *non_constant_p = true;
+ return t;
+ }
+ }
if (integer_zerop (val))
val = TREE_OPERAND (t, 2);
else
@@ -8799,10 +8811,13 @@ potential_constant_expression_1 (tree t,
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);
+ 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)
{
--- gcc/cp/cp-gimplify.c.jj 2021-06-09 21:54:39.473193977 +0200
+++ gcc/cp/cp-gimplify.c 2021-06-10 09:49:35.898557178 +0200
@@ -161,7 +161,9 @@ genericize_if_stmt (tree *stmt_p)
if (!else_)
else_ = build_empty_stmt (locus);
- if (integer_nonzerop (cond) && !TREE_SIDE_EFFECTS (else_))
+ 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_;
--- gcc/cp/pt.c.jj 2021-06-09 21:54:39.503193563 +0200
+++ gcc/cp/pt.c 2021-06-10 10:12:11.470725151 +0200
@@ -18413,6 +18413,7 @@ tsubst_expr (tree t, tree args, tsubst_f
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_f
}
if (IF_STMT_CONSTEXPR_P (t) && integer_zerop (tmp))
/* Don't instantiate the THEN_CLAUSE. */;
+ else if (IF_STMT_CONSTEVAL_P (t))
+ {
+ bool immediate_fn_ctx_p = in_immediate_fn_ctx_p;
+ in_immediate_fn_ctx_p = true;
+ RECUR (THEN_CLAUSE (t));
+ in_immediate_fn_ctx_p = immediate_fn_ctx_p;
+ }
else
{
tree folded = fold_non_dependent_expr (tmp, complain);
@@ -19385,6 +19393,9 @@ tsubst_lambda_expr (tree t, tree args, t
local_specialization_stack s (lss_copy);
+ bool immediate_fn_ctx_p = in_immediate_fn_ctx_p;
+ in_immediate_fn_ctx_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, t
finish_lambda_function (body);
+ in_immediate_fn_ctx_p = immediate_fn_ctx_p;
+
if (nested)
pop_function_context ();
else
--- gcc/cp/call.c.jj 2021-06-09 21:54:39.436194489 +0200
+++ gcc/cp/call.c 2021-06-10 09:49:35.949556470 +0200
@@ -8840,6 +8840,7 @@ immediate_invocation_p (tree fn, int nar
|| !DECL_IMMEDIATE_FUNCTION_P (current_function_decl))
&& (current_binding_level->kind != sk_function_parms
|| !current_binding_level->immediate_fn_ctx_p)
+ && !in_immediate_fn_ctx_p
/* As an exception, we defer std::source_location::current ()
invocations until genericization because LWG3396 mandates
special behavior for it. */
--- gcc/testsuite/g++.dg/cpp23/consteval-if1.C.jj 2021-06-10 09:49:35.949556470 +0200
+++ gcc/testsuite/g++.dg/cpp23/consteval-if1.C 2021-06-10 09:49:35.949556470 +0200
@@ -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 <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 ();
+}
--- gcc/testsuite/g++.dg/cpp23/consteval-if2.C.jj 2021-06-10 09:49:35.949556470 +0200
+++ gcc/testsuite/g++.dg/cpp23/consteval-if2.C 2021-06-10 09:58:22.459240019 +0200
@@ -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 <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);
+}
--- gcc/testsuite/g++.dg/cpp23/consteval-if3.C.jj 2021-06-10 09:49:35.949556470 +0200
+++ gcc/testsuite/g++.dg/cpp23/consteval-if3.C 2021-06-10 09:49:35.949556470 +0200
@@ -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<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 ();
+}
--- gcc/testsuite/g++.dg/cpp23/consteval-if4.C.jj 2021-06-10 09:49:35.949556470 +0200
+++ gcc/testsuite/g++.dg/cpp23/consteval-if4.C 2021-06-10 09:49:35.949556470 +0200
@@ -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" }
+ }
+}
--- gcc/testsuite/g++.dg/cpp23/consteval-if5.C.jj 2021-06-10 09:49:35.949556470 +0200
+++ gcc/testsuite/g++.dg/cpp23/consteval-if5.C 2021-06-10 09:49:35.949556470 +0200
@@ -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" }
+ }
+}
--- gcc/testsuite/g++.dg/cpp23/consteval-if6.C.jj 2021-06-10 09:49:35.949556470 +0200
+++ gcc/testsuite/g++.dg/cpp23/consteval-if6.C 2021-06-10 09:49:35.949556470 +0200
@@ -0,0 +1,16 @@
+// { dg-do compile { target c++20 } }
+// { dg-options "-w" }
+
+void f()
+{
+ if consteval
+ {
+ goto l;
+ l:;
+ }
+ else
+ {
+ goto l2;
+ l2:;
+ }
+}
--- gcc/testsuite/g++.dg/cpp23/consteval-if7.C.jj 2021-06-10 09:49:35.949556470 +0200
+++ gcc/testsuite/g++.dg/cpp23/consteval-if7.C 2021-06-10 09:49:35.949556470 +0200
@@ -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;
+ }
+}
--- gcc/testsuite/g++.dg/cpp23/consteval-if8.C.jj 2021-06-10 09:49:35.950556456 +0200
+++ gcc/testsuite/g++.dg/cpp23/consteval-if8.C 2021-06-10 09:49:35.950556456 +0200
@@ -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" }
+ }
+}
--- gcc/testsuite/g++.dg/cpp23/consteval-if9.C.jj 2021-06-10 09:49:35.950556456 +0200
+++ gcc/testsuite/g++.dg/cpp23/consteval-if9.C 2021-06-10 09:49:35.950556456 +0200
@@ -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" }
+ }
+}
--- gcc/testsuite/g++.dg/cpp23/consteval-if10.C.jj 2021-06-10 09:58:15.456337333 +0200
+++ gcc/testsuite/g++.dg/cpp23/consteval-if10.C 2021-06-10 10:15:50.288688058 +0200
@@ -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 <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);
+}
--- gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C.jj 2021-06-10 09:46:03.851503924 +0200
+++ gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C 2021-06-10 09:49:35.950556456 +0200
@@ -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
Jakub
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] c++: Add C++23 consteval if support - P1938R3 [PR100974]
2021-06-10 8:34 [PATCH] c++: Add C++23 consteval if support - P1938R3 [PR100974] Jakub Jelinek
@ 2021-06-10 10:24 ` Jonathan Wakely
2021-06-10 10:34 ` Jakub Jelinek
2021-06-10 14:09 ` Jason Merrill
1 sibling, 1 reply; 10+ messages in thread
From: Jonathan Wakely @ 2021-06-10 10:24 UTC (permalink / raw)
To: Jakub Jelinek; +Cc: Jason Merrill, gcc Patches
On Thu, 10 Jun 2021 at 09:34, Jakub Jelinek wrote:
> Also, the paper doesn't contain the exact __cpp_if_consteval value,
Right, proposals aren't supposed to, because the value gets set when
the proposal is voted into the working draft (which the proposal
author doesn't control).
> but https://github.com/cplusplus/draft/pull/4660/files mentions 202106L
> which this patch uses.
That's the right value.
> 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.
Is there any advantage to changing that (cheaper for GCC to evaluate?)
or should we just continue to use the __builtin unconditionally?
I suppose it's theoretically possible that there could be a non-GCC
compiler where defined(__cpp_if_consteval) is true but
__has_builtin(__builtin_is_constant_evaluated) is false.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] c++: Add C++23 consteval if support - P1938R3 [PR100974]
2021-06-10 10:24 ` Jonathan Wakely
@ 2021-06-10 10:34 ` Jakub Jelinek
2021-06-10 10:43 ` Jonathan Wakely
0 siblings, 1 reply; 10+ messages in thread
From: Jakub Jelinek @ 2021-06-10 10:34 UTC (permalink / raw)
To: Jonathan Wakely; +Cc: Jason Merrill, gcc Patches
On Thu, Jun 10, 2021 at 11:24:43AM +0100, Jonathan Wakely wrote:
> > 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.
>
> Is there any advantage to changing that (cheaper for GCC to evaluate?)
I guess compile-time lost in the noise.
> or should we just continue to use the __builtin unconditionally?
>
> I suppose it's theoretically possible that there could be a non-GCC
> compiler where defined(__cpp_if_consteval) is true but
> __has_builtin(__builtin_is_constant_evaluated) is false.
Up to you. The wording says Equivalent to
if consteval { return true; } else { return false; }
and return __builtin_is_constant_evaluated (); is equivalent to that.
Perhaps some people could appreciate to see it literally there, but we
can't use it for C++20 (due to the -Wpedantic warnings or -pedantic-errors
errors) and for C++17 and earlier (consteval is not a keyword).
Jakub
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] c++: Add C++23 consteval if support - P1938R3 [PR100974]
2021-06-10 10:34 ` Jakub Jelinek
@ 2021-06-10 10:43 ` Jonathan Wakely
0 siblings, 0 replies; 10+ messages in thread
From: Jonathan Wakely @ 2021-06-10 10:43 UTC (permalink / raw)
To: Jakub Jelinek; +Cc: Jason Merrill, gcc Patches
On Thu, 10 Jun 2021 at 11:34, Jakub Jelinek wrote:
>
> On Thu, Jun 10, 2021 at 11:24:43AM +0100, Jonathan Wakely wrote:
> > > 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.
> >
> > Is there any advantage to changing that (cheaper for GCC to evaluate?)
>
> I guess compile-time lost in the noise.
>
> > or should we just continue to use the __builtin unconditionally?
> >
> > I suppose it's theoretically possible that there could be a non-GCC
> > compiler where defined(__cpp_if_consteval) is true but
> > __has_builtin(__builtin_is_constant_evaluated) is false.
>
> Up to you. The wording says Equivalent to
> if consteval { return true; } else { return false; }
> and return __builtin_is_constant_evaluated (); is equivalent to that.
>
> Perhaps some people could appreciate to see it literally there, but we
Those people can write their own pure-C++23 library then ;-)
> can't use it for C++20 (due to the -Wpedantic warnings or -pedantic-errors
Yeah.
> errors) and for C++17 and earlier (consteval is not a keyword).
std::is_constant_evaluated isn't defined for C++17 and earlier, so in
C++14 and C++17 code we have to use the builtin directly (and for
C++11 constexpr is so limited that there are no uses for it).
I think we should just leave it as-is, and revisit if a good reason
arises in future.
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] c++: Add C++23 consteval if support - P1938R3 [PR100974]
2021-06-10 8:34 [PATCH] c++: Add C++23 consteval if support - P1938R3 [PR100974] Jakub Jelinek
2021-06-10 10:24 ` Jonathan Wakely
@ 2021-06-10 14:09 ` Jason Merrill
2021-06-10 14:44 ` Jakub Jelinek
1 sibling, 1 reply; 10+ messages in thread
From: Jason Merrill @ 2021-06-10 14:09 UTC (permalink / raw)
To: Jakub Jelinek; +Cc: gcc-patches, Jonathan Wakely
On 6/10/21 4:34 AM, Jakub Jelinek wrote:
> Hi!
>
> The following patch implements consteval if support.
Great!
> 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.
>
> I'm not 100% sure whether lambda body is enclosed by the surrounding
> statement, I'm assuming it is not - see consteval-if10.C testcase.
Correct.
> Also, the paper doesn't contain the exact __cpp_if_consteval value,
> but https://github.com/cplusplus/draft/pull/4660/files mentions 202106L
> which this patch uses.
>
> 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.
>
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
>
> 2021-06-10 Jakub Jelinek <jakub@redhat.com>
>
> PR c++/100974 - P1938R3 - if consteval
> gcc/c-family/
> * c-cppbuiltin.c (c_cpp_builtins): Predefine __cpp_if_consteval for
> -std=c++2b.
> gcc/cp/
> * cp-tree.h (struct saved_scope): Add immediate_fn_ctx_p
> member.
> (in_immediate_fn_ctx_p, IF_STMT_CONSTEVAL_P): Define.
> * parser.c (cp_parser_lambda_expression): Temporarily disable
> in_immediate_fn_ctx_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_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_immediate_fn_ctx_p when recursing on
> IF_STMT_CONSTEVAL_P then branch.
> (tsubst_lambda_expr): Temporarily disable
> in_immediate_fn_ctx_p when instantiating lambda body.
> * call.c (immediate_invocation_p): Return false when
> in_immediate_fn_ctx_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.jj 2021-06-09 21:54:39.433194531 +0200
> +++ gcc/c-family/c-cppbuiltin.c 2021-06-10 09:49:35.776558874 +0200
> @@ -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)
> {
> --- gcc/cp/cp-tree.h.jj 2021-06-09 21:54:39.474193964 +0200
> +++ gcc/cp/cp-tree.h 2021-06-10 09:49:35.795558610 +0200
> @@ -478,6 +478,7 @@ extern GTY(()) tree cp_global_trees[CPTI
> 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)
> @@ -1816,6 +1817,7 @@ struct GTY(()) saved_scope {
> /* Nonzero if we are parsing the discarded statement of a constexpr
> if-statement. */
> BOOL_BITFIELD discarded_stmt : 1;
> + BOOL_BITFIELD immediate_fn_ctx_p : 1;
>
> int unevaluated_operand;
> int inhibit_evaluation_warnings;
> @@ -1879,6 +1881,7 @@ extern GTY(()) struct saved_scope *scope
> #define processing_explicit_instantiation scope_chain->x_processing_explicit_instantiation
>
> #define in_discarded_stmt scope_chain->discarded_stmt
> +#define in_immediate_fn_ctx_p scope_chain->immediate_fn_ctx_p
>
> #define current_ref_temp_count scope_chain->ref_temp_count
>
> @@ -5211,6 +5214,7 @@ more_aggr_init_expr_args_p (const aggr_i
> #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. */
> --- gcc/cp/parser.c.jj 2021-06-09 21:54:39.482193853 +0200
> +++ gcc/cp/parser.c 2021-06-10 10:09:23.753052980 +0200
> @@ -10902,6 +10902,11 @@ cp_parser_lambda_expression (cp_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 immediate_fn_ctx_p = in_immediate_fn_ctx_p;
It's hard to distinguish between these two names when reading the code;
let's give the local variable a name including "saved".
> + in_immediate_fn_ctx_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*
>
> finish_struct (type, /*attributes=*/NULL_TREE);
>
> + in_immediate_fn_ctx_p = immediate_fn_ctx_p;
> in_discarded_stmt = discarded;
>
> parser->num_template_parameter_lists = saved_num_template_parameter_lists;
> @@ -12324,6 +12330,73 @@ cp_parser_selection_statement (cp_parser
> "%<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 immediate_fn_ctx_p = in_immediate_fn_ctx_p;
> + statement = begin_if_stmt ();
> + IF_STMT_CONSTEVAL_P (statement) = true;
> + condition = finish_if_stmt_cond (boolean_false_node, statement);
> +
> + if (cp_lexer_next_token_is_not (parser->lexer, CPP_OPEN_BRACE))
> + error ("%<if consteval%> requires compound statement");
> +
> + in_immediate_fn_ctx_p |= ce > 0;
> + cp_parser_implicitly_scoped_statement (parser, NULL, guard_tinfo);
Maybe use cp_parser_compound_statement directly instead of this and
checking CPP_OPEN_BRACE above? Either way is fine.
> + 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);
> + 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))
> + error ("%<if consteval%> requires compound statement");
> +
> + in_immediate_fn_ctx_p = immediate_fn_ctx_p | (ce < 0);
> + cp_parser_implicitly_scoped_statement (parser, NULL,
> + guard_tinfo);
> +
> + finish_else_clause (statement);
> + }
> +
> + in_immediate_fn_ctx_p = immediate_fn_ctx_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;
> --- gcc/cp/decl.c.jj 2021-06-09 21:54:39.477193922 +0200
> +++ gcc/cp/decl.c 2021-06-10 09:49:35.872557540 +0200
> @@ -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
> && 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_entr
> 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_bin
> 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_bin
> 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:
> @@ -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 %<constexpr if%> statement");
> + else if (ent->in_consteval_if)
> + inform (input_location, " enters %<consteval if%> statement");
> }
>
> if (ent->in_omp_scope)
> --- gcc/cp/constexpr.c.jj 2021-06-09 21:54:39.456194213 +0200
> +++ gcc/cp/constexpr.c 2021-06-10 09:49:35.881557415 +0200
> @@ -3299,6 +3299,18 @@ cxx_eval_conditional_expression (const c
> non_constant_p, overflow_p);
> VERIFY_CONSTANT (val);
> /* Don't VERIFY_CONSTANT the other operands. */
> + if (TREE_CODE (t) == IF_STMT && IF_STMT_CONSTEVAL_P (t))
> + {
> + /* Evaluate the condition as if it was
> + if (__builtin_is_constant_evaluated ()). */
> + if (ctx->manifestly_const_eval)
> + val = boolean_true_node;
> + else
> + {
> + *non_constant_p = true;
> + return t;
> + }
Why set *non_constant_p? Shouldn't this just be
val = boolean_false_node;
so we constant-evaluate the else clause when we are trying to
constant-evaluate in a non-manifestly-constant-evaluated context?
> + }
> if (integer_zerop (val))
> val = TREE_OPERAND (t, 2);
> else
> @@ -8799,10 +8811,13 @@ potential_constant_expression_1 (tree t,
> 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);
> + 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);
> + }
Don't we still want to shortcut consideration of one of the arms for if
consteval?
> --- gcc/cp/cp-gimplify.c.jj 2021-06-09 21:54:39.473193977 +0200
> +++ gcc/cp/cp-gimplify.c 2021-06-10 09:49:35.898557178 +0200
> @@ -161,7 +161,9 @@ genericize_if_stmt (tree *stmt_p)
> if (!else_)
> else_ = build_empty_stmt (locus);
>
> - if (integer_nonzerop (cond) && !TREE_SIDE_EFFECTS (else_))
> + if (IF_STMT_CONSTEVAL_P (stmt))
> + stmt = else_;
This seems redundant, since you're using boolean_false_node for the
condition.
> + else if (integer_nonzerop (cond) && !TREE_SIDE_EFFECTS (else_))
> stmt = then_;
> else if (integer_zerop (cond) && !TREE_SIDE_EFFECTS (then_))
> stmt = else_;
> --- gcc/cp/pt.c.jj 2021-06-09 21:54:39.503193563 +0200
> +++ gcc/cp/pt.c 2021-06-10 10:12:11.470725151 +0200
> @@ -18413,6 +18413,7 @@ tsubst_expr (tree t, tree args, tsubst_f
> 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_f
> }
> if (IF_STMT_CONSTEXPR_P (t) && integer_zerop (tmp))
> /* Don't instantiate the THEN_CLAUSE. */;
> + else if (IF_STMT_CONSTEVAL_P (t))
> + {
> + bool immediate_fn_ctx_p = in_immediate_fn_ctx_p;
> + in_immediate_fn_ctx_p = true;
> + RECUR (THEN_CLAUSE (t));
> + in_immediate_fn_ctx_p = immediate_fn_ctx_p;
> + }
> else
> {
> tree folded = fold_non_dependent_expr (tmp, complain);
> @@ -19385,6 +19393,9 @@ tsubst_lambda_expr (tree t, tree args, t
>
> local_specialization_stack s (lss_copy);
>
> + bool immediate_fn_ctx_p = in_immediate_fn_ctx_p;
> + in_immediate_fn_ctx_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, t
>
> finish_lambda_function (body);
>
> + in_immediate_fn_ctx_p = immediate_fn_ctx_p;
> +
> if (nested)
> pop_function_context ();
> else
> --- gcc/cp/call.c.jj 2021-06-09 21:54:39.436194489 +0200
> +++ gcc/cp/call.c 2021-06-10 09:49:35.949556470 +0200
> @@ -8840,6 +8840,7 @@ immediate_invocation_p (tree fn, int nar
> || !DECL_IMMEDIATE_FUNCTION_P (current_function_decl))
> && (current_binding_level->kind != sk_function_parms
> || !current_binding_level->immediate_fn_ctx_p)
> + && !in_immediate_fn_ctx_p
Now that we have this flag, shouldn't we set it in actual immediate
function context? Or rename the flag to match when it's actually set.
> /* As an exception, we defer std::source_location::current ()
> invocations until genericization because LWG3396 mandates
> special behavior for it. */
> --- gcc/testsuite/g++.dg/cpp23/consteval-if1.C.jj 2021-06-10 09:49:35.949556470 +0200
> +++ gcc/testsuite/g++.dg/cpp23/consteval-if1.C 2021-06-10 09:49:35.949556470 +0200
> @@ -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 <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 ();
> +}
> --- gcc/testsuite/g++.dg/cpp23/consteval-if2.C.jj 2021-06-10 09:49:35.949556470 +0200
> +++ gcc/testsuite/g++.dg/cpp23/consteval-if2.C 2021-06-10 09:58:22.459240019 +0200
> @@ -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 <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);
> +}
> --- gcc/testsuite/g++.dg/cpp23/consteval-if3.C.jj 2021-06-10 09:49:35.949556470 +0200
> +++ gcc/testsuite/g++.dg/cpp23/consteval-if3.C 2021-06-10 09:49:35.949556470 +0200
> @@ -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<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 ();
> +}
> --- gcc/testsuite/g++.dg/cpp23/consteval-if4.C.jj 2021-06-10 09:49:35.949556470 +0200
> +++ gcc/testsuite/g++.dg/cpp23/consteval-if4.C 2021-06-10 09:49:35.949556470 +0200
> @@ -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" }
> + }
> +}
> --- gcc/testsuite/g++.dg/cpp23/consteval-if5.C.jj 2021-06-10 09:49:35.949556470 +0200
> +++ gcc/testsuite/g++.dg/cpp23/consteval-if5.C 2021-06-10 09:49:35.949556470 +0200
> @@ -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" }
> + }
> +}
> --- gcc/testsuite/g++.dg/cpp23/consteval-if6.C.jj 2021-06-10 09:49:35.949556470 +0200
> +++ gcc/testsuite/g++.dg/cpp23/consteval-if6.C 2021-06-10 09:49:35.949556470 +0200
> @@ -0,0 +1,16 @@
> +// { dg-do compile { target c++20 } }
> +// { dg-options "-w" }
> +
> +void f()
> +{
> + if consteval
> + {
> + goto l;
> + l:;
> + }
> + else
> + {
> + goto l2;
> + l2:;
> + }
> +}
> --- gcc/testsuite/g++.dg/cpp23/consteval-if7.C.jj 2021-06-10 09:49:35.949556470 +0200
> +++ gcc/testsuite/g++.dg/cpp23/consteval-if7.C 2021-06-10 09:49:35.949556470 +0200
> @@ -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;
> + }
> +}
> --- gcc/testsuite/g++.dg/cpp23/consteval-if8.C.jj 2021-06-10 09:49:35.950556456 +0200
> +++ gcc/testsuite/g++.dg/cpp23/consteval-if8.C 2021-06-10 09:49:35.950556456 +0200
> @@ -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" }
> + }
> +}
> --- gcc/testsuite/g++.dg/cpp23/consteval-if9.C.jj 2021-06-10 09:49:35.950556456 +0200
> +++ gcc/testsuite/g++.dg/cpp23/consteval-if9.C 2021-06-10 09:49:35.950556456 +0200
> @@ -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" }
> + }
> +}
> --- gcc/testsuite/g++.dg/cpp23/consteval-if10.C.jj 2021-06-10 09:58:15.456337333 +0200
> +++ gcc/testsuite/g++.dg/cpp23/consteval-if10.C 2021-06-10 10:15:50.288688058 +0200
> @@ -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 <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);
> +}
> --- gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C.jj 2021-06-10 09:46:03.851503924 +0200
> +++ gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C 2021-06-10 09:49:35.950556456 +0200
> @@ -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
>
> Jakub
>
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] c++: Add C++23 consteval if support - P1938R3 [PR100974]
2021-06-10 14:09 ` Jason Merrill
@ 2021-06-10 14:44 ` Jakub Jelinek
2021-06-10 17:25 ` Jakub Jelinek
2021-06-10 19:00 ` Jason Merrill
0 siblings, 2 replies; 10+ messages in thread
From: Jakub Jelinek @ 2021-06-10 14:44 UTC (permalink / raw)
To: Jason Merrill; +Cc: gcc-patches, Jonathan Wakely
On Thu, Jun 10, 2021 at 10:09:26AM -0400, Jason Merrill wrote:
> > --- gcc/cp/parser.c.jj 2021-06-09 21:54:39.482193853 +0200
> > +++ gcc/cp/parser.c 2021-06-10 10:09:23.753052980 +0200
> > @@ -10902,6 +10902,11 @@ cp_parser_lambda_expression (cp_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 immediate_fn_ctx_p = in_immediate_fn_ctx_p;
>
> It's hard to distinguish between these two names when reading the code;
> let's give the local variable a name including "saved".
Will do.
> > + if (cp_lexer_next_token_is_not (parser->lexer, CPP_OPEN_BRACE))
> > + error ("%<if consteval%> requires compound statement");
> > +
> > + in_immediate_fn_ctx_p |= ce > 0;
> > + cp_parser_implicitly_scoped_statement (parser, NULL, guard_tinfo);
>
> Maybe use cp_parser_compound_statement directly instead of this and checking
> CPP_OPEN_BRACE above? Either way is fine.
I thought doing it this way will provide better diagnostics for what I think
can be a common bug - people so used to normal if not requiring compound
statements forgetting those {}s from time to time.
> > + if (TREE_CODE (t) == IF_STMT && IF_STMT_CONSTEVAL_P (t))
> > + {
> > + /* Evaluate the condition as if it was
> > + if (__builtin_is_constant_evaluated ()). */
> > + if (ctx->manifestly_const_eval)
> > + val = boolean_true_node;
> > + else
> > + {
> > + *non_constant_p = true;
> > + return t;
> > + }
>
> Why set *non_constant_p? Shouldn't this just be
>
> val = boolean_false_node;
>
> so we constant-evaluate the else clause when we are trying to
> constant-evaluate in a non-manifestly-constant-evaluated context?
It matches what we do for CP_BUILT_IN_IS_CONSTANT_EVALUATED calls:
/* For __builtin_is_constant_evaluated, defer it if not
ctx->manifestly_const_eval, otherwise fold it to true. */
if (fndecl_built_in_p (fun, CP_BUILT_IN_IS_CONSTANT_EVALUATED,
BUILT_IN_FRONTEND))
{
if (!ctx->manifestly_const_eval)
{
*non_constant_p = true;
return t;
}
return boolean_true_node;
}
I believe we sometimes try to constexpr evaluate something without
having manifestly_const_eval = true even on expressions that
are in manifestly constant evaluated contexts and am worried if
we just folded it to boolean_false_node we could get a constant expression
and replace the expression with that, even before we actually try to
constant evaluate it with manifestly_const_eval = true.
If I do (for the CP_BUILT_IN_IS_CONSTANT_EVALUATED):
--- gcc/cp/constexpr.c.jj 2021-06-10 15:27:31.123353594 +0200
+++ gcc/cp/constexpr.c 2021-06-10 16:26:38.368168281 +0200
@@ -1320,10 +1320,7 @@ cxx_eval_builtin_function_call (const co
BUILT_IN_FRONTEND))
{
if (!ctx->manifestly_const_eval)
- {
- *non_constant_p = true;
- return t;
- }
+ return boolean_false_node;
return boolean_true_node;
}
then
FAIL: g++.dg/cpp2a/is-constant-evaluated1.C -std=c++14 execution test
FAIL: g++.dg/cpp2a/is-constant-evaluated1.C -std=c++17 execution test
FAIL: g++.dg/cpp2a/is-constant-evaluated1.C -std=c++2a execution test
FAIL: g++.dg/cpp2a/is-constant-evaluated1.C -std=c++17 -fconcepts execution test
FAIL: g++.dg/cpp2a/is-constant-evaluated2.C -std=c++14 execution test
FAIL: g++.dg/cpp2a/is-constant-evaluated2.C -std=c++17 execution test
FAIL: g++.dg/cpp2a/is-constant-evaluated2.C -std=c++2a execution test
FAIL: g++.dg/cpp2a/is-constant-evaluated2.C -std=c++17 -fconcepts execution test
FAIL: g++.dg/cpp2a/is-constant-evaluated9.C -std=gnu++2a (test for excess errors)
at least regresses.
> > + 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);
> > + }
>
> Don't we still want to shortcut consideration of one of the arms for if
> consteval?
potential_constant_expression_p{,_1} doesn't get passed whether it is
manifestly_const_eval or not.
> > --- gcc/cp/cp-gimplify.c.jj 2021-06-09 21:54:39.473193977 +0200
> > +++ gcc/cp/cp-gimplify.c 2021-06-10 09:49:35.898557178 +0200
> > @@ -161,7 +161,9 @@ genericize_if_stmt (tree *stmt_p)
> > if (!else_)
> > else_ = build_empty_stmt (locus);
> > - if (integer_nonzerop (cond) && !TREE_SIDE_EFFECTS (else_))
> > + if (IF_STMT_CONSTEVAL_P (stmt))
> > + stmt = else_;
>
> This seems redundant, since you're using boolean_false_node for the
> condition.
It is only when !TREE_SIDE_EFFECTS (else_).
I think that is about having labels in the then_/else_ blocks and
gotos jumping into those from outside.
But IF_STMT_CONSTEVAL_P guarantees that is not the case (and we verify
earlier), so we can do that (and in fact, for IF_STMT_CONSTEVAL_P have
to, because then_ could contain consteval calls not constant expression
evaluated).
I guess we could do that for IF_STMT_CONSTEXPR_P too, that also
doesn't allow gotos/switch into the branches.
> > --- gcc/cp/call.c.jj 2021-06-09 21:54:39.436194489 +0200
> > +++ gcc/cp/call.c 2021-06-10 09:49:35.949556470 +0200
> > @@ -8840,6 +8840,7 @@ immediate_invocation_p (tree fn, int nar
> > || !DECL_IMMEDIATE_FUNCTION_P (current_function_decl))
> > && (current_binding_level->kind != sk_function_parms
> > || !current_binding_level->immediate_fn_ctx_p)
> > + && !in_immediate_fn_ctx_p
>
> Now that we have this flag, shouldn't we set it in actual immediate function
> context? Or rename the flag to match when it's actually set.
I guess I can try that. Though, I'm not sure if we could also get rid of
the current_binding_level->immediate_fn_ctx_p for sk_function_parms case.
Jakub
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] c++: Add C++23 consteval if support - P1938R3 [PR100974]
2021-06-10 14:44 ` Jakub Jelinek
@ 2021-06-10 17:25 ` Jakub Jelinek
2021-06-10 19:00 ` Jason Merrill
1 sibling, 0 replies; 10+ messages in thread
From: Jakub Jelinek @ 2021-06-10 17:25 UTC (permalink / raw)
To: Jason Merrill; +Cc: gcc-patches, Jonathan Wakely
On Thu, Jun 10, 2021 at 04:44:09PM +0200, Jakub Jelinek wrote:
> > Maybe use cp_parser_compound_statement directly instead of this and checking
> > CPP_OPEN_BRACE above? Either way is fine.
>
> I thought doing it this way will provide better diagnostics for what I think
> can be a common bug - people so used to normal if not requiring compound
> statements forgetting those {}s from time to time.
What the patch currently diagnoses is:
consteval-if2.C:13:6: error: ‘if consteval’ requires compound statement
13 | if consteval if (true) {} // { dg-error "'if consteval' requires compound statement" }
| ^~~~~~~~~
while with cp_parser_compound_statement directly it diagnoses:
consteval-if2.C:13:16: error: expected ‘{’ before ‘if’
13 | if consteval if (true) {} // { dg-error "'if consteval' requires compound statement" }
| ^~
What do you prefer?
Dunno if the fine detail that in the grammar only one of the statements
is compound-statement and then there is a requirement that the other
statement has to be a compound-statement shouldn't affect how it is
reported.
Jakub
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH] c++: Add C++23 consteval if support - P1938R3 [PR100974]
2021-06-10 14:44 ` Jakub Jelinek
2021-06-10 17:25 ` Jakub Jelinek
@ 2021-06-10 19:00 ` Jason Merrill
2021-06-11 10:28 ` [PATCH v2] " Jakub Jelinek
1 sibling, 1 reply; 10+ messages in thread
From: Jason Merrill @ 2021-06-10 19:00 UTC (permalink / raw)
To: Jakub Jelinek; +Cc: gcc-patches, Jonathan Wakely
On 6/10/21 10:44 AM, Jakub Jelinek wrote:
> On Thu, Jun 10, 2021 at 10:09:26AM -0400, Jason Merrill wrote:
>>> --- gcc/cp/parser.c.jj 2021-06-09 21:54:39.482193853 +0200
>>> +++ gcc/cp/parser.c 2021-06-10 10:09:23.753052980 +0200
>>> @@ -10902,6 +10902,11 @@ cp_parser_lambda_expression (cp_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 immediate_fn_ctx_p = in_immediate_fn_ctx_p;
>>
>> It's hard to distinguish between these two names when reading the code;
>> let's give the local variable a name including "saved".
>
> Will do.
>
>>> + if (cp_lexer_next_token_is_not (parser->lexer, CPP_OPEN_BRACE))
>>> + error ("%<if consteval%> requires compound statement");
>>> +
>>> + in_immediate_fn_ctx_p |= ce > 0;
>>> + cp_parser_implicitly_scoped_statement (parser, NULL, guard_tinfo);
>>
>> Maybe use cp_parser_compound_statement directly instead of this and checking
>> CPP_OPEN_BRACE above? Either way is fine.
>
> I thought doing it this way will provide better diagnostics for what I think
> can be a common bug - people so used to normal if not requiring compound
> statements forgetting those {}s from time to time.
> What the patch currently diagnoses is:
> consteval-if2.C:13:6: error: ‘if consteval’ requires compound statement
> 13 | if consteval if (true) {} // { dg-error "'if consteval' requires compound statement" }
> | ^~~~~~~~~
> while with cp_parser_compound_statement directly it diagnoses:
> consteval-if2.C:13:16: error: expected ‘{’ before ‘if’
> 13 | if consteval if (true) {} // { dg-error "'if consteval' requires compound statement" }
> | ^~
> What do you prefer?
The second is clearer about the fix, the first is clearer about the
problem. Maybe add a fixit to the first error?
> Dunno if the fine detail that in the grammar only one of the statements
> is compound-statement and then there is a requirement that the other
> statement has to be a compound-statement shouldn't affect how it is
> reported.
The difference was to prevent "else ;" from binding to an enclosing if
rather than the if consteval.
>>> + if (TREE_CODE (t) == IF_STMT && IF_STMT_CONSTEVAL_P (t))
>>> + {
>>> + /* Evaluate the condition as if it was
>>> + if (__builtin_is_constant_evaluated ()). */
>>> + if (ctx->manifestly_const_eval)
>>> + val = boolean_true_node;
>>> + else
>>> + {
>>> + *non_constant_p = true;
>>> + return t;
>>> + }
>>
>> Why set *non_constant_p? Shouldn't this just be
>>
>> val = boolean_false_node;
>>
>> so we constant-evaluate the else clause when we are trying to
>> constant-evaluate in a non-manifestly-constant-evaluated context?
>
> It matches what we do for CP_BUILT_IN_IS_CONSTANT_EVALUATED calls:
> /* For __builtin_is_constant_evaluated, defer it if not
> ctx->manifestly_const_eval, otherwise fold it to true. */
> if (fndecl_built_in_p (fun, CP_BUILT_IN_IS_CONSTANT_EVALUATED,
> BUILT_IN_FRONTEND))
> {
> if (!ctx->manifestly_const_eval)
> {
> *non_constant_p = true;
> return t;
> }
> return boolean_true_node;
> }
> I believe we sometimes try to constexpr evaluate something without
> having manifestly_const_eval = true even on expressions that
> are in manifestly constant evaluated contexts and am worried if
> we just folded it to boolean_false_node we could get a constant expression
> and replace the expression with that, even before we actually try to
> constant evaluate it with manifestly_const_eval = true.
>
> If I do (for the CP_BUILT_IN_IS_CONSTANT_EVALUATED):
> --- gcc/cp/constexpr.c.jj 2021-06-10 15:27:31.123353594 +0200
> +++ gcc/cp/constexpr.c 2021-06-10 16:26:38.368168281 +0200
> @@ -1320,10 +1320,7 @@ cxx_eval_builtin_function_call (const co
> BUILT_IN_FRONTEND))
> {
> if (!ctx->manifestly_const_eval)
> - {
> - *non_constant_p = true;
> - return t;
> - }
> + return boolean_false_node;
> return boolean_true_node;
> }
>
> then
> FAIL: g++.dg/cpp2a/is-constant-evaluated1.C -std=c++14 execution test
> FAIL: g++.dg/cpp2a/is-constant-evaluated1.C -std=c++17 execution test
> FAIL: g++.dg/cpp2a/is-constant-evaluated1.C -std=c++2a execution test
> FAIL: g++.dg/cpp2a/is-constant-evaluated1.C -std=c++17 -fconcepts execution test
> FAIL: g++.dg/cpp2a/is-constant-evaluated2.C -std=c++14 execution test
> FAIL: g++.dg/cpp2a/is-constant-evaluated2.C -std=c++17 execution test
> FAIL: g++.dg/cpp2a/is-constant-evaluated2.C -std=c++2a execution test
> FAIL: g++.dg/cpp2a/is-constant-evaluated2.C -std=c++17 -fconcepts execution test
> FAIL: g++.dg/cpp2a/is-constant-evaluated9.C -std=gnu++2a (test for excess errors)
> at least regresses.
OK, just add a comment then.
>>> + 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);
>>> + }
>>
>> Don't we still want to shortcut consideration of one of the arms for if
>> consteval?
>
> potential_constant_expression_p{,_1} doesn't get passed whether it is
> manifestly_const_eval or not.
OK, just add a comment then.
>>> --- gcc/cp/cp-gimplify.c.jj 2021-06-09 21:54:39.473193977 +0200
>>> +++ gcc/cp/cp-gimplify.c 2021-06-10 09:49:35.898557178 +0200
>>> @@ -161,7 +161,9 @@ genericize_if_stmt (tree *stmt_p)
>>> if (!else_)
>>> else_ = build_empty_stmt (locus);
>>> - if (integer_nonzerop (cond) && !TREE_SIDE_EFFECTS (else_))
>>> + if (IF_STMT_CONSTEVAL_P (stmt))
>>> + stmt = else_;
>>
>> This seems redundant, since you're using boolean_false_node for the
>> condition.
>
> It is only when !TREE_SIDE_EFFECTS (else_).
> I think that is about having labels in the then_/else_ blocks and
> gotos jumping into those from outside.
> But IF_STMT_CONSTEVAL_P guarantees that is not the case (and we verify
> earlier), so we can do that (and in fact, for IF_STMT_CONSTEVAL_P have
> to, because then_ could contain consteval calls not constant expression
> evaluated).
> I guess we could do that for IF_STMT_CONSTEXPR_P too, that also
> doesn't allow gotos/switch into the branches.
>
>>> --- gcc/cp/call.c.jj 2021-06-09 21:54:39.436194489 +0200
>>> +++ gcc/cp/call.c 2021-06-10 09:49:35.949556470 +0200
>>> @@ -8840,6 +8840,7 @@ immediate_invocation_p (tree fn, int nar
>>> || !DECL_IMMEDIATE_FUNCTION_P (current_function_decl))
>>> && (current_binding_level->kind != sk_function_parms
>>> || !current_binding_level->immediate_fn_ctx_p)
>>> + && !in_immediate_fn_ctx_p
>>
>> Now that we have this flag, shouldn't we set it in actual immediate function
>> context? Or rename the flag to match when it's actually set.
>
> I guess I can try that. Though, I'm not sure if we could also get rid of
> the current_binding_level->immediate_fn_ctx_p for sk_function_parms case.
>
> Jakub
>
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v2] c++: Add C++23 consteval if support - P1938R3 [PR100974]
2021-06-10 19:00 ` Jason Merrill
@ 2021-06-11 10:28 ` Jakub Jelinek
2021-06-11 12:49 ` Jason Merrill
0 siblings, 1 reply; 10+ messages in thread
From: Jakub Jelinek @ 2021-06-11 10:28 UTC (permalink / raw)
To: Jason Merrill; +Cc: gcc-patches, Jonathan Wakely
On Thu, Jun 10, 2021 at 03:00:54PM -0400, Jason Merrill wrote:
> The second is clearer about the fix, the first is clearer about the problem.
> Maybe add a fixit to the first error?
Done.
> OK, just add a comment then.
>
> OK, just add a comment then.
And added comments.
> > > > --- gcc/cp/cp-gimplify.c.jj 2021-06-09 21:54:39.473193977 +0200
> > > > +++ gcc/cp/cp-gimplify.c 2021-06-10 09:49:35.898557178 +0200
> > > > @@ -161,7 +161,9 @@ genericize_if_stmt (tree *stmt_p)
> > > > if (!else_)
> > > > else_ = build_empty_stmt (locus);
> > > > - if (integer_nonzerop (cond) && !TREE_SIDE_EFFECTS (else_))
> > > > + if (IF_STMT_CONSTEVAL_P (stmt))
> > > > + stmt = else_;
> > >
> > > This seems redundant, since you're using boolean_false_node for the
> > > condition.
> >
> > It is only when !TREE_SIDE_EFFECTS (else_).
> > I think that is about having labels in the then_/else_ blocks and
> > gotos jumping into those from outside.
> > But IF_STMT_CONSTEVAL_P guarantees that is not the case (and we verify
> > earlier), so we can do that (and in fact, for IF_STMT_CONSTEVAL_P have
> > to, because then_ could contain consteval calls not constant expression
> > evaluated).
> > I guess we could do that for IF_STMT_CONSTEXPR_P too, that also
> > doesn't allow gotos/switch into the branches.
For this too.
> >
> > > > --- gcc/cp/call.c.jj 2021-06-09 21:54:39.436194489 +0200
> > > > +++ gcc/cp/call.c 2021-06-10 09:49:35.949556470 +0200
> > > > @@ -8840,6 +8840,7 @@ immediate_invocation_p (tree fn, int nar
> > > > || !DECL_IMMEDIATE_FUNCTION_P (current_function_decl))
> > > > && (current_binding_level->kind != sk_function_parms
> > > > || !current_binding_level->immediate_fn_ctx_p)
> > > > + && !in_immediate_fn_ctx_p
> > >
> > > Now that we have this flag, shouldn't we set it in actual immediate function
> > > context? Or rename the flag to match when it's actually set.
> >
> > I guess I can try that. Though, I'm not sure if we could also get rid of
> > the current_binding_level->immediate_fn_ctx_p for sk_function_parms case.
Had a look at this, but could handle it.
So instead I've renamed it to in_consteval_if_p.
Ok for trunk if it passes bootstrap/regtest?
2021-06-11 Jakub Jelinek <jakub@redhat.com>
PR c++/100974 - P1938R3 - if consteval
gcc/c-family/
* c-cppbuiltin.c (c_cpp_builtins): Predefine __cpp_if_consteval for
-std=c++2b.
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.
--- gcc/c-family/c-cppbuiltin.c.jj 2021-06-10 19:56:20.584335765 +0200
+++ gcc/c-family/c-cppbuiltin.c 2021-06-11 11:34:15.408578098 +0200
@@ -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)
{
--- gcc/cp/cp-tree.h.jj 2021-06-11 11:32:35.857980773 +0200
+++ gcc/cp/cp-tree.h 2021-06-11 11:35:52.468210524 +0200
@@ -478,6 +478,7 @@ extern GTY(()) tree cp_global_trees[CPTI
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
#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_i
#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. */
--- gcc/cp/parser.c.jj 2021-06-10 19:56:20.625335201 +0200
+++ gcc/cp/parser.c 2021-06-11 12:07:36.096767982 +0200
@@ -10902,6 +10902,11 @@ cp_parser_lambda_expression (cp_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*
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
"%<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;
--- gcc/cp/decl.c.jj 2021-06-10 19:56:20.591335668 +0200
+++ gcc/cp/decl.c 2021-06-11 11:34:15.466577281 +0200
@@ -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
&& 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_entr
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_bin
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_bin
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:
@@ -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 %<constexpr if%> statement");
+ else if (ent->in_consteval_if)
+ inform (input_location, " enters %<consteval if%> statement");
}
if (ent->in_omp_scope)
--- gcc/cp/constexpr.c.jj 2021-06-11 11:32:35.841980998 +0200
+++ gcc/cp/constexpr.c 2021-06-11 11:41:04.425832699 +0200
@@ -1315,7 +1315,10 @@ cxx_eval_builtin_function_call (const co
}
/* 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 c
/*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,
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)
{
--- gcc/cp/cp-gimplify.c.jj 2021-06-10 19:56:20.587335723 +0200
+++ gcc/cp/cp-gimplify.c 2021-06-11 11:44:41.479824945 +0200
@@ -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_;
--- gcc/cp/pt.c.jj 2021-06-11 11:32:35.862980702 +0200
+++ gcc/cp/pt.c 2021-06-11 11:34:15.484577027 +0200
@@ -18413,6 +18413,7 @@ tsubst_expr (tree t, tree args, tsubst_f
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_f
}
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, t
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, t
finish_lambda_function (body);
+ in_consteval_if_p = save_in_consteval_if_p;
+
if (nested)
pop_function_context ();
else
--- gcc/cp/call.c.jj 2021-06-10 19:56:20.585335751 +0200
+++ gcc/cp/call.c 2021-06-11 11:34:15.499576816 +0200
@@ -8840,6 +8840,7 @@ immediate_invocation_p (tree fn, int nar
|| !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. */
--- gcc/testsuite/g++.dg/cpp23/consteval-if1.C.jj 2021-06-11 11:34:15.500576802 +0200
+++ gcc/testsuite/g++.dg/cpp23/consteval-if1.C 2021-06-11 11:34:15.500576802 +0200
@@ -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 <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 ();
+}
--- gcc/testsuite/g++.dg/cpp23/consteval-if2.C.jj 2021-06-11 11:34:15.500576802 +0200
+++ gcc/testsuite/g++.dg/cpp23/consteval-if2.C 2021-06-11 11:34:15.500576802 +0200
@@ -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 <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);
+}
--- gcc/testsuite/g++.dg/cpp23/consteval-if3.C.jj 2021-06-11 11:34:15.500576802 +0200
+++ gcc/testsuite/g++.dg/cpp23/consteval-if3.C 2021-06-11 11:34:15.500576802 +0200
@@ -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<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 ();
+}
--- gcc/testsuite/g++.dg/cpp23/consteval-if4.C.jj 2021-06-11 11:34:15.500576802 +0200
+++ gcc/testsuite/g++.dg/cpp23/consteval-if4.C 2021-06-11 11:34:15.500576802 +0200
@@ -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" }
+ }
+}
--- gcc/testsuite/g++.dg/cpp23/consteval-if5.C.jj 2021-06-11 11:34:15.500576802 +0200
+++ gcc/testsuite/g++.dg/cpp23/consteval-if5.C 2021-06-11 11:34:15.500576802 +0200
@@ -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" }
+ }
+}
--- gcc/testsuite/g++.dg/cpp23/consteval-if6.C.jj 2021-06-11 11:34:15.500576802 +0200
+++ gcc/testsuite/g++.dg/cpp23/consteval-if6.C 2021-06-11 11:34:15.500576802 +0200
@@ -0,0 +1,16 @@
+// { dg-do compile { target c++20 } }
+// { dg-options "-w" }
+
+void f()
+{
+ if consteval
+ {
+ goto l;
+ l:;
+ }
+ else
+ {
+ goto l2;
+ l2:;
+ }
+}
--- gcc/testsuite/g++.dg/cpp23/consteval-if7.C.jj 2021-06-11 11:34:15.500576802 +0200
+++ gcc/testsuite/g++.dg/cpp23/consteval-if7.C 2021-06-11 11:34:15.500576802 +0200
@@ -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;
+ }
+}
--- gcc/testsuite/g++.dg/cpp23/consteval-if8.C.jj 2021-06-11 11:34:15.500576802 +0200
+++ gcc/testsuite/g++.dg/cpp23/consteval-if8.C 2021-06-11 11:34:15.500576802 +0200
@@ -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" }
+ }
+}
--- gcc/testsuite/g++.dg/cpp23/consteval-if9.C.jj 2021-06-11 11:34:15.500576802 +0200
+++ gcc/testsuite/g++.dg/cpp23/consteval-if9.C 2021-06-11 11:34:15.500576802 +0200
@@ -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" }
+ }
+}
--- gcc/testsuite/g++.dg/cpp23/consteval-if10.C.jj 2021-06-11 11:34:15.500576802 +0200
+++ gcc/testsuite/g++.dg/cpp23/consteval-if10.C 2021-06-11 11:34:15.500576802 +0200
@@ -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 <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);
+}
--- gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C.jj 2021-06-10 19:56:20.629335146 +0200
+++ gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C 2021-06-11 11:34:15.508576689 +0200
@@ -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
Jakub
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2] c++: Add C++23 consteval if support - P1938R3 [PR100974]
2021-06-11 10:28 ` [PATCH v2] " Jakub Jelinek
@ 2021-06-11 12:49 ` Jason Merrill
0 siblings, 0 replies; 10+ messages in thread
From: Jason Merrill @ 2021-06-11 12:49 UTC (permalink / raw)
To: Jakub Jelinek; +Cc: gcc-patches, Jonathan Wakely
On 6/11/21 6:28 AM, Jakub Jelinek wrote:
> On Thu, Jun 10, 2021 at 03:00:54PM -0400, Jason Merrill wrote:
>> The second is clearer about the fix, the first is clearer about the problem.
>> Maybe add a fixit to the first error?
>
> Done.
>
>> OK, just add a comment then.
>>
>> OK, just add a comment then.
>
> And added comments.
>
>>>>> --- gcc/cp/cp-gimplify.c.jj 2021-06-09 21:54:39.473193977 +0200
>>>>> +++ gcc/cp/cp-gimplify.c 2021-06-10 09:49:35.898557178 +0200
>>>>> @@ -161,7 +161,9 @@ genericize_if_stmt (tree *stmt_p)
>>>>> if (!else_)
>>>>> else_ = build_empty_stmt (locus);
>>>>> - if (integer_nonzerop (cond) && !TREE_SIDE_EFFECTS (else_))
>>>>> + if (IF_STMT_CONSTEVAL_P (stmt))
>>>>> + stmt = else_;
>>>>
>>>> This seems redundant, since you're using boolean_false_node for the
>>>> condition.
>>>
>>> It is only when !TREE_SIDE_EFFECTS (else_).
>>> I think that is about having labels in the then_/else_ blocks and
>>> gotos jumping into those from outside.
>>> But IF_STMT_CONSTEVAL_P guarantees that is not the case (and we verify
>>> earlier), so we can do that (and in fact, for IF_STMT_CONSTEVAL_P have
>>> to, because then_ could contain consteval calls not constant expression
>>> evaluated).
>>> I guess we could do that for IF_STMT_CONSTEXPR_P too, that also
>>> doesn't allow gotos/switch into the branches.
>
> For this too.
>>>
>>>>> --- gcc/cp/call.c.jj 2021-06-09 21:54:39.436194489 +0200
>>>>> +++ gcc/cp/call.c 2021-06-10 09:49:35.949556470 +0200
>>>>> @@ -8840,6 +8840,7 @@ immediate_invocation_p (tree fn, int nar
>>>>> || !DECL_IMMEDIATE_FUNCTION_P (current_function_decl))
>>>>> && (current_binding_level->kind != sk_function_parms
>>>>> || !current_binding_level->immediate_fn_ctx_p)
>>>>> + && !in_immediate_fn_ctx_p
>>>>
>>>> Now that we have this flag, shouldn't we set it in actual immediate function
>>>> context? Or rename the flag to match when it's actually set.
>>>
>>> I guess I can try that. Though, I'm not sure if we could also get rid of
>>> the current_binding_level->immediate_fn_ctx_p for sk_function_parms case.
>
> Had a look at this, but could handle it.
> So instead I've renamed it to in_consteval_if_p.
>
> Ok for trunk if it passes bootstrap/regtest?
OK.
> 2021-06-11 Jakub Jelinek <jakub@redhat.com>
>
> PR c++/100974 - P1938R3 - if consteval
> gcc/c-family/
> * c-cppbuiltin.c (c_cpp_builtins): Predefine __cpp_if_consteval for
> -std=c++2b.
> 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.
>
> --- gcc/c-family/c-cppbuiltin.c.jj 2021-06-10 19:56:20.584335765 +0200
> +++ gcc/c-family/c-cppbuiltin.c 2021-06-11 11:34:15.408578098 +0200
> @@ -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)
> {
> --- gcc/cp/cp-tree.h.jj 2021-06-11 11:32:35.857980773 +0200
> +++ gcc/cp/cp-tree.h 2021-06-11 11:35:52.468210524 +0200
> @@ -478,6 +478,7 @@ extern GTY(()) tree cp_global_trees[CPTI
> 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
> #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_i
> #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. */
> --- gcc/cp/parser.c.jj 2021-06-10 19:56:20.625335201 +0200
> +++ gcc/cp/parser.c 2021-06-11 12:07:36.096767982 +0200
> @@ -10902,6 +10902,11 @@ cp_parser_lambda_expression (cp_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*
>
> 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
> "%<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;
> --- gcc/cp/decl.c.jj 2021-06-10 19:56:20.591335668 +0200
> +++ gcc/cp/decl.c 2021-06-11 11:34:15.466577281 +0200
> @@ -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
> && 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_entr
> 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_bin
> 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_bin
> 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:
> @@ -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 %<constexpr if%> statement");
> + else if (ent->in_consteval_if)
> + inform (input_location, " enters %<consteval if%> statement");
> }
>
> if (ent->in_omp_scope)
> --- gcc/cp/constexpr.c.jj 2021-06-11 11:32:35.841980998 +0200
> +++ gcc/cp/constexpr.c 2021-06-11 11:41:04.425832699 +0200
> @@ -1315,7 +1315,10 @@ cxx_eval_builtin_function_call (const co
> }
>
> /* 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 c
> /*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,
> 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)
> {
> --- gcc/cp/cp-gimplify.c.jj 2021-06-10 19:56:20.587335723 +0200
> +++ gcc/cp/cp-gimplify.c 2021-06-11 11:44:41.479824945 +0200
> @@ -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_;
> --- gcc/cp/pt.c.jj 2021-06-11 11:32:35.862980702 +0200
> +++ gcc/cp/pt.c 2021-06-11 11:34:15.484577027 +0200
> @@ -18413,6 +18413,7 @@ tsubst_expr (tree t, tree args, tsubst_f
> 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_f
> }
> 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, t
>
> 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, t
>
> finish_lambda_function (body);
>
> + in_consteval_if_p = save_in_consteval_if_p;
> +
> if (nested)
> pop_function_context ();
> else
> --- gcc/cp/call.c.jj 2021-06-10 19:56:20.585335751 +0200
> +++ gcc/cp/call.c 2021-06-11 11:34:15.499576816 +0200
> @@ -8840,6 +8840,7 @@ immediate_invocation_p (tree fn, int nar
> || !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. */
> --- gcc/testsuite/g++.dg/cpp23/consteval-if1.C.jj 2021-06-11 11:34:15.500576802 +0200
> +++ gcc/testsuite/g++.dg/cpp23/consteval-if1.C 2021-06-11 11:34:15.500576802 +0200
> @@ -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 <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 ();
> +}
> --- gcc/testsuite/g++.dg/cpp23/consteval-if2.C.jj 2021-06-11 11:34:15.500576802 +0200
> +++ gcc/testsuite/g++.dg/cpp23/consteval-if2.C 2021-06-11 11:34:15.500576802 +0200
> @@ -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 <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);
> +}
> --- gcc/testsuite/g++.dg/cpp23/consteval-if3.C.jj 2021-06-11 11:34:15.500576802 +0200
> +++ gcc/testsuite/g++.dg/cpp23/consteval-if3.C 2021-06-11 11:34:15.500576802 +0200
> @@ -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<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 ();
> +}
> --- gcc/testsuite/g++.dg/cpp23/consteval-if4.C.jj 2021-06-11 11:34:15.500576802 +0200
> +++ gcc/testsuite/g++.dg/cpp23/consteval-if4.C 2021-06-11 11:34:15.500576802 +0200
> @@ -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" }
> + }
> +}
> --- gcc/testsuite/g++.dg/cpp23/consteval-if5.C.jj 2021-06-11 11:34:15.500576802 +0200
> +++ gcc/testsuite/g++.dg/cpp23/consteval-if5.C 2021-06-11 11:34:15.500576802 +0200
> @@ -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" }
> + }
> +}
> --- gcc/testsuite/g++.dg/cpp23/consteval-if6.C.jj 2021-06-11 11:34:15.500576802 +0200
> +++ gcc/testsuite/g++.dg/cpp23/consteval-if6.C 2021-06-11 11:34:15.500576802 +0200
> @@ -0,0 +1,16 @@
> +// { dg-do compile { target c++20 } }
> +// { dg-options "-w" }
> +
> +void f()
> +{
> + if consteval
> + {
> + goto l;
> + l:;
> + }
> + else
> + {
> + goto l2;
> + l2:;
> + }
> +}
> --- gcc/testsuite/g++.dg/cpp23/consteval-if7.C.jj 2021-06-11 11:34:15.500576802 +0200
> +++ gcc/testsuite/g++.dg/cpp23/consteval-if7.C 2021-06-11 11:34:15.500576802 +0200
> @@ -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;
> + }
> +}
> --- gcc/testsuite/g++.dg/cpp23/consteval-if8.C.jj 2021-06-11 11:34:15.500576802 +0200
> +++ gcc/testsuite/g++.dg/cpp23/consteval-if8.C 2021-06-11 11:34:15.500576802 +0200
> @@ -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" }
> + }
> +}
> --- gcc/testsuite/g++.dg/cpp23/consteval-if9.C.jj 2021-06-11 11:34:15.500576802 +0200
> +++ gcc/testsuite/g++.dg/cpp23/consteval-if9.C 2021-06-11 11:34:15.500576802 +0200
> @@ -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" }
> + }
> +}
> --- gcc/testsuite/g++.dg/cpp23/consteval-if10.C.jj 2021-06-11 11:34:15.500576802 +0200
> +++ gcc/testsuite/g++.dg/cpp23/consteval-if10.C 2021-06-11 11:34:15.500576802 +0200
> @@ -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 <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);
> +}
> --- gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C.jj 2021-06-10 19:56:20.629335146 +0200
> +++ gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C 2021-06-11 11:34:15.508576689 +0200
> @@ -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
>
>
> Jakub
>
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2021-06-11 12:49 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-06-10 8:34 [PATCH] c++: Add C++23 consteval if support - P1938R3 [PR100974] Jakub Jelinek
2021-06-10 10:24 ` Jonathan Wakely
2021-06-10 10:34 ` Jakub Jelinek
2021-06-10 10:43 ` Jonathan Wakely
2021-06-10 14:09 ` Jason Merrill
2021-06-10 14:44 ` Jakub Jelinek
2021-06-10 17:25 ` Jakub Jelinek
2021-06-10 19:00 ` Jason Merrill
2021-06-11 10:28 ` [PATCH v2] " Jakub Jelinek
2021-06-11 12:49 ` Jason Merrill
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).