public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Jason Merrill <jason@redhat.com>
To: Jakub Jelinek <jakub@redhat.com>
Cc: gcc-patches@gcc.gnu.org, Jonathan Wakely <jwakely@redhat.com>
Subject: Re: [PATCH] c++: Add C++23 consteval if support - P1938R3 [PR100974]
Date: Thu, 10 Jun 2021 10:09:26 -0400	[thread overview]
Message-ID: <a22dc5f8-f67b-9efd-ef30-5205e03a34f3@redhat.com> (raw)
In-Reply-To: <20210610083416.GC7746@tucnak>

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
> 


  parent reply	other threads:[~2021-06-10 14:09 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-06-10  8:34 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 [this message]
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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=a22dc5f8-f67b-9efd-ef30-5205e03a34f3@redhat.com \
    --to=jason@redhat.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=jakub@redhat.com \
    --cc=jwakely@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).