* [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).