c++: Add C++23 consteval if support - P1938R3 [PR100974]
The following patch implements consteval if support. There is a new IF_STMT_CONSTEVAL_P flag on IF_STMT and IF_COND is boolean_false_node to match the non-manifestly constant evaluation behavior, while constexpr evaluation special-cases it. Perhaps cleaner would be to set the condition to __builtin_is_constant_evaluated () call but we need the IF_STMT_CONSTEVAL_P flag anyway and the IL would be larger. And I'm not changing the libstdc++ side, where perhaps we could change std::is_constant_evaluated definition for #ifdef __cpp_if_consteval case to if consteval { return true; } else { return false; } but we need to keep it defined to __builtin_is_constant_evaluated () for C++20 or older. 2021-06-11 Jakub Jelinek <jakub@redhat.com> PR c++/100974 gcc/c-family/ * c-cppbuiltin.c (c_cpp_builtins): Predefine __cpp_if_consteval for -std=c++2b for P1938R3 consteval if support. gcc/cp/ * cp-tree.h (struct saved_scope): Add consteval_if_p member. Formatting fix for the discarded_stmt comment. (in_consteval_if_p, IF_STMT_CONSTEVAL_P): Define. * parser.c (cp_parser_lambda_expression): Temporarily disable in_consteval_if_p when parsing lambda body. (cp_parser_selection_statement): Parse consteval if. * decl.c (struct named_label_entry): Add in_consteval_if member. (level_for_consteval_if): New function. (poplevel_named_label_1, check_previous_goto_1, check_goto): Handle consteval if. * constexpr.c (cxx_eval_builtin_function_call): Clarify in comment why CP_BUILT_IN_IS_CONSTANT_EVALUATED needs to *non_constant_p for !ctx->manifestly_const_eval. (cxx_eval_conditional_expression): For IF_STMT_CONSTEVAL_P evaluate condition as if it was __builtin_is_constant_evaluated call. (potential_constant_expression_1): For IF_STMT_CONSTEVAL_P always recurse on both branches. * cp-gimplify.c (genericize_if_stmt): Genericize IF_STMT_CONSTEVAL_P as the else branch. * pt.c (tsubst_expr) <case IF_STMT>: Copy IF_STMT_CONSTEVAL_P. Temporarily set in_consteval_if_p when recursing on IF_STMT_CONSTEVAL_P then branch. (tsubst_lambda_expr): Temporarily disable in_consteval_if_p when instantiating lambda body. * call.c (immediate_invocation_p): Return false when in_consteval_if_p. gcc/testsuite/ * g++.dg/cpp23/consteval-if1.C: New test. * g++.dg/cpp23/consteval-if2.C: New test. * g++.dg/cpp23/consteval-if3.C: New test. * g++.dg/cpp23/consteval-if4.C: New test. * g++.dg/cpp23/consteval-if5.C: New test. * g++.dg/cpp23/consteval-if6.C: New test. * g++.dg/cpp23/consteval-if7.C: New test. * g++.dg/cpp23/consteval-if8.C: New test. * g++.dg/cpp23/consteval-if9.C: New test. * g++.dg/cpp23/consteval-if10.C: New test. * g++.dg/cpp23/feat-cxx2b.C: Add __cpp_if_consteval tests.
This commit is contained in:
parent
9d20ec9747
commit
117c642664
19 changed files with 650 additions and 10 deletions
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -8840,6 +8840,7 @@ immediate_invocation_p (tree fn, int nargs)
|
|||
|| !DECL_IMMEDIATE_FUNCTION_P (current_function_decl))
|
||||
&& (current_binding_level->kind != sk_function_parms
|
||||
|| !current_binding_level->immediate_fn_ctx_p)
|
||||
&& !in_consteval_if_p
|
||||
/* As an exception, we defer std::source_location::current ()
|
||||
invocations until genericization because LWG3396 mandates
|
||||
special behavior for it. */
|
||||
|
|
|
@ -1315,7 +1315,10 @@ cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun,
|
|||
}
|
||||
|
||||
/* For __builtin_is_constant_evaluated, defer it if not
|
||||
ctx->manifestly_const_eval, otherwise fold it to true. */
|
||||
ctx->manifestly_const_eval (as sometimes we try to constant evaluate
|
||||
without manifestly_const_eval even expressions or parts thereof which
|
||||
will later be manifestly const_eval evaluated), otherwise fold it to
|
||||
true. */
|
||||
if (fndecl_built_in_p (fun, CP_BUILT_IN_IS_CONSTANT_EVALUATED,
|
||||
BUILT_IN_FRONTEND))
|
||||
{
|
||||
|
@ -3298,6 +3301,22 @@ cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t,
|
|||
/*lval*/false,
|
||||
non_constant_p, overflow_p);
|
||||
VERIFY_CONSTANT (val);
|
||||
if (TREE_CODE (t) == IF_STMT && IF_STMT_CONSTEVAL_P (t))
|
||||
{
|
||||
/* Evaluate the condition as if it was
|
||||
if (__builtin_is_constant_evaluated ()), i.e. defer it if not
|
||||
ctx->manifestly_const_eval (as sometimes we try to constant evaluate
|
||||
without manifestly_const_eval even expressions or parts thereof which
|
||||
will later be manifestly const_eval evaluated), otherwise fold it to
|
||||
true. */
|
||||
if (ctx->manifestly_const_eval)
|
||||
val = boolean_true_node;
|
||||
else
|
||||
{
|
||||
*non_constant_p = true;
|
||||
return t;
|
||||
}
|
||||
}
|
||||
/* Don't VERIFY_CONSTANT the other operands. */
|
||||
if (integer_zerop (val))
|
||||
val = TREE_OPERAND (t, 2);
|
||||
|
@ -8809,10 +8828,17 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
|
|||
return false;
|
||||
if (!processing_template_decl)
|
||||
tmp = cxx_eval_outermost_constant_expr (tmp, true);
|
||||
if (integer_zerop (tmp))
|
||||
return RECUR (TREE_OPERAND (t, 2), want_rval);
|
||||
else if (TREE_CODE (tmp) == INTEGER_CST)
|
||||
return RECUR (TREE_OPERAND (t, 1), want_rval);
|
||||
/* potential_constant_expression* isn't told if it is called for
|
||||
manifestly_const_eval or not, so for consteval if always
|
||||
process both branches as if the condition is not a known
|
||||
constant. */
|
||||
if (TREE_CODE (t) != IF_STMT || !IF_STMT_CONSTEVAL_P (t))
|
||||
{
|
||||
if (integer_zerop (tmp))
|
||||
return RECUR (TREE_OPERAND (t, 2), want_rval);
|
||||
else if (TREE_CODE (tmp) == INTEGER_CST)
|
||||
return RECUR (TREE_OPERAND (t, 1), want_rval);
|
||||
}
|
||||
tmp = *jump_target;
|
||||
for (i = 1; i < 3; ++i)
|
||||
{
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -478,6 +478,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
|
|||
AGGR_INIT_ZERO_FIRST (in AGGR_INIT_EXPR)
|
||||
CONSTRUCTOR_MUTABLE_POISON (in CONSTRUCTOR)
|
||||
OVL_HIDDEN_P (in OVERLOAD)
|
||||
IF_STMT_CONSTEVAL_P (in IF_STMT)
|
||||
SWITCH_STMT_NO_BREAK_P (in SWITCH_STMT)
|
||||
LAMBDA_EXPR_CAPTURE_OPTIMIZED (in LAMBDA_EXPR)
|
||||
IMPLICIT_CONV_EXPR_BRACED_INIT (in IMPLICIT_CONV_EXPR)
|
||||
|
@ -1813,9 +1814,12 @@ struct GTY(()) saved_scope {
|
|||
BOOL_BITFIELD x_processing_explicit_instantiation : 1;
|
||||
BOOL_BITFIELD need_pop_function_context : 1;
|
||||
|
||||
/* Nonzero if we are parsing the discarded statement of a constexpr
|
||||
if-statement. */
|
||||
/* Nonzero if we are parsing the discarded statement of a constexpr
|
||||
if-statement. */
|
||||
BOOL_BITFIELD discarded_stmt : 1;
|
||||
/* Nonzero if we are parsing or instantiating the compound-statement
|
||||
of consteval if statement. */
|
||||
BOOL_BITFIELD consteval_if_p : 1;
|
||||
|
||||
int unevaluated_operand;
|
||||
int inhibit_evaluation_warnings;
|
||||
|
@ -1879,6 +1883,7 @@ extern GTY(()) struct saved_scope *scope_chain;
|
|||
#define processing_explicit_instantiation scope_chain->x_processing_explicit_instantiation
|
||||
|
||||
#define in_discarded_stmt scope_chain->discarded_stmt
|
||||
#define in_consteval_if_p scope_chain->consteval_if_p
|
||||
|
||||
#define current_ref_temp_count scope_chain->ref_temp_count
|
||||
|
||||
|
@ -5211,6 +5216,7 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
|
|||
#define ELSE_CLAUSE(NODE) TREE_OPERAND (IF_STMT_CHECK (NODE), 2)
|
||||
#define IF_SCOPE(NODE) TREE_OPERAND (IF_STMT_CHECK (NODE), 3)
|
||||
#define IF_STMT_CONSTEXPR_P(NODE) TREE_LANG_FLAG_0 (IF_STMT_CHECK (NODE))
|
||||
#define IF_STMT_CONSTEVAL_P(NODE) TREE_LANG_FLAG_2 (IF_STMT_CHECK (NODE))
|
||||
|
||||
/* Like PACK_EXPANSION_EXTRA_ARGS, for constexpr if. IF_SCOPE is used while
|
||||
building an IF_STMT; IF_STMT_EXTRA_ARGS is used after it is complete. */
|
||||
|
|
|
@ -222,6 +222,7 @@ struct GTY((for_user)) named_label_entry {
|
|||
bool in_omp_scope;
|
||||
bool in_transaction_scope;
|
||||
bool in_constexpr_if;
|
||||
bool in_consteval_if;
|
||||
};
|
||||
|
||||
#define named_labels cp_function_chain->x_named_labels
|
||||
|
@ -491,6 +492,16 @@ level_for_constexpr_if (cp_binding_level *b)
|
|||
&& IF_STMT_CONSTEXPR_P (b->this_entity));
|
||||
}
|
||||
|
||||
/* True if B is the level for the condition of a consteval if. */
|
||||
|
||||
static bool
|
||||
level_for_consteval_if (cp_binding_level *b)
|
||||
{
|
||||
return (b->kind == sk_cond && b->this_entity
|
||||
&& TREE_CODE (b->this_entity) == IF_STMT
|
||||
&& IF_STMT_CONSTEVAL_P (b->this_entity));
|
||||
}
|
||||
|
||||
/* Update data for defined and undefined labels when leaving a scope. */
|
||||
|
||||
int
|
||||
|
@ -530,6 +541,8 @@ poplevel_named_label_1 (named_label_entry **slot, cp_binding_level *bl)
|
|||
case sk_block:
|
||||
if (level_for_constexpr_if (bl->level_chain))
|
||||
ent->in_constexpr_if = true;
|
||||
else if (level_for_consteval_if (bl->level_chain))
|
||||
ent->in_consteval_if = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -3391,6 +3404,7 @@ check_previous_goto_1 (tree decl, cp_binding_level* level, tree names,
|
|||
bool complained = false;
|
||||
int identified = 0;
|
||||
bool saw_eh = false, saw_omp = false, saw_tm = false, saw_cxif = false;
|
||||
bool saw_ceif = false;
|
||||
|
||||
if (exited_omp)
|
||||
{
|
||||
|
@ -3470,6 +3484,12 @@ check_previous_goto_1 (tree decl, cp_binding_level* level, tree names,
|
|||
loc = EXPR_LOCATION (b->level_chain->this_entity);
|
||||
saw_cxif = true;
|
||||
}
|
||||
else if (!saw_ceif && level_for_consteval_if (b->level_chain))
|
||||
{
|
||||
inf = G_(" enters %<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)
|
||||
|
|
102
gcc/cp/parser.c
102
gcc/cp/parser.c
|
@ -10902,6 +10902,11 @@ cp_parser_lambda_expression (cp_parser* parser)
|
|||
bool discarded = in_discarded_stmt;
|
||||
in_discarded_stmt = 0;
|
||||
|
||||
/* Similarly the body of a lambda in immediate function context is not
|
||||
in immediate function context. */
|
||||
bool save_in_consteval_if_p = in_consteval_if_p;
|
||||
in_consteval_if_p = false;
|
||||
|
||||
/* By virtue of defining a local class, a lambda expression has access to
|
||||
the private variables of enclosing classes. */
|
||||
|
||||
|
@ -10932,6 +10937,7 @@ cp_parser_lambda_expression (cp_parser* parser)
|
|||
|
||||
finish_struct (type, /*attributes=*/NULL_TREE);
|
||||
|
||||
in_consteval_if_p = save_in_consteval_if_p;
|
||||
in_discarded_stmt = discarded;
|
||||
|
||||
parser->num_template_parameter_lists = saved_num_template_parameter_lists;
|
||||
|
@ -12324,6 +12330,102 @@ cp_parser_selection_statement (cp_parser* parser, bool *if_p,
|
|||
"%<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;
|
||||
|
|
13
gcc/cp/pt.c
13
gcc/cp/pt.c
|
@ -18413,6 +18413,7 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
|
|||
case IF_STMT:
|
||||
stmt = begin_if_stmt ();
|
||||
IF_STMT_CONSTEXPR_P (stmt) = IF_STMT_CONSTEXPR_P (t);
|
||||
IF_STMT_CONSTEVAL_P (stmt) = IF_STMT_CONSTEVAL_P (t);
|
||||
if (IF_STMT_CONSTEXPR_P (t))
|
||||
args = add_extra_args (IF_STMT_EXTRA_ARGS (t), args);
|
||||
tmp = RECUR (IF_COND (t));
|
||||
|
@ -18433,6 +18434,13 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
|
|||
}
|
||||
if (IF_STMT_CONSTEXPR_P (t) && integer_zerop (tmp))
|
||||
/* Don't instantiate the THEN_CLAUSE. */;
|
||||
else if (IF_STMT_CONSTEVAL_P (t))
|
||||
{
|
||||
bool save_in_consteval_if_p = in_consteval_if_p;
|
||||
in_consteval_if_p = true;
|
||||
RECUR (THEN_CLAUSE (t));
|
||||
in_consteval_if_p = save_in_consteval_if_p;
|
||||
}
|
||||
else
|
||||
{
|
||||
tree folded = fold_non_dependent_expr (tmp, complain);
|
||||
|
@ -19385,6 +19393,9 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
|
|||
|
||||
local_specialization_stack s (lss_copy);
|
||||
|
||||
bool save_in_consteval_if_p = in_consteval_if_p;
|
||||
in_consteval_if_p = false;
|
||||
|
||||
tree body = start_lambda_function (fn, r);
|
||||
|
||||
/* Now record them for lookup_init_capture_pack. */
|
||||
|
@ -19425,6 +19436,8 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
|
|||
|
||||
finish_lambda_function (body);
|
||||
|
||||
in_consteval_if_p = save_in_consteval_if_p;
|
||||
|
||||
if (nested)
|
||||
pop_function_context ();
|
||||
else
|
||||
|
|
103
gcc/testsuite/g++.dg/cpp23/consteval-if1.C
Normal file
103
gcc/testsuite/g++.dg/cpp23/consteval-if1.C
Normal file
|
@ -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 ();
|
||||
}
|
36
gcc/testsuite/g++.dg/cpp23/consteval-if10.C
Normal file
36
gcc/testsuite/g++.dg/cpp23/consteval-if10.C
Normal file
|
@ -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);
|
||||
}
|
129
gcc/testsuite/g++.dg/cpp23/consteval-if2.C
Normal file
129
gcc/testsuite/g++.dg/cpp23/consteval-if2.C
Normal file
|
@ -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);
|
||||
}
|
73
gcc/testsuite/g++.dg/cpp23/consteval-if3.C
Normal file
73
gcc/testsuite/g++.dg/cpp23/consteval-if3.C
Normal file
|
@ -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 ();
|
||||
}
|
44
gcc/testsuite/g++.dg/cpp23/consteval-if4.C
Normal file
44
gcc/testsuite/g++.dg/cpp23/consteval-if4.C
Normal file
|
@ -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" }
|
||||
}
|
||||
}
|
14
gcc/testsuite/g++.dg/cpp23/consteval-if5.C
Normal file
14
gcc/testsuite/g++.dg/cpp23/consteval-if5.C
Normal file
|
@ -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" }
|
||||
}
|
||||
}
|
16
gcc/testsuite/g++.dg/cpp23/consteval-if6.C
Normal file
16
gcc/testsuite/g++.dg/cpp23/consteval-if6.C
Normal file
|
@ -0,0 +1,16 @@
|
|||
// { dg-do compile { target c++20 } }
|
||||
// { dg-options "-w" }
|
||||
|
||||
void f()
|
||||
{
|
||||
if consteval
|
||||
{
|
||||
goto l;
|
||||
l:;
|
||||
}
|
||||
else
|
||||
{
|
||||
goto l2;
|
||||
l2:;
|
||||
}
|
||||
}
|
16
gcc/testsuite/g++.dg/cpp23/consteval-if7.C
Normal file
16
gcc/testsuite/g++.dg/cpp23/consteval-if7.C
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
14
gcc/testsuite/g++.dg/cpp23/consteval-if8.C
Normal file
14
gcc/testsuite/g++.dg/cpp23/consteval-if8.C
Normal file
|
@ -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" }
|
||||
}
|
||||
}
|
11
gcc/testsuite/g++.dg/cpp23/consteval-if9.C
Normal file
11
gcc/testsuite/g++.dg/cpp23/consteval-if9.C
Normal file
|
@ -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" }
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue