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  <ja...@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


Reply via email to