public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [C++ PATCH] Ensure constexpr evaluation is done on pre-cp_fold_function bodies (PR c++/89285)
@ 2019-02-18 22:46 Jakub Jelinek
  2019-02-19  2:04 ` Jason Merrill
  0 siblings, 1 reply; 9+ messages in thread
From: Jakub Jelinek @ 2019-02-18 22:46 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches

Hi!

As mentioned in the PR, we've regressed on the trunk in diagnostics of some
invalid constexpr evaluations.  The problem is that the constexpr evaluation
is effectively done on post-cp_fold_function bodies/arguments and cp_fold
optimizes away some important trees for constexpr diagnostics, either
itself, or through using GENERIC match.pd (on the testcase in particular
diagnostics about reinterpret_cast).
While we save on constexpr call hash table bodies of the functions
pre-cp_fold_function, due to sharing and cp_fold_r the STATEMENT_LIST
statements etc. are modified directly and genericization modifies it as
well.

The following patch uses copy_fn which we have been using before the the
recursive constexpr cases also to make a copy of the constexpr function
before cp_fold_function clobbers it.
I had to implement cxx_eval_conditional_expression handling of various
C++ FE statements that are replaced during genericization.

Bootstrapped/regtested on x86_64-linux and i686-linux (98,11,14,17,2a), ok
for trunk?

2019-02-18  Jakub Jelinek  <jakub@redhat.com>

	PR c++/89285
	* constexpr.c (struct constexpr_fundef): Add parms and result members.
	(retrieve_constexpr_fundef): Adjust for the above change.
	(register_constexpr_fundef): Save constexpr body with copy_fn,
	temporarily set DECL_CONTEXT on DECL_RESULT before that.
	(get_fundef_copy): Change FUN argument to FUNDEF with
	constexpr_fundef * type, grab body and parms/result out of
	constexpr_fundef struct and temporarily change it for copy_fn calls
	too.
	(cxx_eval_builtin_function_call): For __builtin_FUNCTION temporarily
	adjust current_function_decl from ctx->call context.  For arith
	overflow builtins, don't test is_constant_expression on the result,
	instead test if arguments are suitable constant expressions.
	(cxx_bind_parameters_in_call): Grab parameters from new_call.  Undo
	convert_for_arg_passing changes for TREE_ADDRESSABLE type passing.
	(cxx_eval_call_expression): Adjust get_fundef_copy caller.
	(cxx_eval_conditional_expression): For IF_STMT, allow then or else
	operands to be NULL.
	(label_matches): Handle BREAK_STMT and CONTINUE_STMT.
	(cxx_eval_loop_expr): Add support for FOR_STMT, WHILE_STMT and DO_STMT.
	(cxx_eval_switch_expr): Add support for SWITCH_STMT.
	(cxx_eval_constant_expression): Handle IF_STMT, FOR_STMT, WHILE_STMT,
	DO_STMT, CONTINUE_STMT, SWITCH_STMT, BREAK_STMT and CONTINUE_STMT.
	For SIZEOF_EXPR, recurse on the result of fold_sizeof_expr.  Ignore
	DECL_EXPR with USING_DECL operand.
	* lambda.c (maybe_add_lambda_conv_op): Build thisarg using
	build_int_cst to make it a valid constant expression.

	* g++.dg/ubsan/vptr-4.C: Expect reinterpret_cast errors.
	* g++.dg/cpp1y/constexpr-84192.C (f2): Adjust expected diagnostics.
	* g++.dg/cpp1y/constexpr-70265-2.C (foo): Adjust expected line of
	diagnostics.
	* g++.dg/cpp1y/constexpr-89285.C: New test.

--- gcc/cp/constexpr.c.jj	2019-02-17 17:09:47.113351897 +0100
+++ gcc/cp/constexpr.c	2019-02-18 19:34:57.995136395 +0100
@@ -139,6 +139,8 @@ ensure_literal_type_for_constexpr_object
 struct GTY((for_user)) constexpr_fundef {
   tree decl;
   tree body;
+  tree parms;
+  tree result;
 };
 
 struct constexpr_fundef_hasher : ggc_ptr_hash<constexpr_fundef>
@@ -176,11 +178,10 @@ constexpr_fundef_hasher::hash (constexpr
 static constexpr_fundef *
 retrieve_constexpr_fundef (tree fun)
 {
-  constexpr_fundef fundef = { NULL, NULL };
   if (constexpr_fundef_table == NULL)
     return NULL;
 
-  fundef.decl = fun;
+  constexpr_fundef fundef = { fun, NULL, NULL, NULL };
   return constexpr_fundef_table->find (&fundef);
 }
 
@@ -897,8 +898,19 @@ register_constexpr_fundef (tree fun, tre
       = hash_table<constexpr_fundef_hasher>::create_ggc (101);
 
   entry.decl = fun;
-  entry.body = body;
+  tree saved_fn = current_function_decl;
+  bool clear_ctx = false;
+  current_function_decl = fun;
+  if (DECL_RESULT (fun) && DECL_CONTEXT (DECL_RESULT (fun)) == NULL_TREE)
+    {
+      clear_ctx = true;
+      DECL_CONTEXT (DECL_RESULT (fun)) = fun;
+    }
+  entry.body = copy_fn (fun, entry.parms, entry.result);
+  current_function_decl = saved_fn;
   slot = constexpr_fundef_table->find_slot (&entry, INSERT);
+  if (clear_ctx)
+    DECL_CONTEXT (DECL_RESULT (fun)) = NULL_TREE;
 
   gcc_assert (*slot == NULL);
   *slot = ggc_alloc<constexpr_fundef> ();
@@ -1114,27 +1126,40 @@ maybe_initialize_fundef_copies_table ()
    is parms, TYPE is result.  */
 
 static tree
-get_fundef_copy (tree fun)
+get_fundef_copy (constexpr_fundef *fundef)
 {
   maybe_initialize_fundef_copies_table ();
 
   tree copy;
   bool existed;
-  tree *slot = &fundef_copies_table->get_or_insert (fun, &existed);
+  tree *slot = &fundef_copies_table->get_or_insert (fundef->decl, &existed);
 
   if (!existed)
     {
       /* There is no cached function available, or in use.  We can use
 	 the function directly.  That the slot is now created records
 	 that this function is now in use.  */
-      copy = build_tree_list (DECL_SAVED_TREE (fun), DECL_ARGUMENTS (fun));
-      TREE_TYPE (copy) = DECL_RESULT (fun);
+      copy = build_tree_list (fundef->body, fundef->parms);
+      TREE_TYPE (copy) = fundef->result;
     }
   else if (*slot == NULL_TREE)
     {
       /* We've already used the function itself, so make a copy.  */
       copy = build_tree_list (NULL, NULL);
-      TREE_PURPOSE (copy) = copy_fn (fun, TREE_VALUE (copy), TREE_TYPE (copy));
+      tree saved_body = DECL_SAVED_TREE (fundef->decl);
+      tree saved_parms = DECL_ARGUMENTS (fundef->decl);
+      tree saved_result = DECL_RESULT (fundef->decl);
+      tree saved_fn = current_function_decl;
+      DECL_SAVED_TREE (fundef->decl) = fundef->body;
+      DECL_ARGUMENTS (fundef->decl) = fundef->parms;
+      DECL_RESULT (fundef->decl) = fundef->result;
+      current_function_decl = fundef->decl;
+      TREE_PURPOSE (copy) = copy_fn (fundef->decl, TREE_VALUE (copy),
+				     TREE_TYPE (copy));
+      current_function_decl = saved_fn;
+      DECL_RESULT (fundef->decl) = saved_result;
+      DECL_ARGUMENTS (fundef->decl) = saved_parms;
+      DECL_SAVED_TREE (fundef->decl) = saved_body;
     }
   else
     {
@@ -1245,8 +1270,15 @@ cxx_eval_builtin_function_call (const co
 
   bool save_ffbcp = force_folding_builtin_constant_p;
   force_folding_builtin_constant_p = true;
+  tree save_cur_fn = current_function_decl;
+  /* Return name of ctx->call->fundef->decl for __builtin_FUNCTION ().  */
+  if (fndecl_built_in_p (fun, BUILT_IN_FUNCTION)
+      && ctx->call
+      && ctx->call->fundef)
+    current_function_decl = ctx->call->fundef->decl;
   new_call = fold_builtin_call_array (EXPR_LOCATION (t), TREE_TYPE (t),
 				      CALL_EXPR_FN (t), nargs, args);
+  current_function_decl = save_cur_fn;
   force_folding_builtin_constant_p = save_ffbcp;
   if (new_call == NULL)
     {
@@ -1269,6 +1301,49 @@ cxx_eval_builtin_function_call (const co
       return t;
     }
 
+  if (fndecl_built_in_p (fun, BUILT_IN_NORMAL))
+    switch (DECL_FUNCTION_CODE (fun))
+      {
+      case BUILT_IN_ADD_OVERFLOW:
+      case BUILT_IN_SADD_OVERFLOW:
+      case BUILT_IN_SADDL_OVERFLOW:
+      case BUILT_IN_SADDLL_OVERFLOW:
+      case BUILT_IN_UADD_OVERFLOW:
+      case BUILT_IN_UADDL_OVERFLOW:
+      case BUILT_IN_UADDLL_OVERFLOW:
+      case BUILT_IN_SUB_OVERFLOW:
+      case BUILT_IN_SSUB_OVERFLOW:
+      case BUILT_IN_SSUBL_OVERFLOW:
+      case BUILT_IN_SSUBLL_OVERFLOW:
+      case BUILT_IN_USUB_OVERFLOW:
+      case BUILT_IN_USUBL_OVERFLOW:
+      case BUILT_IN_USUBLL_OVERFLOW:
+      case BUILT_IN_MUL_OVERFLOW:
+      case BUILT_IN_SMUL_OVERFLOW:
+      case BUILT_IN_SMULL_OVERFLOW:
+      case BUILT_IN_SMULLL_OVERFLOW:
+      case BUILT_IN_UMUL_OVERFLOW:
+      case BUILT_IN_UMULL_OVERFLOW:
+      case BUILT_IN_UMULLL_OVERFLOW:
+	/* These builtins will fold into
+	   (cast)
+	     ((something = __real__ SAVE_EXPR <.???_OVERFLOW (cst1, cst2)>),
+	      __imag__ SAVE_EXPR <.???_OVERFLOW (cst1, cst2)>)
+	   which fails is_constant_expression.  */
+	if (TREE_CODE (args[0]) != INTEGER_CST
+	    || TREE_CODE (args[1]) != INTEGER_CST
+	    || !potential_constant_expression (args[2]))
+	  {
+	    if (!*non_constant_p && !ctx->quiet)
+	      error ("%q+E is not a constant expression", new_call);
+	    *non_constant_p = true;
+	    return t;
+	  }
+	return cxx_eval_constant_expression (&new_ctx, new_call, lval,
+					     non_constant_p, overflow_p);
+      default:
+	break;
+      }
   if (!is_constant_expression (new_call))
     {
       if (!*non_constant_p && !ctx->quiet)
@@ -1341,7 +1416,7 @@ cxx_bind_parameters_in_call (const const
 {
   const int nargs = call_expr_nargs (t);
   tree fun = new_call->fundef->decl;
-  tree parms = DECL_ARGUMENTS (fun);
+  tree parms = new_call->fundef->parms;
   int i;
   tree *p = &new_call->bindings;
   for (i = 0; i < nargs; ++i)
@@ -1358,6 +1433,9 @@ cxx_bind_parameters_in_call (const const
 	  x = ctx->object;
 	  x = build_address (x);
 	}
+      if (TREE_ADDRESSABLE (type) && TYPE_REF_P (TREE_TYPE (x)))
+	/* Undo convert_for_arg_passing work here.  */
+	x = build_fold_indirect_ref_loc (EXPR_LOCATION (x), x);
       arg = cxx_eval_constant_expression (ctx, x, /*lval=*/false,
 					  non_constant_p, overflow_p);
       /* Don't VERIFY_CONSTANT here.  */
@@ -1761,7 +1839,7 @@ cxx_eval_call_expression (const constexp
 	  tree body, parms, res;
 
 	  /* Reuse or create a new unshared copy of this function's body.  */
-	  tree copy = get_fundef_copy (fun);
+	  tree copy = get_fundef_copy (new_call.fundef);
 	  body = TREE_PURPOSE (copy);
 	  parms = TREE_VALUE (copy);
 	  res = TREE_TYPE (copy);
@@ -2223,14 +2301,13 @@ cxx_eval_conditional_expression (const c
   VERIFY_CONSTANT (val);
   /* Don't VERIFY_CONSTANT the other operands.  */
   if (integer_zerop (val))
-    return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 2),
-					 lval,
-					 non_constant_p, overflow_p,
-					 jump_target);
-  return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
-				       lval,
-				       non_constant_p, overflow_p,
-				       jump_target);
+    val = TREE_OPERAND (t, 2);
+  else
+    val = TREE_OPERAND (t, 1);
+  if (TREE_CODE (t) == IF_STMT && !val)
+    val = void_node;
+  return cxx_eval_constant_expression (ctx, val, lval, non_constant_p,
+				       overflow_p, jump_target);
 }
 
 /* Subroutine of cxx_eval_constant_expression.
@@ -4036,6 +4113,10 @@ label_matches (const constexpr_ctx *ctx,
 	}
       break;
 
+    case BREAK_STMT:
+    case CONTINUE_STMT:
+      break;
+
     default:
       gcc_unreachable ();
     }
@@ -4096,20 +4177,84 @@ cxx_eval_loop_expr (const constexpr_ctx
 {
   constexpr_ctx new_ctx = *ctx;
 
-  tree body = TREE_OPERAND (t, 0);
+  tree body, cond = NULL_TREE, expr = NULL_TREE;
   int count = 0;
+  switch (TREE_CODE (t))
+    {
+    case LOOP_EXPR:
+      body = LOOP_EXPR_BODY (t);
+      break;
+    case DO_STMT:
+      body = DO_BODY (t);
+      cond = DO_COND (t);
+      break;
+    case WHILE_STMT:
+      body = WHILE_BODY (t);
+      cond = WHILE_COND (t);
+      count = -1;
+      break;
+    case FOR_STMT:
+      if (FOR_INIT_STMT (t))
+	cxx_eval_constant_expression (ctx, FOR_INIT_STMT (t), /*lval*/false,
+				      non_constant_p, overflow_p, jump_target);
+      if (*non_constant_p)
+	return NULL_TREE;
+      body = FOR_BODY (t);
+      cond = FOR_COND (t);
+      expr = FOR_EXPR (t);
+      count = -1;
+      break;
+    default:
+      gcc_unreachable ();
+    }
+  hash_set<tree> save_exprs;
+  new_ctx.save_exprs = &save_exprs;
   do
     {
-      hash_set<tree> save_exprs;
-      new_ctx.save_exprs = &save_exprs;
+      if (count != -1)
+	{
+	  if (body)
+	    cxx_eval_constant_expression (&new_ctx, body, /*lval*/false,
+					  non_constant_p, overflow_p,
+					  jump_target);
+	  if (breaks (jump_target))
+	    {
+	      *jump_target = NULL_TREE;
+	      break;
+	    }
 
-      cxx_eval_constant_expression (&new_ctx, body, /*lval*/false,
-				    non_constant_p, overflow_p, jump_target);
+	  if (TREE_CODE (t) != LOOP_EXPR && continues (jump_target))
+	    *jump_target = NULL_TREE;
+
+	  if (expr)
+	    cxx_eval_constant_expression (&new_ctx, expr, /*lval*/false,
+					  non_constant_p, overflow_p,
+					  jump_target);
+	}
+
+      if (cond)
+	{
+	  tree res
+	    = cxx_eval_constant_expression (&new_ctx, cond, /*lval*/false,
+					    non_constant_p, overflow_p,
+					    jump_target);
+	  if (res)
+	    {
+	      if (verify_constant (res, ctx->quiet, non_constant_p,
+				   overflow_p))
+		break;
+	      if (integer_zerop (res))
+		break;
+	    }
+	  else
+	    gcc_assert (*jump_target);
+	}
 
       /* Forget saved values of SAVE_EXPRs.  */
       for (hash_set<tree>::iterator iter = save_exprs.begin();
 	   iter != save_exprs.end(); ++iter)
 	new_ctx.values->remove (*iter);
+
       if (++count >= constexpr_loop_limit)
 	{
 	  if (!ctx->quiet)
@@ -4123,11 +4268,14 @@ cxx_eval_loop_expr (const constexpr_ctx
     }
   while (!returns (jump_target)
 	 && !breaks (jump_target)
-	 && !switches (jump_target)
+	 && !continues (jump_target)
+	 && (!switches (jump_target) || count == 0)
 	 && !*non_constant_p);
 
-  if (breaks (jump_target))
-    *jump_target = NULL_TREE;
+  /* Forget saved values of SAVE_EXPRs.  */
+  for (hash_set<tree>::iterator iter = save_exprs.begin();
+       iter != save_exprs.end(); ++iter)
+    new_ctx.values->remove (*iter);
 
   return NULL_TREE;
 }
@@ -4140,13 +4288,15 @@ cxx_eval_switch_expr (const constexpr_ct
 		      bool *non_constant_p, bool *overflow_p,
 		      tree *jump_target)
 {
-  tree cond = SWITCH_COND (t);
+  tree cond
+    = TREE_CODE (t) == SWITCH_STMT ? SWITCH_STMT_COND (t) : SWITCH_COND (t);
   cond = cxx_eval_constant_expression (ctx, cond, false,
 				       non_constant_p, overflow_p);
   VERIFY_CONSTANT (cond);
   *jump_target = cond;
 
-  tree body = SWITCH_BODY (t);
+  tree body
+    = TREE_CODE (t) == SWITCH_STMT ? SWITCH_STMT_BODY (t) : SWITCH_BODY (t);
   constexpr_ctx new_ctx = *ctx;
   constexpr_switch_state css = css_default_not_seen;
   new_ctx.css_state = &css;
@@ -4222,6 +4372,10 @@ cxx_eval_constant_expression (const cons
 	case STATEMENT_LIST:
 	case LOOP_EXPR:
 	case COND_EXPR:
+	case IF_STMT:
+	case DO_STMT:
+	case WHILE_STMT:
+	case FOR_STMT:
 	  break;
 	case LABEL_EXPR:
 	case CASE_LABEL_EXPR:
@@ -4366,6 +4520,11 @@ cxx_eval_constant_expression (const cons
     case DECL_EXPR:
       {
 	r = DECL_EXPR_DECL (t);
+	if (TREE_CODE (r) == USING_DECL)
+	  {
+	    r = void_node;
+	    break;
+	  }
 	if (AGGREGATE_TYPE_P (TREE_TYPE (r))
 	    || VECTOR_TYPE_P (TREE_TYPE (r)))
 	  {
@@ -4453,6 +4612,9 @@ cxx_eval_constant_expression (const cons
 	r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
 					  lval,
 					  non_constant_p, overflow_p);
+      /* FALLTHRU */
+    case BREAK_STMT:
+    case CONTINUE_STMT:
       if (jump_target)
 	*jump_target = t;
       else
@@ -4503,6 +4665,17 @@ cxx_eval_constant_expression (const cons
 				      jump_target);
       break;
 
+    case CLEANUP_STMT:
+      r = cxx_eval_constant_expression (ctx, CLEANUP_BODY (t), lval,
+					non_constant_p, overflow_p,
+					jump_target);
+      if (!CLEANUP_EH_ONLY (t) && !*non_constant_p)
+	/* Also evaluate the cleanup.  */
+	cxx_eval_constant_expression (ctx, CLEANUP_EXPR (t), true,
+				      non_constant_p, overflow_p,
+				      jump_target);
+      break;
+
       /* These differ from cxx_eval_unary_expression in that this doesn't
 	 check for a constant operand or result; an address can be
 	 constant without its operand being, and vice versa.  */
@@ -4558,8 +4731,9 @@ cxx_eval_constant_expression (const cons
       break;
 
     case SIZEOF_EXPR:
-      r = fold_sizeof_expr (t);
-      VERIFY_CONSTANT (r);
+      r = cxx_eval_constant_expression (ctx, fold_sizeof_expr (t), lval,
+					non_constant_p, overflow_p,
+					jump_target);
       break;
 
     case COMPOUND_EXPR:
@@ -4679,16 +4853,18 @@ cxx_eval_constant_expression (const cons
       break;
 
     case COND_EXPR:
+    case IF_STMT:
       if (jump_target && *jump_target)
 	{
 	  tree orig_jump = *jump_target;
+	  tree arg = ((TREE_CODE (t) != IF_STMT || TREE_OPERAND (t, 1))
+		      ? TREE_OPERAND (t, 1) : void_node);
 	  /* When jumping to a label, the label might be either in the
 	     then or else blocks, so process then block first in skipping
 	     mode first, and if we are still in the skipping mode at its end,
 	     process the else block too.  */
-	  r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
-					    lval, non_constant_p, overflow_p,
-					    jump_target);
+	  r = cxx_eval_constant_expression (ctx, arg, lval, non_constant_p,
+					    overflow_p, jump_target);
 	  /* It's possible that we found the label in the then block.  But
 	     it could have been followed by another jumping statement, e.g.
 	     say we're looking for case 1:
@@ -4702,9 +4878,12 @@ cxx_eval_constant_expression (const cons
 	     in which case we need not go looking to the else block.
 	     (goto is not allowed in a constexpr function.)  */
 	  if (*jump_target == orig_jump)
-	    r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 2),
-					      lval, non_constant_p, overflow_p,
-					      jump_target);
+	    {
+	      arg = ((TREE_CODE (t) != IF_STMT || TREE_OPERAND (t, 2))
+		     ? TREE_OPERAND (t, 2) : void_node);
+	      r = cxx_eval_constant_expression (ctx, arg, lval, non_constant_p,
+						overflow_p, jump_target);
+	    }
 	  break;
 	}
       r = cxx_eval_conditional_expression (ctx, t, lval,
@@ -4948,11 +5127,15 @@ cxx_eval_constant_expression (const cons
       break;
 
     case LOOP_EXPR:
+    case DO_STMT:
+    case WHILE_STMT:
+    case FOR_STMT:
       cxx_eval_loop_expr (ctx, t,
 			  non_constant_p, overflow_p, jump_target);
       break;
 
     case SWITCH_EXPR:
+    case SWITCH_STMT:
       cxx_eval_switch_expr (ctx, t,
 			    non_constant_p, overflow_p, jump_target);
       break;
--- gcc/cp/lambda.c.jj	2019-02-05 23:27:42.246248060 +0100
+++ gcc/cp/lambda.c	2019-02-18 11:12:37.168026551 +0100
@@ -1086,8 +1086,7 @@ maybe_add_lambda_conv_op (tree type)
   tree optype = TREE_TYPE (callop);
   tree fn_result = TREE_TYPE (optype);
 
-  tree thisarg = build_nop (TREE_TYPE (DECL_ARGUMENTS (callop)),
-			    null_pointer_node);
+  tree thisarg = build_int_cst (TREE_TYPE (DECL_ARGUMENTS (callop)), 0);
   if (generic_lambda_p)
     {
       ++processing_template_decl;
--- gcc/testsuite/g++.dg/ubsan/vptr-4.C.jj	2018-04-19 15:57:36.816482603 +0200
+++ gcc/testsuite/g++.dg/ubsan/vptr-4.C	2019-02-18 19:03:46.906819794 +0100
@@ -15,11 +15,11 @@ struct T : S {
   int b;
   int g() { return 0; }
   virtual int v() { return 1; }
-  constexpr const T *foo() { return (const T *) reinterpret_cast<const S *> (this); }
+  constexpr const T *foo() { return (const T *) reinterpret_cast<const S *> (this); } // { dg-error "is not a constant expression" }
 };
 
 constexpr T t;
-constexpr const T *p = t.foo ();
+constexpr const T *p = t.foo ();	// { dg-message "expansion of" }
 
 template <typename U>
 struct V {
@@ -39,16 +39,17 @@ struct W : V<U> {
 };
 
 constexpr W<int> w;
-constexpr const W<int> *s = w.foo ();
+constexpr const W<int> *s = w.foo ();	// { dg-error "is not a constant expression" }
+// { dg-message "expansion of" "" { target *-*-* } .-1 }
 
 template <typename U>
 int foo (void)
 {
   static constexpr T t;
-  static constexpr const T *p = t.foo ();
+  static constexpr const T *p = t.foo ();	// { dg-message "expansion of" }
   static constexpr W<U> w;
-  static constexpr const W<U> *s = w.foo ();
-  return t.b + w.b;
+  static constexpr const W<U> *s = w.foo ();	// { dg-error "is not a constant expression" }
+  return t.b + w.b;				// { dg-message "expansion of" "" { target *-*-* } .-1 }
 }
 
 int x = foo <char> ();
--- gcc/testsuite/g++.dg/cpp1y/constexpr-84192.C.jj	2018-02-16 23:38:05.140373372 +0100
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-84192.C	2019-02-18 15:21:16.844662250 +0100
@@ -12,7 +12,7 @@ void
 f2 ()
 { 
   for (;;)
-    constexpr bool b = ({ break; false; }) && false;	// { dg-error "statement is not a constant expression" }
+    constexpr bool b = ({ break; false; }) && false;	// { dg-error "is not a constant expression" }
 }
 
 constexpr bool
--- gcc/testsuite/g++.dg/cpp1y/constexpr-70265-2.C.jj	2017-11-21 20:22:58.977029063 +0100
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-70265-2.C	2019-02-18 10:37:03.052110091 +0100
@@ -5,8 +5,8 @@ constexpr int
 foo (int p)
 {
   int t = 0;
-  while (1)
-    t = 0;  // { dg-error "count exceeds" }
+  while (1)  // { dg-error "count exceeds" }
+    t = 0;
   return t;
 }
 
--- gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C.jj	2019-02-17 17:25:04.724339082 +0100
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C	2019-02-18 17:43:23.550885072 +0100
@@ -0,0 +1,20 @@
+// PR c++/89285
+// { dg-do compile { target c++14 } }
+
+struct A {
+  int a {};
+};
+struct B {
+  int b {};
+  constexpr B (A *x) {
+    int *c = &x->a;
+    while (*c)
+      c = reinterpret_cast<int *>((reinterpret_cast<char *>(c) + *c));
+    *c = reinterpret_cast<char *>(this) - reinterpret_cast<char *>(c);	// { dg-error "reinterpret_cast" }
+  }
+};
+struct C : A {
+  B bar {this};
+};
+
+constexpr C foo {};	// { dg-message "expansion of" }

	Jakub

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [C++ PATCH] Ensure constexpr evaluation is done on pre-cp_fold_function bodies (PR c++/89285)
  2019-02-18 22:46 [C++ PATCH] Ensure constexpr evaluation is done on pre-cp_fold_function bodies (PR c++/89285) Jakub Jelinek
@ 2019-02-19  2:04 ` Jason Merrill
  2019-02-19  4:31   ` Jakub Jelinek
  0 siblings, 1 reply; 9+ messages in thread
From: Jason Merrill @ 2019-02-19  2:04 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 2/18/19 12:45 PM, Jakub Jelinek wrote:
> Hi!
> 
> As mentioned in the PR, we've regressed on the trunk in diagnostics of some
> invalid constexpr evaluations.  The problem is that the constexpr evaluation
> is effectively done on post-cp_fold_function bodies/arguments and cp_fold
> optimizes away some important trees for constexpr diagnostics, either
> itself, or through using GENERIC match.pd (on the testcase in particular
> diagnostics about reinterpret_cast).
> While we save on constexpr call hash table bodies of the functions
> pre-cp_fold_function, due to sharing and cp_fold_r the STATEMENT_LIST
> statements etc. are modified directly and genericization modifies it as
> well.
> 
> The following patch uses copy_fn which we have been using before the the
> recursive constexpr cases also to make a copy of the constexpr function
> before cp_fold_function clobbers it.
> I had to implement cxx_eval_conditional_expression handling of various
> C++ FE statements that are replaced during genericization.
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux (98,11,14,17,2a), ok
> for trunk?
> 
> 2019-02-18  Jakub Jelinek  <jakub@redhat.com>
> 
> 	PR c++/89285
> 	* constexpr.c (struct constexpr_fundef): Add parms and result members.
> 	(retrieve_constexpr_fundef): Adjust for the above change.
> 	(register_constexpr_fundef): Save constexpr body with copy_fn,
> 	temporarily set DECL_CONTEXT on DECL_RESULT before that.
> 	(get_fundef_copy): Change FUN argument to FUNDEF with
> 	constexpr_fundef * type, grab body and parms/result out of
> 	constexpr_fundef struct and temporarily change it for copy_fn calls
> 	too.
> 	(cxx_eval_builtin_function_call): For __builtin_FUNCTION temporarily
> 	adjust current_function_decl from ctx->call context.  For arith
> 	overflow builtins, don't test is_constant_expression on the result,
> 	instead test if arguments are suitable constant expressions.
> 	(cxx_bind_parameters_in_call): Grab parameters from new_call.  Undo
> 	convert_for_arg_passing changes for TREE_ADDRESSABLE type passing.
> 	(cxx_eval_call_expression): Adjust get_fundef_copy caller.
> 	(cxx_eval_conditional_expression): For IF_STMT, allow then or else
> 	operands to be NULL.
> 	(label_matches): Handle BREAK_STMT and CONTINUE_STMT.
> 	(cxx_eval_loop_expr): Add support for FOR_STMT, WHILE_STMT and DO_STMT.
> 	(cxx_eval_switch_expr): Add support for SWITCH_STMT.
> 	(cxx_eval_constant_expression): Handle IF_STMT, FOR_STMT, WHILE_STMT,
> 	DO_STMT, CONTINUE_STMT, SWITCH_STMT, BREAK_STMT and CONTINUE_STMT.
> 	For SIZEOF_EXPR, recurse on the result of fold_sizeof_expr.  Ignore
> 	DECL_EXPR with USING_DECL operand.
> 	* lambda.c (maybe_add_lambda_conv_op): Build thisarg using
> 	build_int_cst to make it a valid constant expression.
> 
> 	* g++.dg/ubsan/vptr-4.C: Expect reinterpret_cast errors.
> 	* g++.dg/cpp1y/constexpr-84192.C (f2): Adjust expected diagnostics.
> 	* g++.dg/cpp1y/constexpr-70265-2.C (foo): Adjust expected line of
> 	diagnostics.
> 	* g++.dg/cpp1y/constexpr-89285.C: New test.
> 
> --- gcc/cp/constexpr.c.jj	2019-02-17 17:09:47.113351897 +0100
> +++ gcc/cp/constexpr.c	2019-02-18 19:34:57.995136395 +0100
> @@ -1269,6 +1301,49 @@ cxx_eval_builtin_function_call (const co
>         return t;
>       }
>   
> +  if (fndecl_built_in_p (fun, BUILT_IN_NORMAL))
> +    switch (DECL_FUNCTION_CODE (fun))
> +      {
> +      case BUILT_IN_ADD_OVERFLOW:
> +      case BUILT_IN_SADD_OVERFLOW:
> +      case BUILT_IN_SADDL_OVERFLOW:
> +      case BUILT_IN_SADDLL_OVERFLOW:
> +      case BUILT_IN_UADD_OVERFLOW:
> +      case BUILT_IN_UADDL_OVERFLOW:
> +      case BUILT_IN_UADDLL_OVERFLOW:
> +      case BUILT_IN_SUB_OVERFLOW:
> +      case BUILT_IN_SSUB_OVERFLOW:
> +      case BUILT_IN_SSUBL_OVERFLOW:
> +      case BUILT_IN_SSUBLL_OVERFLOW:
> +      case BUILT_IN_USUB_OVERFLOW:
> +      case BUILT_IN_USUBL_OVERFLOW:
> +      case BUILT_IN_USUBLL_OVERFLOW:
> +      case BUILT_IN_MUL_OVERFLOW:
> +      case BUILT_IN_SMUL_OVERFLOW:
> +      case BUILT_IN_SMULL_OVERFLOW:
> +      case BUILT_IN_SMULLL_OVERFLOW:
> +      case BUILT_IN_UMUL_OVERFLOW:
> +      case BUILT_IN_UMULL_OVERFLOW:
> +      case BUILT_IN_UMULLL_OVERFLOW:
> +	/* These builtins will fold into
> +	   (cast)
> +	     ((something = __real__ SAVE_EXPR <.???_OVERFLOW (cst1, cst2)>),
> +	      __imag__ SAVE_EXPR <.???_OVERFLOW (cst1, cst2)>)
> +	   which fails is_constant_expression.  */
> +	if (TREE_CODE (args[0]) != INTEGER_CST
> +	    || TREE_CODE (args[1]) != INTEGER_CST
> +	    || !potential_constant_expression (args[2]))
> +	  {
> +	    if (!*non_constant_p && !ctx->quiet)
> +	      error ("%q+E is not a constant expression", new_call);
> +	    *non_constant_p = true;
> +	    return t;
> +	  }
> +	return cxx_eval_constant_expression (&new_ctx, new_call, lval,
> +					     non_constant_p, overflow_p);
> +      default:
> +	break;
> +      }

What is this for?  Won't this recursive cxx_eval_constant_expression 
come back to this function again?  If the expression is constant, 
shouldn't it have been folded by fold_builtin_call_array?

> @@ -1358,6 +1433,9 @@ cxx_bind_parameters_in_call (const const
>   	  x = ctx->object;
>   	  x = build_address (x);
>   	}
> +      if (TREE_ADDRESSABLE (type) && TYPE_REF_P (TREE_TYPE (x)))
> +	/* Undo convert_for_arg_passing work here.  */
> +	x = build_fold_indirect_ref_loc (EXPR_LOCATION (x), x);

Not convert_from_reference?

> @@ -4036,6 +4113,10 @@ label_matches (const constexpr_ctx *ctx,
>   	}
>         break;
>   
> +    case BREAK_STMT:
> +    case CONTINUE_STMT:
> +      break;
> +

Let's add a comment that these are handled directly in cxx_eval_loop_expr.

Jason

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [C++ PATCH] Ensure constexpr evaluation is done on pre-cp_fold_function bodies (PR c++/89285)
  2019-02-19  2:04 ` Jason Merrill
@ 2019-02-19  4:31   ` Jakub Jelinek
  2019-02-20  7:46     ` [C++ PATCH] Ensure constexpr evaluation is done on pre-cp_fold_function bodies (PR c++/89285, take 2) Jakub Jelinek
  2019-02-20 21:22     ` [C++ PATCH] Ensure constexpr evaluation is done on pre-cp_fold_function bodies (PR c++/89285) Jason Merrill
  0 siblings, 2 replies; 9+ messages in thread
From: Jakub Jelinek @ 2019-02-19  4:31 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches

On Mon, Feb 18, 2019 at 04:04:15PM -1000, Jason Merrill wrote:
> > --- gcc/cp/constexpr.c.jj	2019-02-17 17:09:47.113351897 +0100
> > +++ gcc/cp/constexpr.c	2019-02-18 19:34:57.995136395 +0100
> > @@ -1269,6 +1301,49 @@ cxx_eval_builtin_function_call (const co
> >         return t;
> >       }
> > +  if (fndecl_built_in_p (fun, BUILT_IN_NORMAL))
> > +    switch (DECL_FUNCTION_CODE (fun))
> > +      {
> > +      case BUILT_IN_ADD_OVERFLOW:
> > +      case BUILT_IN_SADD_OVERFLOW:
> > +      case BUILT_IN_SADDL_OVERFLOW:
> > +      case BUILT_IN_SADDLL_OVERFLOW:
> > +      case BUILT_IN_UADD_OVERFLOW:
> > +      case BUILT_IN_UADDL_OVERFLOW:
> > +      case BUILT_IN_UADDLL_OVERFLOW:
> > +      case BUILT_IN_SUB_OVERFLOW:
> > +      case BUILT_IN_SSUB_OVERFLOW:
> > +      case BUILT_IN_SSUBL_OVERFLOW:
> > +      case BUILT_IN_SSUBLL_OVERFLOW:
> > +      case BUILT_IN_USUB_OVERFLOW:
> > +      case BUILT_IN_USUBL_OVERFLOW:
> > +      case BUILT_IN_USUBLL_OVERFLOW:
> > +      case BUILT_IN_MUL_OVERFLOW:
> > +      case BUILT_IN_SMUL_OVERFLOW:
> > +      case BUILT_IN_SMULL_OVERFLOW:
> > +      case BUILT_IN_SMULLL_OVERFLOW:
> > +      case BUILT_IN_UMUL_OVERFLOW:
> > +      case BUILT_IN_UMULL_OVERFLOW:
> > +      case BUILT_IN_UMULLL_OVERFLOW:
> > +	/* These builtins will fold into
> > +	   (cast)
> > +	     ((something = __real__ SAVE_EXPR <.???_OVERFLOW (cst1, cst2)>),
> > +	      __imag__ SAVE_EXPR <.???_OVERFLOW (cst1, cst2)>)
> > +	   which fails is_constant_expression.  */
> > +	if (TREE_CODE (args[0]) != INTEGER_CST
> > +	    || TREE_CODE (args[1]) != INTEGER_CST
> > +	    || !potential_constant_expression (args[2]))
> > +	  {
> > +	    if (!*non_constant_p && !ctx->quiet)
> > +	      error ("%q+E is not a constant expression", new_call);
> > +	    *non_constant_p = true;
> > +	    return t;
> > +	  }
> > +	return cxx_eval_constant_expression (&new_ctx, new_call, lval,
> > +					     non_constant_p, overflow_p);
> > +      default:
> > +	break;
> > +      }
> 
> What is this for?  Won't this recursive cxx_eval_constant_expression come
> back to this function again?  If the expression is constant, shouldn't it
> have been folded by fold_builtin_call_array?

This is for the constexpr-arith-overflow.C testcase.
The arguments are INTEGER_CST, INTEGER_CST and ADDR_EXPR of a VAR_DECL or
PARM_DECL, and fold_builtin_call_array returns new_call:
(z = REALPART_EXPR <SAVE_EXPR <.ADD_OVERFLOW (0, 0)>>;, (bool) IMAGPART_EXPR <SAVE_EXPR <.ADD_OVERFLOW (0, 0)>>;);
where this doesn't pass is_constant_expression because of the z store.
cxx_eval_constant_expression is able to evaluate this, as
z = 0;
false;
in this case.
I guess builtins.c folding could be improved and simplify it to
(z = 0; (bool) false;);
but that still doesn't pass is_constant_expression check.
For C++14 it passes potential_constant_expression though, and that
is what I've used for these builtins in the first iteration, but
the testcase happened to pass even for C++11 and
potential_constant_expression is false here.  Though, perhaps we are going
too far for C++11 here and should reject it, after all, people have the
possibility to use __builtin_*_overflow_p now which should be usable even in
C++11.  The reason why it passed with C++11 is that when parsing we saw
a __builtin_add_overflow (0, 0, &z) call and potential_constant_expression
said it is ok, then folded it into that
(z = REALPART_EXPR <SAVE_EXPR <.ADD_OVERFLOW (0, 0)>>;, (bool) IMAGPART_EXPR <SAVE_EXPR <.ADD_OVERFLOW (0, 0)>>;);
which is not potential_constant_expression, but nothing called it again
and cxx_eval_constant_expression can handle it.

> > @@ -1358,6 +1433,9 @@ cxx_bind_parameters_in_call (const const
> >   	  x = ctx->object;
> >   	  x = build_address (x);
> >   	}
> > +      if (TREE_ADDRESSABLE (type) && TYPE_REF_P (TREE_TYPE (x)))
> > +	/* Undo convert_for_arg_passing work here.  */
> > +	x = build_fold_indirect_ref_loc (EXPR_LOCATION (x), x);
> 
> Not convert_from_reference?

Will change.

> > @@ -4036,6 +4113,10 @@ label_matches (const constexpr_ctx *ctx,
> >   	}
> >         break;
> > +    case BREAK_STMT:
> > +    case CONTINUE_STMT:
> > +      break;
> > +
> 
> Let's add a comment that these are handled directly in cxx_eval_loop_expr.

Ok, will do.

	Jakub

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [C++ PATCH] Ensure constexpr evaluation is done on pre-cp_fold_function bodies (PR c++/89285, take 2)
  2019-02-19  4:31   ` Jakub Jelinek
@ 2019-02-20  7:46     ` Jakub Jelinek
  2019-02-20 21:22     ` [C++ PATCH] Ensure constexpr evaluation is done on pre-cp_fold_function bodies (PR c++/89285) Jason Merrill
  1 sibling, 0 replies; 9+ messages in thread
From: Jakub Jelinek @ 2019-02-20  7:46 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches

Hi!

On Tue, Feb 19, 2019 at 05:31:28AM +0100, Jakub Jelinek wrote:
> > > +	/* These builtins will fold into
> > > +	   (cast)
> > > +	     ((something = __real__ SAVE_EXPR <.???_OVERFLOW (cst1, cst2)>),
> > > +	      __imag__ SAVE_EXPR <.???_OVERFLOW (cst1, cst2)>)
> > > +	   which fails is_constant_expression.  */

This part is now simplified by builtins.c change, so it is actually just
(something = cst1), cst2
but still the !is_constant_expression issue applies.
> > > @@ -1358,6 +1433,9 @@ cxx_bind_parameters_in_call (const const
> > >   	  x = ctx->object;
> > >   	  x = build_address (x);
> > >   	}
> > > +      if (TREE_ADDRESSABLE (type) && TYPE_REF_P (TREE_TYPE (x)))
> > > +	/* Undo convert_for_arg_passing work here.  */
> > > +	x = build_fold_indirect_ref_loc (EXPR_LOCATION (x), x);
> > 
> > Not convert_from_reference?
> 
> Will change.

Changed.

> > > @@ -4036,6 +4113,10 @@ label_matches (const constexpr_ctx *ctx,
> > >   	}
> > >         break;
> > > +    case BREAK_STMT:
> > > +    case CONTINUE_STMT:
> > > +      break;
> > > +
> > 
> > Let's add a comment that these are handled directly in cxx_eval_loop_expr.
> 
> Ok, will do.

Likewise.

Here is an updated version with those changes, bootstrapped/regtested on
x86_64-linux/i686-linux.

2019-02-20  Jakub Jelinek  <jakub@redhat.com>

	PR c++/89285
	* builtins.c (fold_builtin_arith_overflow): If first two args are
	INTEGER_CSTs, set intres and ovfres to constants rather than calls
	to ifn.

	* constexpr.c (struct constexpr_fundef): Add parms and result members.
	(retrieve_constexpr_fundef): Adjust for the above change.
	(register_constexpr_fundef): Save constexpr body with copy_fn,
	temporarily set DECL_CONTEXT on DECL_RESULT before that.
	(get_fundef_copy): Change FUN argument to FUNDEF with
	constexpr_fundef * type, grab body and parms/result out of
	constexpr_fundef struct and temporarily change it for copy_fn calls
	too.
	(cxx_eval_builtin_function_call): For __builtin_FUNCTION temporarily
	adjust current_function_decl from ctx->call context.  For arith
	overflow builtins, don't test is_constant_expression on the result,
	instead test if arguments are suitable constant expressions.
	(cxx_bind_parameters_in_call): Grab parameters from new_call.  Undo
	convert_for_arg_passing changes for TREE_ADDRESSABLE type passing.
	(cxx_eval_call_expression): Adjust get_fundef_copy caller.
	(cxx_eval_conditional_expression): For IF_STMT, allow then or else
	operands to be NULL.
	(label_matches): Handle BREAK_STMT and CONTINUE_STMT.
	(cxx_eval_loop_expr): Add support for FOR_STMT, WHILE_STMT and DO_STMT.
	(cxx_eval_switch_expr): Add support for SWITCH_STMT.
	(cxx_eval_constant_expression): Handle IF_STMT, FOR_STMT, WHILE_STMT,
	DO_STMT, CONTINUE_STMT, SWITCH_STMT, BREAK_STMT and CONTINUE_STMT.
	For SIZEOF_EXPR, recurse on the result of fold_sizeof_expr.  Ignore
	DECL_EXPR with USING_DECL operand.
	* lambda.c (maybe_add_lambda_conv_op): Build thisarg using
	build_int_cst to make it a valid constant expression.

	* g++.dg/ubsan/vptr-4.C: Expect reinterpret_cast errors.
	* g++.dg/cpp1y/constexpr-84192.C (f2): Adjust expected diagnostics.
	* g++.dg/cpp1y/constexpr-70265-2.C (foo): Adjust expected line of
	diagnostics.
	* g++.dg/cpp1y/constexpr-89285.C: New test.

--- gcc/builtins.c.jj	2019-02-16 12:19:37.599192312 +0100
+++ gcc/builtins.c	2019-02-19 10:20:01.103606684 +0100
@@ -9302,8 +9302,7 @@ fold_builtin_arith_overflow (location_t
 			     tree arg0, tree arg1, tree arg2)
 {
   enum internal_fn ifn = IFN_LAST;
-  /* The code of the expression corresponding to the type-generic
-     built-in, or ERROR_MARK for the type-specific ones.  */
+  /* The code of the expression corresponding to the built-in.  */
   enum tree_code opcode = ERROR_MARK;
   bool ovf_only = false;
 
@@ -9313,42 +9312,39 @@ fold_builtin_arith_overflow (location_t
       ovf_only = true;
       /* FALLTHRU */
     case BUILT_IN_ADD_OVERFLOW:
-      opcode = PLUS_EXPR;
-      /* FALLTHRU */
     case BUILT_IN_SADD_OVERFLOW:
     case BUILT_IN_SADDL_OVERFLOW:
     case BUILT_IN_SADDLL_OVERFLOW:
     case BUILT_IN_UADD_OVERFLOW:
     case BUILT_IN_UADDL_OVERFLOW:
     case BUILT_IN_UADDLL_OVERFLOW:
+      opcode = PLUS_EXPR;
       ifn = IFN_ADD_OVERFLOW;
       break;
     case BUILT_IN_SUB_OVERFLOW_P:
       ovf_only = true;
       /* FALLTHRU */
     case BUILT_IN_SUB_OVERFLOW:
-      opcode = MINUS_EXPR;
-      /* FALLTHRU */
     case BUILT_IN_SSUB_OVERFLOW:
     case BUILT_IN_SSUBL_OVERFLOW:
     case BUILT_IN_SSUBLL_OVERFLOW:
     case BUILT_IN_USUB_OVERFLOW:
     case BUILT_IN_USUBL_OVERFLOW:
     case BUILT_IN_USUBLL_OVERFLOW:
+      opcode = MINUS_EXPR;
       ifn = IFN_SUB_OVERFLOW;
       break;
     case BUILT_IN_MUL_OVERFLOW_P:
       ovf_only = true;
       /* FALLTHRU */
     case BUILT_IN_MUL_OVERFLOW:
-      opcode = MULT_EXPR;
-      /* FALLTHRU */
     case BUILT_IN_SMUL_OVERFLOW:
     case BUILT_IN_SMULL_OVERFLOW:
     case BUILT_IN_SMULLL_OVERFLOW:
     case BUILT_IN_UMUL_OVERFLOW:
     case BUILT_IN_UMULL_OVERFLOW:
     case BUILT_IN_UMULLL_OVERFLOW:
+      opcode = MULT_EXPR;
       ifn = IFN_MUL_OVERFLOW;
       break;
     default:
@@ -9373,13 +9369,27 @@ fold_builtin_arith_overflow (location_t
 				 ? boolean_true_node : boolean_false_node,
 				 arg2);
 
-  tree ctype = build_complex_type (type);
-  tree call = build_call_expr_internal_loc (loc, ifn, ctype,
-					    2, arg0, arg1);
-  tree tgt = save_expr (call);
-  tree intres = build1_loc (loc, REALPART_EXPR, type, tgt);
-  tree ovfres = build1_loc (loc, IMAGPART_EXPR, type, tgt);
-  ovfres = fold_convert_loc (loc, boolean_type_node, ovfres);
+  tree intres, ovfres;
+  if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST)
+    {
+      intres = fold_binary_loc (loc, opcode, type,
+				fold_convert_loc (loc, type, arg0),
+				fold_convert_loc (loc, type, arg1));
+      if (TREE_OVERFLOW (intres))
+	intres = drop_tree_overflow (intres);
+      ovfres = (arith_overflowed_p (opcode, type, arg0, arg1)
+		? boolean_true_node : boolean_false_node);
+    }
+  else
+    {
+      tree ctype = build_complex_type (type);
+      tree call = build_call_expr_internal_loc (loc, ifn, ctype, 2,
+						arg0, arg1);
+      tree tgt = save_expr (call);
+      intres = build1_loc (loc, REALPART_EXPR, type, tgt);
+      ovfres = build1_loc (loc, IMAGPART_EXPR, type, tgt);
+      ovfres = fold_convert_loc (loc, boolean_type_node, ovfres);
+    }
 
   if (ovf_only)
     return omit_one_operand_loc (loc, boolean_type_node, ovfres, arg2);
--- gcc/cp/constexpr.c.jj	2019-02-19 09:33:15.499185224 +0100
+++ gcc/cp/constexpr.c	2019-02-19 10:26:09.567962395 +0100
@@ -139,6 +139,8 @@ ensure_literal_type_for_constexpr_object
 struct GTY((for_user)) constexpr_fundef {
   tree decl;
   tree body;
+  tree parms;
+  tree result;
 };
 
 struct constexpr_fundef_hasher : ggc_ptr_hash<constexpr_fundef>
@@ -176,11 +178,10 @@ constexpr_fundef_hasher::hash (constexpr
 static constexpr_fundef *
 retrieve_constexpr_fundef (tree fun)
 {
-  constexpr_fundef fundef = { NULL, NULL };
   if (constexpr_fundef_table == NULL)
     return NULL;
 
-  fundef.decl = fun;
+  constexpr_fundef fundef = { fun, NULL, NULL, NULL };
   return constexpr_fundef_table->find (&fundef);
 }
 
@@ -897,8 +898,19 @@ register_constexpr_fundef (tree fun, tre
       = hash_table<constexpr_fundef_hasher>::create_ggc (101);
 
   entry.decl = fun;
-  entry.body = body;
+  tree saved_fn = current_function_decl;
+  bool clear_ctx = false;
+  current_function_decl = fun;
+  if (DECL_RESULT (fun) && DECL_CONTEXT (DECL_RESULT (fun)) == NULL_TREE)
+    {
+      clear_ctx = true;
+      DECL_CONTEXT (DECL_RESULT (fun)) = fun;
+    }
+  entry.body = copy_fn (fun, entry.parms, entry.result);
+  current_function_decl = saved_fn;
   slot = constexpr_fundef_table->find_slot (&entry, INSERT);
+  if (clear_ctx)
+    DECL_CONTEXT (DECL_RESULT (fun)) = NULL_TREE;
 
   gcc_assert (*slot == NULL);
   *slot = ggc_alloc<constexpr_fundef> ();
@@ -1114,27 +1126,40 @@ maybe_initialize_fundef_copies_table ()
    is parms, TYPE is result.  */
 
 static tree
-get_fundef_copy (tree fun)
+get_fundef_copy (constexpr_fundef *fundef)
 {
   maybe_initialize_fundef_copies_table ();
 
   tree copy;
   bool existed;
-  tree *slot = &fundef_copies_table->get_or_insert (fun, &existed);
+  tree *slot = &fundef_copies_table->get_or_insert (fundef->decl, &existed);
 
   if (!existed)
     {
       /* There is no cached function available, or in use.  We can use
 	 the function directly.  That the slot is now created records
 	 that this function is now in use.  */
-      copy = build_tree_list (DECL_SAVED_TREE (fun), DECL_ARGUMENTS (fun));
-      TREE_TYPE (copy) = DECL_RESULT (fun);
+      copy = build_tree_list (fundef->body, fundef->parms);
+      TREE_TYPE (copy) = fundef->result;
     }
   else if (*slot == NULL_TREE)
     {
       /* We've already used the function itself, so make a copy.  */
       copy = build_tree_list (NULL, NULL);
-      TREE_PURPOSE (copy) = copy_fn (fun, TREE_VALUE (copy), TREE_TYPE (copy));
+      tree saved_body = DECL_SAVED_TREE (fundef->decl);
+      tree saved_parms = DECL_ARGUMENTS (fundef->decl);
+      tree saved_result = DECL_RESULT (fundef->decl);
+      tree saved_fn = current_function_decl;
+      DECL_SAVED_TREE (fundef->decl) = fundef->body;
+      DECL_ARGUMENTS (fundef->decl) = fundef->parms;
+      DECL_RESULT (fundef->decl) = fundef->result;
+      current_function_decl = fundef->decl;
+      TREE_PURPOSE (copy) = copy_fn (fundef->decl, TREE_VALUE (copy),
+				     TREE_TYPE (copy));
+      current_function_decl = saved_fn;
+      DECL_RESULT (fundef->decl) = saved_result;
+      DECL_ARGUMENTS (fundef->decl) = saved_parms;
+      DECL_SAVED_TREE (fundef->decl) = saved_body;
     }
   else
     {
@@ -1245,8 +1270,15 @@ cxx_eval_builtin_function_call (const co
 
   bool save_ffbcp = force_folding_builtin_constant_p;
   force_folding_builtin_constant_p = true;
+  tree save_cur_fn = current_function_decl;
+  /* Return name of ctx->call->fundef->decl for __builtin_FUNCTION ().  */
+  if (fndecl_built_in_p (fun, BUILT_IN_FUNCTION)
+      && ctx->call
+      && ctx->call->fundef)
+    current_function_decl = ctx->call->fundef->decl;
   new_call = fold_builtin_call_array (EXPR_LOCATION (t), TREE_TYPE (t),
 				      CALL_EXPR_FN (t), nargs, args);
+  current_function_decl = save_cur_fn;
   force_folding_builtin_constant_p = save_ffbcp;
   if (new_call == NULL)
     {
@@ -1269,6 +1301,46 @@ cxx_eval_builtin_function_call (const co
       return t;
     }
 
+  if (fndecl_built_in_p (fun, BUILT_IN_NORMAL))
+    switch (DECL_FUNCTION_CODE (fun))
+      {
+      case BUILT_IN_ADD_OVERFLOW:
+      case BUILT_IN_SADD_OVERFLOW:
+      case BUILT_IN_SADDL_OVERFLOW:
+      case BUILT_IN_SADDLL_OVERFLOW:
+      case BUILT_IN_UADD_OVERFLOW:
+      case BUILT_IN_UADDL_OVERFLOW:
+      case BUILT_IN_UADDLL_OVERFLOW:
+      case BUILT_IN_SUB_OVERFLOW:
+      case BUILT_IN_SSUB_OVERFLOW:
+      case BUILT_IN_SSUBL_OVERFLOW:
+      case BUILT_IN_SSUBLL_OVERFLOW:
+      case BUILT_IN_USUB_OVERFLOW:
+      case BUILT_IN_USUBL_OVERFLOW:
+      case BUILT_IN_USUBLL_OVERFLOW:
+      case BUILT_IN_MUL_OVERFLOW:
+      case BUILT_IN_SMUL_OVERFLOW:
+      case BUILT_IN_SMULL_OVERFLOW:
+      case BUILT_IN_SMULLL_OVERFLOW:
+      case BUILT_IN_UMUL_OVERFLOW:
+      case BUILT_IN_UMULL_OVERFLOW:
+      case BUILT_IN_UMULLL_OVERFLOW:
+	/* These builtins will fold into ((something = cst1), cst2)
+	   which fails is_constant_expression.  */
+	if (TREE_CODE (args[0]) != INTEGER_CST
+	    || TREE_CODE (args[1]) != INTEGER_CST
+	    || !potential_constant_expression (args[2]))
+	  {
+	    if (!*non_constant_p && !ctx->quiet)
+	      error ("%q+E is not a constant expression", new_call);
+	    *non_constant_p = true;
+	    return t;
+	  }
+	return cxx_eval_constant_expression (&new_ctx, new_call, lval,
+					     non_constant_p, overflow_p);
+      default:
+	break;
+      }
   if (!is_constant_expression (new_call))
     {
       if (!*non_constant_p && !ctx->quiet)
@@ -1341,7 +1413,7 @@ cxx_bind_parameters_in_call (const const
 {
   const int nargs = call_expr_nargs (t);
   tree fun = new_call->fundef->decl;
-  tree parms = DECL_ARGUMENTS (fun);
+  tree parms = new_call->fundef->parms;
   int i;
   tree *p = &new_call->bindings;
   for (i = 0; i < nargs; ++i)
@@ -1358,6 +1430,9 @@ cxx_bind_parameters_in_call (const const
 	  x = ctx->object;
 	  x = build_address (x);
 	}
+      if (TREE_ADDRESSABLE (type))
+	/* Undo convert_for_arg_passing work here.  */
+	x = convert_from_reference (x);
       arg = cxx_eval_constant_expression (ctx, x, /*lval=*/false,
 					  non_constant_p, overflow_p);
       /* Don't VERIFY_CONSTANT here.  */
@@ -1761,7 +1836,7 @@ cxx_eval_call_expression (const constexp
 	  tree body, parms, res;
 
 	  /* Reuse or create a new unshared copy of this function's body.  */
-	  tree copy = get_fundef_copy (fun);
+	  tree copy = get_fundef_copy (new_call.fundef);
 	  body = TREE_PURPOSE (copy);
 	  parms = TREE_VALUE (copy);
 	  res = TREE_TYPE (copy);
@@ -2223,14 +2298,13 @@ cxx_eval_conditional_expression (const c
   VERIFY_CONSTANT (val);
   /* Don't VERIFY_CONSTANT the other operands.  */
   if (integer_zerop (val))
-    return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 2),
-					 lval,
-					 non_constant_p, overflow_p,
-					 jump_target);
-  return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
-				       lval,
-				       non_constant_p, overflow_p,
-				       jump_target);
+    val = TREE_OPERAND (t, 2);
+  else
+    val = TREE_OPERAND (t, 1);
+  if (TREE_CODE (t) == IF_STMT && !val)
+    val = void_node;
+  return cxx_eval_constant_expression (ctx, val, lval, non_constant_p,
+				       overflow_p, jump_target);
 }
 
 /* Subroutine of cxx_eval_constant_expression.
@@ -4047,6 +4121,12 @@ label_matches (const constexpr_ctx *ctx,
 	}
       break;
 
+    case BREAK_STMT:
+    case CONTINUE_STMT:
+      /* These two are handled directly in cxx_eval_loop_expr by testing
+	 breaks (jump_target) or continues (jump_target).  */
+      break;
+
     default:
       gcc_unreachable ();
     }
@@ -4107,20 +4187,84 @@ cxx_eval_loop_expr (const constexpr_ctx
 {
   constexpr_ctx new_ctx = *ctx;
 
-  tree body = TREE_OPERAND (t, 0);
+  tree body, cond = NULL_TREE, expr = NULL_TREE;
   int count = 0;
+  switch (TREE_CODE (t))
+    {
+    case LOOP_EXPR:
+      body = LOOP_EXPR_BODY (t);
+      break;
+    case DO_STMT:
+      body = DO_BODY (t);
+      cond = DO_COND (t);
+      break;
+    case WHILE_STMT:
+      body = WHILE_BODY (t);
+      cond = WHILE_COND (t);
+      count = -1;
+      break;
+    case FOR_STMT:
+      if (FOR_INIT_STMT (t))
+	cxx_eval_constant_expression (ctx, FOR_INIT_STMT (t), /*lval*/false,
+				      non_constant_p, overflow_p, jump_target);
+      if (*non_constant_p)
+	return NULL_TREE;
+      body = FOR_BODY (t);
+      cond = FOR_COND (t);
+      expr = FOR_EXPR (t);
+      count = -1;
+      break;
+    default:
+      gcc_unreachable ();
+    }
+  hash_set<tree> save_exprs;
+  new_ctx.save_exprs = &save_exprs;
   do
     {
-      hash_set<tree> save_exprs;
-      new_ctx.save_exprs = &save_exprs;
+      if (count != -1)
+	{
+	  if (body)
+	    cxx_eval_constant_expression (&new_ctx, body, /*lval*/false,
+					  non_constant_p, overflow_p,
+					  jump_target);
+	  if (breaks (jump_target))
+	    {
+	      *jump_target = NULL_TREE;
+	      break;
+	    }
 
-      cxx_eval_constant_expression (&new_ctx, body, /*lval*/false,
-				    non_constant_p, overflow_p, jump_target);
+	  if (TREE_CODE (t) != LOOP_EXPR && continues (jump_target))
+	    *jump_target = NULL_TREE;
+
+	  if (expr)
+	    cxx_eval_constant_expression (&new_ctx, expr, /*lval*/false,
+					  non_constant_p, overflow_p,
+					  jump_target);
+	}
+
+      if (cond)
+	{
+	  tree res
+	    = cxx_eval_constant_expression (&new_ctx, cond, /*lval*/false,
+					    non_constant_p, overflow_p,
+					    jump_target);
+	  if (res)
+	    {
+	      if (verify_constant (res, ctx->quiet, non_constant_p,
+				   overflow_p))
+		break;
+	      if (integer_zerop (res))
+		break;
+	    }
+	  else
+	    gcc_assert (*jump_target);
+	}
 
       /* Forget saved values of SAVE_EXPRs.  */
       for (hash_set<tree>::iterator iter = save_exprs.begin();
 	   iter != save_exprs.end(); ++iter)
 	new_ctx.values->remove (*iter);
+
       if (++count >= constexpr_loop_limit)
 	{
 	  if (!ctx->quiet)
@@ -4134,11 +4278,14 @@ cxx_eval_loop_expr (const constexpr_ctx
     }
   while (!returns (jump_target)
 	 && !breaks (jump_target)
-	 && !switches (jump_target)
+	 && !continues (jump_target)
+	 && (!switches (jump_target) || count == 0)
 	 && !*non_constant_p);
 
-  if (breaks (jump_target))
-    *jump_target = NULL_TREE;
+  /* Forget saved values of SAVE_EXPRs.  */
+  for (hash_set<tree>::iterator iter = save_exprs.begin();
+       iter != save_exprs.end(); ++iter)
+    new_ctx.values->remove (*iter);
 
   return NULL_TREE;
 }
@@ -4151,13 +4298,15 @@ cxx_eval_switch_expr (const constexpr_ct
 		      bool *non_constant_p, bool *overflow_p,
 		      tree *jump_target)
 {
-  tree cond = SWITCH_COND (t);
+  tree cond
+    = TREE_CODE (t) == SWITCH_STMT ? SWITCH_STMT_COND (t) : SWITCH_COND (t);
   cond = cxx_eval_constant_expression (ctx, cond, false,
 				       non_constant_p, overflow_p);
   VERIFY_CONSTANT (cond);
   *jump_target = cond;
 
-  tree body = SWITCH_BODY (t);
+  tree body
+    = TREE_CODE (t) == SWITCH_STMT ? SWITCH_STMT_BODY (t) : SWITCH_BODY (t);
   constexpr_ctx new_ctx = *ctx;
   constexpr_switch_state css = css_default_not_seen;
   new_ctx.css_state = &css;
@@ -4233,6 +4382,10 @@ cxx_eval_constant_expression (const cons
 	case STATEMENT_LIST:
 	case LOOP_EXPR:
 	case COND_EXPR:
+	case IF_STMT:
+	case DO_STMT:
+	case WHILE_STMT:
+	case FOR_STMT:
 	  break;
 	case LABEL_EXPR:
 	case CASE_LABEL_EXPR:
@@ -4377,6 +4530,11 @@ cxx_eval_constant_expression (const cons
     case DECL_EXPR:
       {
 	r = DECL_EXPR_DECL (t);
+	if (TREE_CODE (r) == USING_DECL)
+	  {
+	    r = void_node;
+	    break;
+	  }
 	if (AGGREGATE_TYPE_P (TREE_TYPE (r))
 	    || VECTOR_TYPE_P (TREE_TYPE (r)))
 	  {
@@ -4464,6 +4622,9 @@ cxx_eval_constant_expression (const cons
 	r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
 					  lval,
 					  non_constant_p, overflow_p);
+      /* FALLTHRU */
+    case BREAK_STMT:
+    case CONTINUE_STMT:
       if (jump_target)
 	*jump_target = t;
       else
@@ -4514,6 +4675,17 @@ cxx_eval_constant_expression (const cons
 				      jump_target);
       break;
 
+    case CLEANUP_STMT:
+      r = cxx_eval_constant_expression (ctx, CLEANUP_BODY (t), lval,
+					non_constant_p, overflow_p,
+					jump_target);
+      if (!CLEANUP_EH_ONLY (t) && !*non_constant_p)
+	/* Also evaluate the cleanup.  */
+	cxx_eval_constant_expression (ctx, CLEANUP_EXPR (t), true,
+				      non_constant_p, overflow_p,
+				      jump_target);
+      break;
+
       /* These differ from cxx_eval_unary_expression in that this doesn't
 	 check for a constant operand or result; an address can be
 	 constant without its operand being, and vice versa.  */
@@ -4569,8 +4741,9 @@ cxx_eval_constant_expression (const cons
       break;
 
     case SIZEOF_EXPR:
-      r = fold_sizeof_expr (t);
-      VERIFY_CONSTANT (r);
+      r = cxx_eval_constant_expression (ctx, fold_sizeof_expr (t), lval,
+					non_constant_p, overflow_p,
+					jump_target);
       break;
 
     case COMPOUND_EXPR:
@@ -4690,16 +4863,18 @@ cxx_eval_constant_expression (const cons
       break;
 
     case COND_EXPR:
+    case IF_STMT:
       if (jump_target && *jump_target)
 	{
 	  tree orig_jump = *jump_target;
+	  tree arg = ((TREE_CODE (t) != IF_STMT || TREE_OPERAND (t, 1))
+		      ? TREE_OPERAND (t, 1) : void_node);
 	  /* When jumping to a label, the label might be either in the
 	     then or else blocks, so process then block first in skipping
 	     mode first, and if we are still in the skipping mode at its end,
 	     process the else block too.  */
-	  r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
-					    lval, non_constant_p, overflow_p,
-					    jump_target);
+	  r = cxx_eval_constant_expression (ctx, arg, lval, non_constant_p,
+					    overflow_p, jump_target);
 	  /* It's possible that we found the label in the then block.  But
 	     it could have been followed by another jumping statement, e.g.
 	     say we're looking for case 1:
@@ -4713,9 +4888,12 @@ cxx_eval_constant_expression (const cons
 	     in which case we need not go looking to the else block.
 	     (goto is not allowed in a constexpr function.)  */
 	  if (*jump_target == orig_jump)
-	    r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 2),
-					      lval, non_constant_p, overflow_p,
-					      jump_target);
+	    {
+	      arg = ((TREE_CODE (t) != IF_STMT || TREE_OPERAND (t, 2))
+		     ? TREE_OPERAND (t, 2) : void_node);
+	      r = cxx_eval_constant_expression (ctx, arg, lval, non_constant_p,
+						overflow_p, jump_target);
+	    }
 	  break;
 	}
       r = cxx_eval_conditional_expression (ctx, t, lval,
@@ -4959,11 +5137,15 @@ cxx_eval_constant_expression (const cons
       break;
 
     case LOOP_EXPR:
+    case DO_STMT:
+    case WHILE_STMT:
+    case FOR_STMT:
       cxx_eval_loop_expr (ctx, t,
 			  non_constant_p, overflow_p, jump_target);
       break;
 
     case SWITCH_EXPR:
+    case SWITCH_STMT:
       cxx_eval_switch_expr (ctx, t,
 			    non_constant_p, overflow_p, jump_target);
       break;
--- gcc/cp/lambda.c.jj	2019-02-19 09:41:58.515496370 +0100
+++ gcc/cp/lambda.c	2019-02-19 10:11:09.463430637 +0100
@@ -1087,8 +1087,7 @@ maybe_add_lambda_conv_op (tree type)
   tree optype = TREE_TYPE (callop);
   tree fn_result = TREE_TYPE (optype);
 
-  tree thisarg = build_nop (TREE_TYPE (DECL_ARGUMENTS (callop)),
-			    null_pointer_node);
+  tree thisarg = build_int_cst (TREE_TYPE (DECL_ARGUMENTS (callop)), 0);
   if (generic_lambda_p)
     {
       ++processing_template_decl;
--- gcc/testsuite/g++.dg/ubsan/vptr-4.C.jj	2019-02-19 09:33:03.190389710 +0100
+++ gcc/testsuite/g++.dg/ubsan/vptr-4.C	2019-02-19 10:11:09.463430637 +0100
@@ -15,11 +15,11 @@ struct T : S {
   int b;
   int g() { return 0; }
   virtual int v() { return 1; }
-  constexpr const T *foo() { return (const T *) reinterpret_cast<const S *> (this); }
+  constexpr const T *foo() { return (const T *) reinterpret_cast<const S *> (this); } // { dg-error "is not a constant expression" }
 };
 
 constexpr T t;
-constexpr const T *p = t.foo ();
+constexpr const T *p = t.foo ();	// { dg-message "expansion of" }
 
 template <typename U>
 struct V {
@@ -39,16 +39,17 @@ struct W : V<U> {
 };
 
 constexpr W<int> w;
-constexpr const W<int> *s = w.foo ();
+constexpr const W<int> *s = w.foo ();	// { dg-error "is not a constant expression" }
+// { dg-message "expansion of" "" { target *-*-* } .-1 }
 
 template <typename U>
 int foo (void)
 {
   static constexpr T t;
-  static constexpr const T *p = t.foo ();
+  static constexpr const T *p = t.foo ();	// { dg-message "expansion of" }
   static constexpr W<U> w;
-  static constexpr const W<U> *s = w.foo ();
-  return t.b + w.b;
+  static constexpr const W<U> *s = w.foo ();	// { dg-error "is not a constant expression" }
+  return t.b + w.b;				// { dg-message "expansion of" "" { target *-*-* } .-1 }
 }
 
 int x = foo <char> ();
--- gcc/testsuite/g++.dg/cpp1y/constexpr-84192.C.jj	2019-02-19 09:33:03.099391222 +0100
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-84192.C	2019-02-19 10:11:09.463430637 +0100
@@ -12,7 +12,7 @@ void
 f2 ()
 { 
   for (;;)
-    constexpr bool b = ({ break; false; }) && false;	// { dg-error "statement is not a constant expression" }
+    constexpr bool b = ({ break; false; }) && false;	// { dg-error "is not a constant expression" }
 }
 
 constexpr bool
--- gcc/testsuite/g++.dg/cpp1y/constexpr-70265-2.C.jj	2019-02-19 09:33:03.131390690 +0100
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-70265-2.C	2019-02-19 10:11:09.463430637 +0100
@@ -5,8 +5,8 @@ constexpr int
 foo (int p)
 {
   int t = 0;
-  while (1)
-    t = 0;  // { dg-error "count exceeds" }
+  while (1)  // { dg-error "count exceeds" }
+    t = 0;
   return t;
 }
 
--- gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C.jj	2019-02-19 10:11:09.464430621 +0100
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C	2019-02-19 10:11:09.464430621 +0100
@@ -0,0 +1,20 @@
+// PR c++/89285
+// { dg-do compile { target c++14 } }
+
+struct A {
+  int a {};
+};
+struct B {
+  int b {};
+  constexpr B (A *x) {
+    int *c = &x->a;
+    while (*c)
+      c = reinterpret_cast<int *>((reinterpret_cast<char *>(c) + *c));
+    *c = reinterpret_cast<char *>(this) - reinterpret_cast<char *>(c);	// { dg-error "reinterpret_cast" }
+  }
+};
+struct C : A {
+  B bar {this};
+};
+
+constexpr C foo {};	// { dg-message "expansion of" }


	Jakub

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [C++ PATCH] Ensure constexpr evaluation is done on pre-cp_fold_function bodies (PR c++/89285)
  2019-02-19  4:31   ` Jakub Jelinek
  2019-02-20  7:46     ` [C++ PATCH] Ensure constexpr evaluation is done on pre-cp_fold_function bodies (PR c++/89285, take 2) Jakub Jelinek
@ 2019-02-20 21:22     ` Jason Merrill
  2019-02-20 22:16       ` [C++ PATCH] Ensure constexpr evaluation is done on pre-cp_fold_function bodies (PR c++/89285, take 3) Jakub Jelinek
  1 sibling, 1 reply; 9+ messages in thread
From: Jason Merrill @ 2019-02-20 21:22 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 2/18/19 6:31 PM, Jakub Jelinek wrote:
> On Mon, Feb 18, 2019 at 04:04:15PM -1000, Jason Merrill wrote:
>>> --- gcc/cp/constexpr.c.jj	2019-02-17 17:09:47.113351897 +0100
>>> +++ gcc/cp/constexpr.c	2019-02-18 19:34:57.995136395 +0100
>>> @@ -1269,6 +1301,49 @@ cxx_eval_builtin_function_call (const co
>>>          return t;
>>>        }
>>> +  if (fndecl_built_in_p (fun, BUILT_IN_NORMAL))
>>> +    switch (DECL_FUNCTION_CODE (fun))
>>> +      {
>>> +      case BUILT_IN_ADD_OVERFLOW:
>>> +      case BUILT_IN_SADD_OVERFLOW:
>>> +      case BUILT_IN_SADDL_OVERFLOW:
>>> +      case BUILT_IN_SADDLL_OVERFLOW:
>>> +      case BUILT_IN_UADD_OVERFLOW:
>>> +      case BUILT_IN_UADDL_OVERFLOW:
>>> +      case BUILT_IN_UADDLL_OVERFLOW:
>>> +      case BUILT_IN_SUB_OVERFLOW:
>>> +      case BUILT_IN_SSUB_OVERFLOW:
>>> +      case BUILT_IN_SSUBL_OVERFLOW:
>>> +      case BUILT_IN_SSUBLL_OVERFLOW:
>>> +      case BUILT_IN_USUB_OVERFLOW:
>>> +      case BUILT_IN_USUBL_OVERFLOW:
>>> +      case BUILT_IN_USUBLL_OVERFLOW:
>>> +      case BUILT_IN_MUL_OVERFLOW:
>>> +      case BUILT_IN_SMUL_OVERFLOW:
>>> +      case BUILT_IN_SMULL_OVERFLOW:
>>> +      case BUILT_IN_SMULLL_OVERFLOW:
>>> +      case BUILT_IN_UMUL_OVERFLOW:
>>> +      case BUILT_IN_UMULL_OVERFLOW:
>>> +      case BUILT_IN_UMULLL_OVERFLOW:
>>> +	/* These builtins will fold into
>>> +	   (cast)
>>> +	     ((something = __real__ SAVE_EXPR <.???_OVERFLOW (cst1, cst2)>),
>>> +	      __imag__ SAVE_EXPR <.???_OVERFLOW (cst1, cst2)>)
>>> +	   which fails is_constant_expression.  */
>>> +	if (TREE_CODE (args[0]) != INTEGER_CST
>>> +	    || TREE_CODE (args[1]) != INTEGER_CST
>>> +	    || !potential_constant_expression (args[2]))
>>> +	  {
>>> +	    if (!*non_constant_p && !ctx->quiet)
>>> +	      error ("%q+E is not a constant expression", new_call);
>>> +	    *non_constant_p = true;
>>> +	    return t;
>>> +	  }
>>> +	return cxx_eval_constant_expression (&new_ctx, new_call, lval,
>>> +					     non_constant_p, overflow_p);
>>> +      default:
>>> +	break;
>>> +      }
>>
>> What is this for?  Won't this recursive cxx_eval_constant_expression come
>> back to this function again?  If the expression is constant, shouldn't it
>> have been folded by fold_builtin_call_array?
> 
> This is for the constexpr-arith-overflow.C testcase.
> The arguments are INTEGER_CST, INTEGER_CST and ADDR_EXPR of a VAR_DECL or
> PARM_DECL, and fold_builtin_call_array returns new_call:
> (z = REALPART_EXPR <SAVE_EXPR <.ADD_OVERFLOW (0, 0)>>;, (bool) IMAGPART_EXPR <SAVE_EXPR <.ADD_OVERFLOW (0, 0)>>;);
> where this doesn't pass is_constant_expression because of the z store.
> cxx_eval_constant_expression is able to evaluate this, as
> z = 0;
> false;
> in this case.
> I guess builtins.c folding could be improved and simplify it to
> (z = 0; (bool) false;);
> but that still doesn't pass is_constant_expression check.
> For C++14 it passes potential_constant_expression though, and that
> is what I've used for these builtins in the first iteration, but
> the testcase happened to pass even for C++11 and
> potential_constant_expression is false here.  Though, perhaps we are going
> too far for C++11 here and should reject it, after all, people have the
> possibility to use __builtin_*_overflow_p now which should be usable even in
> C++11.  The reason why it passed with C++11 is that when parsing we saw
> a __builtin_add_overflow (0, 0, &z) call and potential_constant_expression
> said it is ok, then folded it into that
> (z = REALPART_EXPR <SAVE_EXPR <.ADD_OVERFLOW (0, 0)>>;, (bool) IMAGPART_EXPR <SAVE_EXPR <.ADD_OVERFLOW (0, 0)>>;);
> which is not potential_constant_expression, but nothing called it again
> and cxx_eval_constant_expression can handle it.

Yeah, that seems like a bug; C++11 shouldn't allow modification of z 
this way either.

Jason

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [C++ PATCH] Ensure constexpr evaluation is done on pre-cp_fold_function bodies (PR c++/89285, take 3)
  2019-02-20 21:22     ` [C++ PATCH] Ensure constexpr evaluation is done on pre-cp_fold_function bodies (PR c++/89285) Jason Merrill
@ 2019-02-20 22:16       ` Jakub Jelinek
  2019-02-21  0:00         ` Jason Merrill
  0 siblings, 1 reply; 9+ messages in thread
From: Jakub Jelinek @ 2019-02-20 22:16 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches

On Wed, Feb 20, 2019 at 11:12:07AM -1000, Jason Merrill wrote:
> > (z = REALPART_EXPR <SAVE_EXPR <.ADD_OVERFLOW (0, 0)>>;, (bool) IMAGPART_EXPR <SAVE_EXPR <.ADD_OVERFLOW (0, 0)>>;);
> > which is not potential_constant_expression, but nothing called it again
> > and cxx_eval_constant_expression can handle it.
> 
> Yeah, that seems like a bug; C++11 shouldn't allow modification of z this
> way either.

Ack.  The following version stops accepting that for C++11, but keeps
accepting for C++14 and later (so for that still needs to test
potential_constant_expression instead of is_constant_expression, plus
added a new inline function so that the code is more readable).
I've tweaked the existing testcase so that it doesn't test it for C++11 only
and added a new c++11_only testcase that covers that.

Ok for trunk if it passes bootstrap/regtest?

2019-02-20  Jakub Jelinek  <jakub@redhat.com>

	PR c++/89285
	* builtins.c (fold_builtin_arith_overflow): If first two args are
	INTEGER_CSTs, set intres and ovfres to constants rather than calls
	to ifn.

	* constexpr.c (struct constexpr_fundef): Add parms and result members.
	(retrieve_constexpr_fundef): Adjust for the above change.
	(register_constexpr_fundef): Save constexpr body with copy_fn,
	temporarily set DECL_CONTEXT on DECL_RESULT before that.
	(get_fundef_copy): Change FUN argument to FUNDEF with
	constexpr_fundef * type, grab body and parms/result out of
	constexpr_fundef struct and temporarily change it for copy_fn calls
	too.
	(arith_overflow_builtin): New function.
	(cxx_eval_builtin_function_call): For __builtin_FUNCTION temporarily
	adjust current_function_decl from ctx->call context.  For
	arith_overflow_builtin functions, test potential_constant_expression
	instead of is_constant_expression.
	(cxx_bind_parameters_in_call): Grab parameters from new_call.  Undo
	convert_for_arg_passing changes for TREE_ADDRESSABLE type passing.
	(cxx_eval_call_expression): Adjust get_fundef_copy caller.
	(cxx_eval_conditional_expression): For IF_STMT, allow then or else
	operands to be NULL.
	(label_matches): Handle BREAK_STMT and CONTINUE_STMT.
	(cxx_eval_loop_expr): Add support for FOR_STMT, WHILE_STMT and DO_STMT.
	(cxx_eval_switch_expr): Add support for SWITCH_STMT.
	(cxx_eval_constant_expression): Handle IF_STMT, FOR_STMT, WHILE_STMT,
	DO_STMT, CONTINUE_STMT, SWITCH_STMT, BREAK_STMT and CONTINUE_STMT.
	For SIZEOF_EXPR, recurse on the result of fold_sizeof_expr.  Ignore
	DECL_EXPR with USING_DECL operand.
	* lambda.c (maybe_add_lambda_conv_op): Build thisarg using
	build_int_cst to make it a valid constant expression.

	* g++.dg/ubsan/vptr-4.C: Expect reinterpret_cast errors.
	* g++.dg/cpp1y/constexpr-84192.C (f2): Adjust expected diagnostics.
	* g++.dg/cpp1y/constexpr-70265-2.C (foo): Adjust expected line of
	diagnostics.
	* g++.dg/cpp1y/constexpr-89285.C: New test.
	* g++.dg/cpp0x/constexpr-arith-overflow.C (add, sub, mul): Ifdef out
	for C++11.
	(TEST_ADD, TEST_SUB, TEST_MUL): Define to Assert (true) for C++11.
	* g++.dg/cpp0x/constexpr-arith-overflow2.C: New test.

--- gcc/builtins.c.jj	2019-02-20 10:00:26.220878262 +0100
+++ gcc/builtins.c	2019-02-20 22:27:48.645760885 +0100
@@ -9302,8 +9302,7 @@ fold_builtin_arith_overflow (location_t
 			     tree arg0, tree arg1, tree arg2)
 {
   enum internal_fn ifn = IFN_LAST;
-  /* The code of the expression corresponding to the type-generic
-     built-in, or ERROR_MARK for the type-specific ones.  */
+  /* The code of the expression corresponding to the built-in.  */
   enum tree_code opcode = ERROR_MARK;
   bool ovf_only = false;
 
@@ -9313,42 +9312,39 @@ fold_builtin_arith_overflow (location_t
       ovf_only = true;
       /* FALLTHRU */
     case BUILT_IN_ADD_OVERFLOW:
-      opcode = PLUS_EXPR;
-      /* FALLTHRU */
     case BUILT_IN_SADD_OVERFLOW:
     case BUILT_IN_SADDL_OVERFLOW:
     case BUILT_IN_SADDLL_OVERFLOW:
     case BUILT_IN_UADD_OVERFLOW:
     case BUILT_IN_UADDL_OVERFLOW:
     case BUILT_IN_UADDLL_OVERFLOW:
+      opcode = PLUS_EXPR;
       ifn = IFN_ADD_OVERFLOW;
       break;
     case BUILT_IN_SUB_OVERFLOW_P:
       ovf_only = true;
       /* FALLTHRU */
     case BUILT_IN_SUB_OVERFLOW:
-      opcode = MINUS_EXPR;
-      /* FALLTHRU */
     case BUILT_IN_SSUB_OVERFLOW:
     case BUILT_IN_SSUBL_OVERFLOW:
     case BUILT_IN_SSUBLL_OVERFLOW:
     case BUILT_IN_USUB_OVERFLOW:
     case BUILT_IN_USUBL_OVERFLOW:
     case BUILT_IN_USUBLL_OVERFLOW:
+      opcode = MINUS_EXPR;
       ifn = IFN_SUB_OVERFLOW;
       break;
     case BUILT_IN_MUL_OVERFLOW_P:
       ovf_only = true;
       /* FALLTHRU */
     case BUILT_IN_MUL_OVERFLOW:
-      opcode = MULT_EXPR;
-      /* FALLTHRU */
     case BUILT_IN_SMUL_OVERFLOW:
     case BUILT_IN_SMULL_OVERFLOW:
     case BUILT_IN_SMULLL_OVERFLOW:
     case BUILT_IN_UMUL_OVERFLOW:
     case BUILT_IN_UMULL_OVERFLOW:
     case BUILT_IN_UMULLL_OVERFLOW:
+      opcode = MULT_EXPR;
       ifn = IFN_MUL_OVERFLOW;
       break;
     default:
@@ -9373,13 +9369,27 @@ fold_builtin_arith_overflow (location_t
 				 ? boolean_true_node : boolean_false_node,
 				 arg2);
 
-  tree ctype = build_complex_type (type);
-  tree call = build_call_expr_internal_loc (loc, ifn, ctype,
-					    2, arg0, arg1);
-  tree tgt = save_expr (call);
-  tree intres = build1_loc (loc, REALPART_EXPR, type, tgt);
-  tree ovfres = build1_loc (loc, IMAGPART_EXPR, type, tgt);
-  ovfres = fold_convert_loc (loc, boolean_type_node, ovfres);
+  tree intres, ovfres;
+  if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST)
+    {
+      intres = fold_binary_loc (loc, opcode, type,
+				fold_convert_loc (loc, type, arg0),
+				fold_convert_loc (loc, type, arg1));
+      if (TREE_OVERFLOW (intres))
+	intres = drop_tree_overflow (intres);
+      ovfres = (arith_overflowed_p (opcode, type, arg0, arg1)
+		? boolean_true_node : boolean_false_node);
+    }
+  else
+    {
+      tree ctype = build_complex_type (type);
+      tree call = build_call_expr_internal_loc (loc, ifn, ctype, 2,
+						arg0, arg1);
+      tree tgt = save_expr (call);
+      intres = build1_loc (loc, REALPART_EXPR, type, tgt);
+      ovfres = build1_loc (loc, IMAGPART_EXPR, type, tgt);
+      ovfres = fold_convert_loc (loc, boolean_type_node, ovfres);
+    }
 
   if (ovf_only)
     return omit_one_operand_loc (loc, boolean_type_node, ovfres, arg2);
--- gcc/cp/constexpr.c.jj	2019-02-20 22:15:04.400281259 +0100
+++ gcc/cp/constexpr.c	2019-02-20 22:31:37.704012468 +0100
@@ -139,6 +139,8 @@ ensure_literal_type_for_constexpr_object
 struct GTY((for_user)) constexpr_fundef {
   tree decl;
   tree body;
+  tree parms;
+  tree result;
 };
 
 struct constexpr_fundef_hasher : ggc_ptr_hash<constexpr_fundef>
@@ -176,11 +178,10 @@ constexpr_fundef_hasher::hash (constexpr
 static constexpr_fundef *
 retrieve_constexpr_fundef (tree fun)
 {
-  constexpr_fundef fundef = { NULL, NULL };
   if (constexpr_fundef_table == NULL)
     return NULL;
 
-  fundef.decl = fun;
+  constexpr_fundef fundef = { fun, NULL, NULL, NULL };
   return constexpr_fundef_table->find (&fundef);
 }
 
@@ -897,8 +898,19 @@ register_constexpr_fundef (tree fun, tre
       = hash_table<constexpr_fundef_hasher>::create_ggc (101);
 
   entry.decl = fun;
-  entry.body = body;
+  tree saved_fn = current_function_decl;
+  bool clear_ctx = false;
+  current_function_decl = fun;
+  if (DECL_RESULT (fun) && DECL_CONTEXT (DECL_RESULT (fun)) == NULL_TREE)
+    {
+      clear_ctx = true;
+      DECL_CONTEXT (DECL_RESULT (fun)) = fun;
+    }
+  entry.body = copy_fn (fun, entry.parms, entry.result);
+  current_function_decl = saved_fn;
   slot = constexpr_fundef_table->find_slot (&entry, INSERT);
+  if (clear_ctx)
+    DECL_CONTEXT (DECL_RESULT (fun)) = NULL_TREE;
 
   gcc_assert (*slot == NULL);
   *slot = ggc_alloc<constexpr_fundef> ();
@@ -1114,27 +1126,40 @@ maybe_initialize_fundef_copies_table ()
    is parms, TYPE is result.  */
 
 static tree
-get_fundef_copy (tree fun)
+get_fundef_copy (constexpr_fundef *fundef)
 {
   maybe_initialize_fundef_copies_table ();
 
   tree copy;
   bool existed;
-  tree *slot = &fundef_copies_table->get_or_insert (fun, &existed);
+  tree *slot = &fundef_copies_table->get_or_insert (fundef->decl, &existed);
 
   if (!existed)
     {
       /* There is no cached function available, or in use.  We can use
 	 the function directly.  That the slot is now created records
 	 that this function is now in use.  */
-      copy = build_tree_list (DECL_SAVED_TREE (fun), DECL_ARGUMENTS (fun));
-      TREE_TYPE (copy) = DECL_RESULT (fun);
+      copy = build_tree_list (fundef->body, fundef->parms);
+      TREE_TYPE (copy) = fundef->result;
     }
   else if (*slot == NULL_TREE)
     {
       /* We've already used the function itself, so make a copy.  */
       copy = build_tree_list (NULL, NULL);
-      TREE_PURPOSE (copy) = copy_fn (fun, TREE_VALUE (copy), TREE_TYPE (copy));
+      tree saved_body = DECL_SAVED_TREE (fundef->decl);
+      tree saved_parms = DECL_ARGUMENTS (fundef->decl);
+      tree saved_result = DECL_RESULT (fundef->decl);
+      tree saved_fn = current_function_decl;
+      DECL_SAVED_TREE (fundef->decl) = fundef->body;
+      DECL_ARGUMENTS (fundef->decl) = fundef->parms;
+      DECL_RESULT (fundef->decl) = fundef->result;
+      current_function_decl = fundef->decl;
+      TREE_PURPOSE (copy) = copy_fn (fundef->decl, TREE_VALUE (copy),
+				     TREE_TYPE (copy));
+      current_function_decl = saved_fn;
+      DECL_RESULT (fundef->decl) = saved_result;
+      DECL_ARGUMENTS (fundef->decl) = saved_parms;
+      DECL_SAVED_TREE (fundef->decl) = saved_body;
     }
   else
     {
@@ -1178,6 +1203,44 @@ get_nth_callarg (tree t, int n)
     }
 }
 
+/* Return true if FUN is
+   __builtin_{add,sub,mul,{s,u}{add,sub,mul}{,l,ll}}_overflow
+   builtin.  */
+
+static inline bool
+arith_overflow_builtin (tree fun)
+{
+  if (fndecl_built_in_p (fun, BUILT_IN_NORMAL))
+    switch (DECL_FUNCTION_CODE (fun))
+      {
+      case BUILT_IN_ADD_OVERFLOW:
+      case BUILT_IN_SADD_OVERFLOW:
+      case BUILT_IN_SADDL_OVERFLOW:
+      case BUILT_IN_SADDLL_OVERFLOW:
+      case BUILT_IN_UADD_OVERFLOW:
+      case BUILT_IN_UADDL_OVERFLOW:
+      case BUILT_IN_UADDLL_OVERFLOW:
+      case BUILT_IN_SUB_OVERFLOW:
+      case BUILT_IN_SSUB_OVERFLOW:
+      case BUILT_IN_SSUBL_OVERFLOW:
+      case BUILT_IN_SSUBLL_OVERFLOW:
+      case BUILT_IN_USUB_OVERFLOW:
+      case BUILT_IN_USUBL_OVERFLOW:
+      case BUILT_IN_USUBLL_OVERFLOW:
+      case BUILT_IN_MUL_OVERFLOW:
+      case BUILT_IN_SMUL_OVERFLOW:
+      case BUILT_IN_SMULL_OVERFLOW:
+      case BUILT_IN_SMULLL_OVERFLOW:
+      case BUILT_IN_UMUL_OVERFLOW:
+      case BUILT_IN_UMULL_OVERFLOW:
+      case BUILT_IN_UMULLL_OVERFLOW:
+	return true;
+      default:
+	break;
+      }
+  return false;
+}
+
 /* Attempt to evaluate T which represents a call to a builtin function.
    We assume here that all builtin functions evaluate to scalar types
    represented by _CST nodes.  */
@@ -1245,8 +1308,15 @@ cxx_eval_builtin_function_call (const co
 
   bool save_ffbcp = force_folding_builtin_constant_p;
   force_folding_builtin_constant_p = true;
+  tree save_cur_fn = current_function_decl;
+  /* Return name of ctx->call->fundef->decl for __builtin_FUNCTION ().  */
+  if (fndecl_built_in_p (fun, BUILT_IN_FUNCTION)
+      && ctx->call
+      && ctx->call->fundef)
+    current_function_decl = ctx->call->fundef->decl;
   new_call = fold_builtin_call_array (EXPR_LOCATION (t), TREE_TYPE (t),
 				      CALL_EXPR_FN (t), nargs, args);
+  current_function_decl = save_cur_fn;
   force_folding_builtin_constant_p = save_ffbcp;
   if (new_call == NULL)
     {
@@ -1269,7 +1339,9 @@ cxx_eval_builtin_function_call (const co
       return t;
     }
 
-  if (!is_constant_expression (new_call))
+  if (arith_overflow_builtin (fun)
+      ? !potential_constant_expression (new_call)
+      : !is_constant_expression (new_call))
     {
       if (!*non_constant_p && !ctx->quiet)
 	error ("%q+E is not a constant expression", new_call);
@@ -1341,7 +1413,7 @@ cxx_bind_parameters_in_call (const const
 {
   const int nargs = call_expr_nargs (t);
   tree fun = new_call->fundef->decl;
-  tree parms = DECL_ARGUMENTS (fun);
+  tree parms = new_call->fundef->parms;
   int i;
   tree *p = &new_call->bindings;
   for (i = 0; i < nargs; ++i)
@@ -1358,6 +1430,9 @@ cxx_bind_parameters_in_call (const const
 	  x = ctx->object;
 	  x = build_address (x);
 	}
+      if (TREE_ADDRESSABLE (type))
+	/* Undo convert_for_arg_passing work here.  */
+	x = convert_from_reference (x);
       arg = cxx_eval_constant_expression (ctx, x, /*lval=*/false,
 					  non_constant_p, overflow_p);
       /* Don't VERIFY_CONSTANT here.  */
@@ -1761,7 +1836,7 @@ cxx_eval_call_expression (const constexp
 	  tree body, parms, res;
 
 	  /* Reuse or create a new unshared copy of this function's body.  */
-	  tree copy = get_fundef_copy (fun);
+	  tree copy = get_fundef_copy (new_call.fundef);
 	  body = TREE_PURPOSE (copy);
 	  parms = TREE_VALUE (copy);
 	  res = TREE_TYPE (copy);
@@ -2223,14 +2298,13 @@ cxx_eval_conditional_expression (const c
   VERIFY_CONSTANT (val);
   /* Don't VERIFY_CONSTANT the other operands.  */
   if (integer_zerop (val))
-    return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 2),
-					 lval,
-					 non_constant_p, overflow_p,
-					 jump_target);
-  return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
-				       lval,
-				       non_constant_p, overflow_p,
-				       jump_target);
+    val = TREE_OPERAND (t, 2);
+  else
+    val = TREE_OPERAND (t, 1);
+  if (TREE_CODE (t) == IF_STMT && !val)
+    val = void_node;
+  return cxx_eval_constant_expression (ctx, val, lval, non_constant_p,
+				       overflow_p, jump_target);
 }
 
 /* Subroutine of cxx_eval_constant_expression.
@@ -4059,6 +4133,12 @@ label_matches (const constexpr_ctx *ctx,
 	}
       break;
 
+    case BREAK_STMT:
+    case CONTINUE_STMT:
+      /* These two are handled directly in cxx_eval_loop_expr by testing
+	 breaks (jump_target) or continues (jump_target).  */
+      break;
+
     default:
       gcc_unreachable ();
     }
@@ -4119,20 +4199,84 @@ cxx_eval_loop_expr (const constexpr_ctx
 {
   constexpr_ctx new_ctx = *ctx;
 
-  tree body = TREE_OPERAND (t, 0);
+  tree body, cond = NULL_TREE, expr = NULL_TREE;
   int count = 0;
+  switch (TREE_CODE (t))
+    {
+    case LOOP_EXPR:
+      body = LOOP_EXPR_BODY (t);
+      break;
+    case DO_STMT:
+      body = DO_BODY (t);
+      cond = DO_COND (t);
+      break;
+    case WHILE_STMT:
+      body = WHILE_BODY (t);
+      cond = WHILE_COND (t);
+      count = -1;
+      break;
+    case FOR_STMT:
+      if (FOR_INIT_STMT (t))
+	cxx_eval_constant_expression (ctx, FOR_INIT_STMT (t), /*lval*/false,
+				      non_constant_p, overflow_p, jump_target);
+      if (*non_constant_p)
+	return NULL_TREE;
+      body = FOR_BODY (t);
+      cond = FOR_COND (t);
+      expr = FOR_EXPR (t);
+      count = -1;
+      break;
+    default:
+      gcc_unreachable ();
+    }
+  hash_set<tree> save_exprs;
+  new_ctx.save_exprs = &save_exprs;
   do
     {
-      hash_set<tree> save_exprs;
-      new_ctx.save_exprs = &save_exprs;
+      if (count != -1)
+	{
+	  if (body)
+	    cxx_eval_constant_expression (&new_ctx, body, /*lval*/false,
+					  non_constant_p, overflow_p,
+					  jump_target);
+	  if (breaks (jump_target))
+	    {
+	      *jump_target = NULL_TREE;
+	      break;
+	    }
 
-      cxx_eval_constant_expression (&new_ctx, body, /*lval*/false,
-				    non_constant_p, overflow_p, jump_target);
+	  if (TREE_CODE (t) != LOOP_EXPR && continues (jump_target))
+	    *jump_target = NULL_TREE;
+
+	  if (expr)
+	    cxx_eval_constant_expression (&new_ctx, expr, /*lval*/false,
+					  non_constant_p, overflow_p,
+					  jump_target);
+	}
+
+      if (cond)
+	{
+	  tree res
+	    = cxx_eval_constant_expression (&new_ctx, cond, /*lval*/false,
+					    non_constant_p, overflow_p,
+					    jump_target);
+	  if (res)
+	    {
+	      if (verify_constant (res, ctx->quiet, non_constant_p,
+				   overflow_p))
+		break;
+	      if (integer_zerop (res))
+		break;
+	    }
+	  else
+	    gcc_assert (*jump_target);
+	}
 
       /* Forget saved values of SAVE_EXPRs.  */
       for (hash_set<tree>::iterator iter = save_exprs.begin();
 	   iter != save_exprs.end(); ++iter)
 	new_ctx.values->remove (*iter);
+
       if (++count >= constexpr_loop_limit)
 	{
 	  if (!ctx->quiet)
@@ -4146,11 +4290,14 @@ cxx_eval_loop_expr (const constexpr_ctx
     }
   while (!returns (jump_target)
 	 && !breaks (jump_target)
-	 && !switches (jump_target)
+	 && !continues (jump_target)
+	 && (!switches (jump_target) || count == 0)
 	 && !*non_constant_p);
 
-  if (breaks (jump_target))
-    *jump_target = NULL_TREE;
+  /* Forget saved values of SAVE_EXPRs.  */
+  for (hash_set<tree>::iterator iter = save_exprs.begin();
+       iter != save_exprs.end(); ++iter)
+    new_ctx.values->remove (*iter);
 
   return NULL_TREE;
 }
@@ -4163,13 +4310,15 @@ cxx_eval_switch_expr (const constexpr_ct
 		      bool *non_constant_p, bool *overflow_p,
 		      tree *jump_target)
 {
-  tree cond = SWITCH_COND (t);
+  tree cond
+    = TREE_CODE (t) == SWITCH_STMT ? SWITCH_STMT_COND (t) : SWITCH_COND (t);
   cond = cxx_eval_constant_expression (ctx, cond, false,
 				       non_constant_p, overflow_p);
   VERIFY_CONSTANT (cond);
   *jump_target = cond;
 
-  tree body = SWITCH_BODY (t);
+  tree body
+    = TREE_CODE (t) == SWITCH_STMT ? SWITCH_STMT_BODY (t) : SWITCH_BODY (t);
   constexpr_ctx new_ctx = *ctx;
   constexpr_switch_state css = css_default_not_seen;
   new_ctx.css_state = &css;
@@ -4245,6 +4394,10 @@ cxx_eval_constant_expression (const cons
 	case STATEMENT_LIST:
 	case LOOP_EXPR:
 	case COND_EXPR:
+	case IF_STMT:
+	case DO_STMT:
+	case WHILE_STMT:
+	case FOR_STMT:
 	  break;
 	case LABEL_EXPR:
 	case CASE_LABEL_EXPR:
@@ -4389,6 +4542,11 @@ cxx_eval_constant_expression (const cons
     case DECL_EXPR:
       {
 	r = DECL_EXPR_DECL (t);
+	if (TREE_CODE (r) == USING_DECL)
+	  {
+	    r = void_node;
+	    break;
+	  }
 	if (AGGREGATE_TYPE_P (TREE_TYPE (r))
 	    || VECTOR_TYPE_P (TREE_TYPE (r)))
 	  {
@@ -4476,6 +4634,9 @@ cxx_eval_constant_expression (const cons
 	r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
 					  lval,
 					  non_constant_p, overflow_p);
+      /* FALLTHRU */
+    case BREAK_STMT:
+    case CONTINUE_STMT:
       if (jump_target)
 	*jump_target = t;
       else
@@ -4526,6 +4687,17 @@ cxx_eval_constant_expression (const cons
 				      jump_target);
       break;
 
+    case CLEANUP_STMT:
+      r = cxx_eval_constant_expression (ctx, CLEANUP_BODY (t), lval,
+					non_constant_p, overflow_p,
+					jump_target);
+      if (!CLEANUP_EH_ONLY (t) && !*non_constant_p)
+	/* Also evaluate the cleanup.  */
+	cxx_eval_constant_expression (ctx, CLEANUP_EXPR (t), true,
+				      non_constant_p, overflow_p,
+				      jump_target);
+      break;
+
       /* These differ from cxx_eval_unary_expression in that this doesn't
 	 check for a constant operand or result; an address can be
 	 constant without its operand being, and vice versa.  */
@@ -4581,8 +4753,9 @@ cxx_eval_constant_expression (const cons
       break;
 
     case SIZEOF_EXPR:
-      r = fold_sizeof_expr (t);
-      VERIFY_CONSTANT (r);
+      r = cxx_eval_constant_expression (ctx, fold_sizeof_expr (t), lval,
+					non_constant_p, overflow_p,
+					jump_target);
       break;
 
     case COMPOUND_EXPR:
@@ -4702,16 +4875,18 @@ cxx_eval_constant_expression (const cons
       break;
 
     case COND_EXPR:
+    case IF_STMT:
       if (jump_target && *jump_target)
 	{
 	  tree orig_jump = *jump_target;
+	  tree arg = ((TREE_CODE (t) != IF_STMT || TREE_OPERAND (t, 1))
+		      ? TREE_OPERAND (t, 1) : void_node);
 	  /* When jumping to a label, the label might be either in the
 	     then or else blocks, so process then block first in skipping
 	     mode first, and if we are still in the skipping mode at its end,
 	     process the else block too.  */
-	  r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
-					    lval, non_constant_p, overflow_p,
-					    jump_target);
+	  r = cxx_eval_constant_expression (ctx, arg, lval, non_constant_p,
+					    overflow_p, jump_target);
 	  /* It's possible that we found the label in the then block.  But
 	     it could have been followed by another jumping statement, e.g.
 	     say we're looking for case 1:
@@ -4725,9 +4900,12 @@ cxx_eval_constant_expression (const cons
 	     in which case we need not go looking to the else block.
 	     (goto is not allowed in a constexpr function.)  */
 	  if (*jump_target == orig_jump)
-	    r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 2),
-					      lval, non_constant_p, overflow_p,
-					      jump_target);
+	    {
+	      arg = ((TREE_CODE (t) != IF_STMT || TREE_OPERAND (t, 2))
+		     ? TREE_OPERAND (t, 2) : void_node);
+	      r = cxx_eval_constant_expression (ctx, arg, lval, non_constant_p,
+						overflow_p, jump_target);
+	    }
 	  break;
 	}
       r = cxx_eval_conditional_expression (ctx, t, lval,
@@ -4971,11 +5149,15 @@ cxx_eval_constant_expression (const cons
       break;
 
     case LOOP_EXPR:
+    case DO_STMT:
+    case WHILE_STMT:
+    case FOR_STMT:
       cxx_eval_loop_expr (ctx, t,
 			  non_constant_p, overflow_p, jump_target);
       break;
 
     case SWITCH_EXPR:
+    case SWITCH_STMT:
       cxx_eval_switch_expr (ctx, t,
 			    non_constant_p, overflow_p, jump_target);
       break;
--- gcc/cp/lambda.c.jj	2019-02-20 10:00:26.360875922 +0100
+++ gcc/cp/lambda.c	2019-02-20 22:27:48.657760689 +0100
@@ -1087,8 +1087,7 @@ maybe_add_lambda_conv_op (tree type)
   tree optype = TREE_TYPE (callop);
   tree fn_result = TREE_TYPE (optype);
 
-  tree thisarg = build_nop (TREE_TYPE (DECL_ARGUMENTS (callop)),
-			    null_pointer_node);
+  tree thisarg = build_int_cst (TREE_TYPE (DECL_ARGUMENTS (callop)), 0);
   if (generic_lambda_p)
     {
       ++processing_template_decl;
--- gcc/testsuite/g++.dg/ubsan/vptr-4.C.jj	2019-02-20 10:00:25.648887822 +0100
+++ gcc/testsuite/g++.dg/ubsan/vptr-4.C	2019-02-20 22:27:48.670760476 +0100
@@ -15,11 +15,11 @@ struct T : S {
   int b;
   int g() { return 0; }
   virtual int v() { return 1; }
-  constexpr const T *foo() { return (const T *) reinterpret_cast<const S *> (this); }
+  constexpr const T *foo() { return (const T *) reinterpret_cast<const S *> (this); } // { dg-error "is not a constant expression" }
 };
 
 constexpr T t;
-constexpr const T *p = t.foo ();
+constexpr const T *p = t.foo ();	// { dg-message "expansion of" }
 
 template <typename U>
 struct V {
@@ -39,16 +39,17 @@ struct W : V<U> {
 };
 
 constexpr W<int> w;
-constexpr const W<int> *s = w.foo ();
+constexpr const W<int> *s = w.foo ();	// { dg-error "is not a constant expression" }
+// { dg-message "expansion of" "" { target *-*-* } .-1 }
 
 template <typename U>
 int foo (void)
 {
   static constexpr T t;
-  static constexpr const T *p = t.foo ();
+  static constexpr const T *p = t.foo ();	// { dg-message "expansion of" }
   static constexpr W<U> w;
-  static constexpr const W<U> *s = w.foo ();
-  return t.b + w.b;
+  static constexpr const W<U> *s = w.foo ();	// { dg-error "is not a constant expression" }
+  return t.b + w.b;				// { dg-message "expansion of" "" { target *-*-* } .-1 }
 }
 
 int x = foo <char> ();
--- gcc/testsuite/g++.dg/cpp1y/constexpr-84192.C.jj	2019-02-20 10:00:25.456891030 +0100
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-84192.C	2019-02-20 22:27:48.686760214 +0100
@@ -12,7 +12,7 @@ void
 f2 ()
 { 
   for (;;)
-    constexpr bool b = ({ break; false; }) && false;	// { dg-error "statement is not a constant expression" }
+    constexpr bool b = ({ break; false; }) && false;	// { dg-error "is not a constant expression" }
 }
 
 constexpr bool
--- gcc/testsuite/g++.dg/cpp1y/constexpr-70265-2.C.jj	2019-02-20 10:00:25.511890111 +0100
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-70265-2.C	2019-02-20 22:27:48.687760198 +0100
@@ -5,8 +5,8 @@ constexpr int
 foo (int p)
 {
   int t = 0;
-  while (1)
-    t = 0;  // { dg-error "count exceeds" }
+  while (1)  // { dg-error "count exceeds" }
+    t = 0;
   return t;
 }
 
--- gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C.jj	2019-02-20 22:27:48.687760198 +0100
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C	2019-02-20 22:27:48.687760198 +0100
@@ -0,0 +1,20 @@
+// PR c++/89285
+// { dg-do compile { target c++14 } }
+
+struct A {
+  int a {};
+};
+struct B {
+  int b {};
+  constexpr B (A *x) {
+    int *c = &x->a;
+    while (*c)
+      c = reinterpret_cast<int *>((reinterpret_cast<char *>(c) + *c));
+    *c = reinterpret_cast<char *>(this) - reinterpret_cast<char *>(c);	// { dg-error "reinterpret_cast" }
+  }
+};
+struct C : A {
+  B bar {this};
+};
+
+constexpr C foo {};	// { dg-message "expansion of" }
--- gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow.C.jj	2016-06-08 21:01:25.183267339 +0200
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow.C	2019-02-20 22:36:59.090753148 +0100
@@ -27,6 +27,7 @@
 
 #define Assert(expr) static_assert ((expr), #expr)
 
+#if __cplusplus >= 201402L
 template <class T>
 constexpr T add (T x, T y, T z = T ())
 {
@@ -48,6 +49,11 @@ constexpr T mul (T x, T y, T z = T ())
 #define TEST_ADD(T, x, y, z) Assert (z == add<T>(x, y))
 #define TEST_SUB(T, x, y, z) Assert (z == sub<T>(x, y))
 #define TEST_MUL(T, x, y, z) Assert (z == mul<T>(x, y))
+#else
+#define TEST_ADD(T, x, y, z) Assert (true)
+#define TEST_SUB(T, x, y, z) Assert (true)
+#define TEST_MUL(T, x, y, z) Assert (true)
+#endif
 
 
 TEST_ADD (signed char,  0,         0,         0);
--- gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow2.C.jj	2019-02-20 22:37:10.891560033 +0100
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow2.C	2019-02-20 22:44:53.661984392 +0100
@@ -0,0 +1,31 @@
+// PR c++/70507 - integer overflow builtins not constant expressions
+// The constexpr-arith-overflow.C testcase covers this for C++14 and later.
+// { dg-do compile }
+// { dg-options "-std=c++11" }
+
+#define Assert(expr) static_assert ((expr), #expr)
+
+template <class T>
+constexpr T add (T x, T y, T z = T ())
+{
+  return __builtin_add_overflow (x, y, &z) ? 0 : z;	// { dg-error "is not a constant expression" }
+}
+
+template <class T>
+constexpr T sub (T x, T y, T z = T ())
+{
+  return __builtin_sub_overflow (x, y, &z) ? 0 : z;	// { dg-error "is not a constant expression" }
+}
+
+template <class T>
+constexpr T mul (T x, T y, T z = T ())
+{
+  return __builtin_mul_overflow (x, y, &z) ? 0 : z;	// { dg-error "is not a constant expression" }
+}
+
+Assert (0 == add<int>(0, 0));	// { dg-error "non-constant condition for static assertion" }
+Assert (0 == sub<int>(0, 0));	// { dg-error "non-constant condition for static assertion" }
+Assert (0 == mul<int>(0, 0));	// { dg-error "non-constant condition for static assertion" }
+// { dg-message "expansion of" "" { target *-*-* } .-3 }
+// { dg-message "expansion of" "" { target *-*-* } .-3 }
+// { dg-message "expansion of" "" { target *-*-* } .-3 }


	Jakub

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [C++ PATCH] Ensure constexpr evaluation is done on pre-cp_fold_function bodies (PR c++/89285, take 3)
  2019-02-20 22:16       ` [C++ PATCH] Ensure constexpr evaluation is done on pre-cp_fold_function bodies (PR c++/89285, take 3) Jakub Jelinek
@ 2019-02-21  0:00         ` Jason Merrill
  2019-02-21  9:47           ` [C++ PATCH] Ensure constexpr evaluation is done on pre-cp_fold_function bodies (PR c++/89285, take 4) Jakub Jelinek
  0 siblings, 1 reply; 9+ messages in thread
From: Jason Merrill @ 2019-02-21  0:00 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 2/20/19 11:52 AM, Jakub Jelinek wrote:
> On Wed, Feb 20, 2019 at 11:12:07AM -1000, Jason Merrill wrote:
>>> (z = REALPART_EXPR <SAVE_EXPR <.ADD_OVERFLOW (0, 0)>>;, (bool) IMAGPART_EXPR <SAVE_EXPR <.ADD_OVERFLOW (0, 0)>>;);
>>> which is not potential_constant_expression, but nothing called it again
>>> and cxx_eval_constant_expression can handle it.
>>
>> Yeah, that seems like a bug; C++11 shouldn't allow modification of z this
>> way either.
> 
> Ack.  The following version stops accepting that for C++11, but keeps
> accepting for C++14 and later (so for that still needs to test
> potential_constant_expression instead of is_constant_expression, plus
> added a new inline function so that the code is more readable).

Can we unconditionally change is_constant_expression to 
potential_constant_expression there?

Jason

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [C++ PATCH] Ensure constexpr evaluation is done on pre-cp_fold_function bodies (PR c++/89285, take 4)
  2019-02-21  0:00         ` Jason Merrill
@ 2019-02-21  9:47           ` Jakub Jelinek
  2019-02-21 21:36             ` Jason Merrill
  0 siblings, 1 reply; 9+ messages in thread
From: Jakub Jelinek @ 2019-02-21  9:47 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches

On Wed, Feb 20, 2019 at 01:58:58PM -1000, Jason Merrill wrote:
> On 2/20/19 11:52 AM, Jakub Jelinek wrote:
> > On Wed, Feb 20, 2019 at 11:12:07AM -1000, Jason Merrill wrote:
> > > > (z = REALPART_EXPR <SAVE_EXPR <.ADD_OVERFLOW (0, 0)>>;, (bool) IMAGPART_EXPR <SAVE_EXPR <.ADD_OVERFLOW (0, 0)>>;);
> > > > which is not potential_constant_expression, but nothing called it again
> > > > and cxx_eval_constant_expression can handle it.
> > > 
> > > Yeah, that seems like a bug; C++11 shouldn't allow modification of z this
> > > way either.
> > 
> > Ack.  The following version stops accepting that for C++11, but keeps
> > accepting for C++14 and later (so for that still needs to test
> > potential_constant_expression instead of is_constant_expression, plus
> > added a new inline function so that the code is more readable).
> 
> Can we unconditionally change is_constant_expression to
> potential_constant_expression there?

Yes, passed bootstrap/regtest on x86_64-linux and i686-linux that way
and when I've tried to construct some builtin where it could make a
difference (e.g. __builtin_memcpy (&a, "b", 1);) it still didn't make a
difference.  So is this version ok for trunk?

2019-02-21  Jakub Jelinek  <jakub@redhat.com>

	PR c++/89285
	* builtins.c (fold_builtin_arith_overflow): If first two args are
	INTEGER_CSTs, set intres and ovfres to constants rather than calls
	to ifn.

	* constexpr.c (struct constexpr_fundef): Add parms and result members.
	(retrieve_constexpr_fundef): Adjust for the above change.
	(register_constexpr_fundef): Save constexpr body with copy_fn,
	temporarily set DECL_CONTEXT on DECL_RESULT before that.
	(get_fundef_copy): Change FUN argument to FUNDEF with
	constexpr_fundef * type, grab body and parms/result out of
	constexpr_fundef struct and temporarily change it for copy_fn calls
	too.
	(cxx_eval_builtin_function_call): For __builtin_FUNCTION temporarily
	adjust current_function_decl from ctx->call context.  Test
	!potential_constant_expression instead of !is_constant_expression.
	(cxx_bind_parameters_in_call): Grab parameters from new_call.  Undo
	convert_for_arg_passing changes for TREE_ADDRESSABLE type passing.
	(cxx_eval_call_expression): Adjust get_fundef_copy caller.
	(cxx_eval_conditional_expression): For IF_STMT, allow then or else
	operands to be NULL.
	(label_matches): Handle BREAK_STMT and CONTINUE_STMT.
	(cxx_eval_loop_expr): Add support for FOR_STMT, WHILE_STMT and DO_STMT.
	(cxx_eval_switch_expr): Add support for SWITCH_STMT.
	(cxx_eval_constant_expression): Handle IF_STMT, FOR_STMT, WHILE_STMT,
	DO_STMT, CONTINUE_STMT, SWITCH_STMT, BREAK_STMT and CONTINUE_STMT.
	For SIZEOF_EXPR, recurse on the result of fold_sizeof_expr.  Ignore
	DECL_EXPR with USING_DECL operand.
	* lambda.c (maybe_add_lambda_conv_op): Build thisarg using
	build_int_cst to make it a valid constant expression.

	* g++.dg/ubsan/vptr-4.C: Expect reinterpret_cast errors.
	* g++.dg/cpp1y/constexpr-84192.C (f2): Adjust expected diagnostics.
	* g++.dg/cpp1y/constexpr-70265-2.C (foo): Adjust expected line of
	diagnostics.
	* g++.dg/cpp1y/constexpr-89285.C: New test.
	* g++.dg/cpp0x/constexpr-arith-overflow.C (add, sub, mul): Ifdef out
	for C++11.
	(TEST_ADD, TEST_SUB, TEST_MUL): Define to Assert (true) for C++11.
	* g++.dg/cpp0x/constexpr-arith-overflow2.C: New test.

--- gcc/builtins.c.jj	2019-02-20 23:40:16.286153862 +0100
+++ gcc/builtins.c	2019-02-21 01:10:15.130407041 +0100
@@ -9302,8 +9302,7 @@ fold_builtin_arith_overflow (location_t
 			     tree arg0, tree arg1, tree arg2)
 {
   enum internal_fn ifn = IFN_LAST;
-  /* The code of the expression corresponding to the type-generic
-     built-in, or ERROR_MARK for the type-specific ones.  */
+  /* The code of the expression corresponding to the built-in.  */
   enum tree_code opcode = ERROR_MARK;
   bool ovf_only = false;
 
@@ -9313,42 +9312,39 @@ fold_builtin_arith_overflow (location_t
       ovf_only = true;
       /* FALLTHRU */
     case BUILT_IN_ADD_OVERFLOW:
-      opcode = PLUS_EXPR;
-      /* FALLTHRU */
     case BUILT_IN_SADD_OVERFLOW:
     case BUILT_IN_SADDL_OVERFLOW:
     case BUILT_IN_SADDLL_OVERFLOW:
     case BUILT_IN_UADD_OVERFLOW:
     case BUILT_IN_UADDL_OVERFLOW:
     case BUILT_IN_UADDLL_OVERFLOW:
+      opcode = PLUS_EXPR;
       ifn = IFN_ADD_OVERFLOW;
       break;
     case BUILT_IN_SUB_OVERFLOW_P:
       ovf_only = true;
       /* FALLTHRU */
     case BUILT_IN_SUB_OVERFLOW:
-      opcode = MINUS_EXPR;
-      /* FALLTHRU */
     case BUILT_IN_SSUB_OVERFLOW:
     case BUILT_IN_SSUBL_OVERFLOW:
     case BUILT_IN_SSUBLL_OVERFLOW:
     case BUILT_IN_USUB_OVERFLOW:
     case BUILT_IN_USUBL_OVERFLOW:
     case BUILT_IN_USUBLL_OVERFLOW:
+      opcode = MINUS_EXPR;
       ifn = IFN_SUB_OVERFLOW;
       break;
     case BUILT_IN_MUL_OVERFLOW_P:
       ovf_only = true;
       /* FALLTHRU */
     case BUILT_IN_MUL_OVERFLOW:
-      opcode = MULT_EXPR;
-      /* FALLTHRU */
     case BUILT_IN_SMUL_OVERFLOW:
     case BUILT_IN_SMULL_OVERFLOW:
     case BUILT_IN_SMULLL_OVERFLOW:
     case BUILT_IN_UMUL_OVERFLOW:
     case BUILT_IN_UMULL_OVERFLOW:
     case BUILT_IN_UMULLL_OVERFLOW:
+      opcode = MULT_EXPR;
       ifn = IFN_MUL_OVERFLOW;
       break;
     default:
@@ -9373,13 +9369,27 @@ fold_builtin_arith_overflow (location_t
 				 ? boolean_true_node : boolean_false_node,
 				 arg2);
 
-  tree ctype = build_complex_type (type);
-  tree call = build_call_expr_internal_loc (loc, ifn, ctype,
-					    2, arg0, arg1);
-  tree tgt = save_expr (call);
-  tree intres = build1_loc (loc, REALPART_EXPR, type, tgt);
-  tree ovfres = build1_loc (loc, IMAGPART_EXPR, type, tgt);
-  ovfres = fold_convert_loc (loc, boolean_type_node, ovfres);
+  tree intres, ovfres;
+  if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST)
+    {
+      intres = fold_binary_loc (loc, opcode, type,
+				fold_convert_loc (loc, type, arg0),
+				fold_convert_loc (loc, type, arg1));
+      if (TREE_OVERFLOW (intres))
+	intres = drop_tree_overflow (intres);
+      ovfres = (arith_overflowed_p (opcode, type, arg0, arg1)
+		? boolean_true_node : boolean_false_node);
+    }
+  else
+    {
+      tree ctype = build_complex_type (type);
+      tree call = build_call_expr_internal_loc (loc, ifn, ctype, 2,
+						arg0, arg1);
+      tree tgt = save_expr (call);
+      intres = build1_loc (loc, REALPART_EXPR, type, tgt);
+      ovfres = build1_loc (loc, IMAGPART_EXPR, type, tgt);
+      ovfres = fold_convert_loc (loc, boolean_type_node, ovfres);
+    }
 
   if (ovf_only)
     return omit_one_operand_loc (loc, boolean_type_node, ovfres, arg2);
--- gcc/cp/constexpr.c.jj	2019-02-20 23:40:16.302153600 +0100
+++ gcc/cp/constexpr.c	2019-02-21 01:10:41.188983408 +0100
@@ -139,6 +139,8 @@ ensure_literal_type_for_constexpr_object
 struct GTY((for_user)) constexpr_fundef {
   tree decl;
   tree body;
+  tree parms;
+  tree result;
 };
 
 struct constexpr_fundef_hasher : ggc_ptr_hash<constexpr_fundef>
@@ -176,11 +178,10 @@ constexpr_fundef_hasher::hash (constexpr
 static constexpr_fundef *
 retrieve_constexpr_fundef (tree fun)
 {
-  constexpr_fundef fundef = { NULL, NULL };
   if (constexpr_fundef_table == NULL)
     return NULL;
 
-  fundef.decl = fun;
+  constexpr_fundef fundef = { fun, NULL, NULL, NULL };
   return constexpr_fundef_table->find (&fundef);
 }
 
@@ -897,8 +898,19 @@ register_constexpr_fundef (tree fun, tre
       = hash_table<constexpr_fundef_hasher>::create_ggc (101);
 
   entry.decl = fun;
-  entry.body = body;
+  tree saved_fn = current_function_decl;
+  bool clear_ctx = false;
+  current_function_decl = fun;
+  if (DECL_RESULT (fun) && DECL_CONTEXT (DECL_RESULT (fun)) == NULL_TREE)
+    {
+      clear_ctx = true;
+      DECL_CONTEXT (DECL_RESULT (fun)) = fun;
+    }
+  entry.body = copy_fn (fun, entry.parms, entry.result);
+  current_function_decl = saved_fn;
   slot = constexpr_fundef_table->find_slot (&entry, INSERT);
+  if (clear_ctx)
+    DECL_CONTEXT (DECL_RESULT (fun)) = NULL_TREE;
 
   gcc_assert (*slot == NULL);
   *slot = ggc_alloc<constexpr_fundef> ();
@@ -1114,27 +1126,40 @@ maybe_initialize_fundef_copies_table ()
    is parms, TYPE is result.  */
 
 static tree
-get_fundef_copy (tree fun)
+get_fundef_copy (constexpr_fundef *fundef)
 {
   maybe_initialize_fundef_copies_table ();
 
   tree copy;
   bool existed;
-  tree *slot = &fundef_copies_table->get_or_insert (fun, &existed);
+  tree *slot = &fundef_copies_table->get_or_insert (fundef->decl, &existed);
 
   if (!existed)
     {
       /* There is no cached function available, or in use.  We can use
 	 the function directly.  That the slot is now created records
 	 that this function is now in use.  */
-      copy = build_tree_list (DECL_SAVED_TREE (fun), DECL_ARGUMENTS (fun));
-      TREE_TYPE (copy) = DECL_RESULT (fun);
+      copy = build_tree_list (fundef->body, fundef->parms);
+      TREE_TYPE (copy) = fundef->result;
     }
   else if (*slot == NULL_TREE)
     {
       /* We've already used the function itself, so make a copy.  */
       copy = build_tree_list (NULL, NULL);
-      TREE_PURPOSE (copy) = copy_fn (fun, TREE_VALUE (copy), TREE_TYPE (copy));
+      tree saved_body = DECL_SAVED_TREE (fundef->decl);
+      tree saved_parms = DECL_ARGUMENTS (fundef->decl);
+      tree saved_result = DECL_RESULT (fundef->decl);
+      tree saved_fn = current_function_decl;
+      DECL_SAVED_TREE (fundef->decl) = fundef->body;
+      DECL_ARGUMENTS (fundef->decl) = fundef->parms;
+      DECL_RESULT (fundef->decl) = fundef->result;
+      current_function_decl = fundef->decl;
+      TREE_PURPOSE (copy) = copy_fn (fundef->decl, TREE_VALUE (copy),
+				     TREE_TYPE (copy));
+      current_function_decl = saved_fn;
+      DECL_RESULT (fundef->decl) = saved_result;
+      DECL_ARGUMENTS (fundef->decl) = saved_parms;
+      DECL_SAVED_TREE (fundef->decl) = saved_body;
     }
   else
     {
@@ -1245,8 +1270,15 @@ cxx_eval_builtin_function_call (const co
 
   bool save_ffbcp = force_folding_builtin_constant_p;
   force_folding_builtin_constant_p = true;
+  tree save_cur_fn = current_function_decl;
+  /* Return name of ctx->call->fundef->decl for __builtin_FUNCTION ().  */
+  if (fndecl_built_in_p (fun, BUILT_IN_FUNCTION)
+      && ctx->call
+      && ctx->call->fundef)
+    current_function_decl = ctx->call->fundef->decl;
   new_call = fold_builtin_call_array (EXPR_LOCATION (t), TREE_TYPE (t),
 				      CALL_EXPR_FN (t), nargs, args);
+  current_function_decl = save_cur_fn;
   force_folding_builtin_constant_p = save_ffbcp;
   if (new_call == NULL)
     {
@@ -1269,7 +1301,7 @@ cxx_eval_builtin_function_call (const co
       return t;
     }
 
-  if (!is_constant_expression (new_call))
+  if (!potential_constant_expression (new_call))
     {
       if (!*non_constant_p && !ctx->quiet)
 	error ("%q+E is not a constant expression", new_call);
@@ -1341,7 +1373,7 @@ cxx_bind_parameters_in_call (const const
 {
   const int nargs = call_expr_nargs (t);
   tree fun = new_call->fundef->decl;
-  tree parms = DECL_ARGUMENTS (fun);
+  tree parms = new_call->fundef->parms;
   int i;
   tree *p = &new_call->bindings;
   for (i = 0; i < nargs; ++i)
@@ -1358,6 +1390,9 @@ cxx_bind_parameters_in_call (const const
 	  x = ctx->object;
 	  x = build_address (x);
 	}
+      if (TREE_ADDRESSABLE (type))
+	/* Undo convert_for_arg_passing work here.  */
+	x = convert_from_reference (x);
       arg = cxx_eval_constant_expression (ctx, x, /*lval=*/false,
 					  non_constant_p, overflow_p);
       /* Don't VERIFY_CONSTANT here.  */
@@ -1761,7 +1796,7 @@ cxx_eval_call_expression (const constexp
 	  tree body, parms, res;
 
 	  /* Reuse or create a new unshared copy of this function's body.  */
-	  tree copy = get_fundef_copy (fun);
+	  tree copy = get_fundef_copy (new_call.fundef);
 	  body = TREE_PURPOSE (copy);
 	  parms = TREE_VALUE (copy);
 	  res = TREE_TYPE (copy);
@@ -2223,14 +2258,13 @@ cxx_eval_conditional_expression (const c
   VERIFY_CONSTANT (val);
   /* Don't VERIFY_CONSTANT the other operands.  */
   if (integer_zerop (val))
-    return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 2),
-					 lval,
-					 non_constant_p, overflow_p,
-					 jump_target);
-  return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
-				       lval,
-				       non_constant_p, overflow_p,
-				       jump_target);
+    val = TREE_OPERAND (t, 2);
+  else
+    val = TREE_OPERAND (t, 1);
+  if (TREE_CODE (t) == IF_STMT && !val)
+    val = void_node;
+  return cxx_eval_constant_expression (ctx, val, lval, non_constant_p,
+				       overflow_p, jump_target);
 }
 
 /* Subroutine of cxx_eval_constant_expression.
@@ -4059,6 +4093,12 @@ label_matches (const constexpr_ctx *ctx,
 	}
       break;
 
+    case BREAK_STMT:
+    case CONTINUE_STMT:
+      /* These two are handled directly in cxx_eval_loop_expr by testing
+	 breaks (jump_target) or continues (jump_target).  */
+      break;
+
     default:
       gcc_unreachable ();
     }
@@ -4119,20 +4159,84 @@ cxx_eval_loop_expr (const constexpr_ctx
 {
   constexpr_ctx new_ctx = *ctx;
 
-  tree body = TREE_OPERAND (t, 0);
+  tree body, cond = NULL_TREE, expr = NULL_TREE;
   int count = 0;
+  switch (TREE_CODE (t))
+    {
+    case LOOP_EXPR:
+      body = LOOP_EXPR_BODY (t);
+      break;
+    case DO_STMT:
+      body = DO_BODY (t);
+      cond = DO_COND (t);
+      break;
+    case WHILE_STMT:
+      body = WHILE_BODY (t);
+      cond = WHILE_COND (t);
+      count = -1;
+      break;
+    case FOR_STMT:
+      if (FOR_INIT_STMT (t))
+	cxx_eval_constant_expression (ctx, FOR_INIT_STMT (t), /*lval*/false,
+				      non_constant_p, overflow_p, jump_target);
+      if (*non_constant_p)
+	return NULL_TREE;
+      body = FOR_BODY (t);
+      cond = FOR_COND (t);
+      expr = FOR_EXPR (t);
+      count = -1;
+      break;
+    default:
+      gcc_unreachable ();
+    }
+  hash_set<tree> save_exprs;
+  new_ctx.save_exprs = &save_exprs;
   do
     {
-      hash_set<tree> save_exprs;
-      new_ctx.save_exprs = &save_exprs;
+      if (count != -1)
+	{
+	  if (body)
+	    cxx_eval_constant_expression (&new_ctx, body, /*lval*/false,
+					  non_constant_p, overflow_p,
+					  jump_target);
+	  if (breaks (jump_target))
+	    {
+	      *jump_target = NULL_TREE;
+	      break;
+	    }
 
-      cxx_eval_constant_expression (&new_ctx, body, /*lval*/false,
-				    non_constant_p, overflow_p, jump_target);
+	  if (TREE_CODE (t) != LOOP_EXPR && continues (jump_target))
+	    *jump_target = NULL_TREE;
+
+	  if (expr)
+	    cxx_eval_constant_expression (&new_ctx, expr, /*lval*/false,
+					  non_constant_p, overflow_p,
+					  jump_target);
+	}
+
+      if (cond)
+	{
+	  tree res
+	    = cxx_eval_constant_expression (&new_ctx, cond, /*lval*/false,
+					    non_constant_p, overflow_p,
+					    jump_target);
+	  if (res)
+	    {
+	      if (verify_constant (res, ctx->quiet, non_constant_p,
+				   overflow_p))
+		break;
+	      if (integer_zerop (res))
+		break;
+	    }
+	  else
+	    gcc_assert (*jump_target);
+	}
 
       /* Forget saved values of SAVE_EXPRs.  */
       for (hash_set<tree>::iterator iter = save_exprs.begin();
 	   iter != save_exprs.end(); ++iter)
 	new_ctx.values->remove (*iter);
+
       if (++count >= constexpr_loop_limit)
 	{
 	  if (!ctx->quiet)
@@ -4146,11 +4250,14 @@ cxx_eval_loop_expr (const constexpr_ctx
     }
   while (!returns (jump_target)
 	 && !breaks (jump_target)
-	 && !switches (jump_target)
+	 && !continues (jump_target)
+	 && (!switches (jump_target) || count == 0)
 	 && !*non_constant_p);
 
-  if (breaks (jump_target))
-    *jump_target = NULL_TREE;
+  /* Forget saved values of SAVE_EXPRs.  */
+  for (hash_set<tree>::iterator iter = save_exprs.begin();
+       iter != save_exprs.end(); ++iter)
+    new_ctx.values->remove (*iter);
 
   return NULL_TREE;
 }
@@ -4163,13 +4270,15 @@ cxx_eval_switch_expr (const constexpr_ct
 		      bool *non_constant_p, bool *overflow_p,
 		      tree *jump_target)
 {
-  tree cond = SWITCH_COND (t);
+  tree cond
+    = TREE_CODE (t) == SWITCH_STMT ? SWITCH_STMT_COND (t) : SWITCH_COND (t);
   cond = cxx_eval_constant_expression (ctx, cond, false,
 				       non_constant_p, overflow_p);
   VERIFY_CONSTANT (cond);
   *jump_target = cond;
 
-  tree body = SWITCH_BODY (t);
+  tree body
+    = TREE_CODE (t) == SWITCH_STMT ? SWITCH_STMT_BODY (t) : SWITCH_BODY (t);
   constexpr_ctx new_ctx = *ctx;
   constexpr_switch_state css = css_default_not_seen;
   new_ctx.css_state = &css;
@@ -4245,6 +4354,10 @@ cxx_eval_constant_expression (const cons
 	case STATEMENT_LIST:
 	case LOOP_EXPR:
 	case COND_EXPR:
+	case IF_STMT:
+	case DO_STMT:
+	case WHILE_STMT:
+	case FOR_STMT:
 	  break;
 	case LABEL_EXPR:
 	case CASE_LABEL_EXPR:
@@ -4389,6 +4502,11 @@ cxx_eval_constant_expression (const cons
     case DECL_EXPR:
       {
 	r = DECL_EXPR_DECL (t);
+	if (TREE_CODE (r) == USING_DECL)
+	  {
+	    r = void_node;
+	    break;
+	  }
 	if (AGGREGATE_TYPE_P (TREE_TYPE (r))
 	    || VECTOR_TYPE_P (TREE_TYPE (r)))
 	  {
@@ -4476,6 +4594,9 @@ cxx_eval_constant_expression (const cons
 	r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0),
 					  lval,
 					  non_constant_p, overflow_p);
+      /* FALLTHRU */
+    case BREAK_STMT:
+    case CONTINUE_STMT:
       if (jump_target)
 	*jump_target = t;
       else
@@ -4526,6 +4647,17 @@ cxx_eval_constant_expression (const cons
 				      jump_target);
       break;
 
+    case CLEANUP_STMT:
+      r = cxx_eval_constant_expression (ctx, CLEANUP_BODY (t), lval,
+					non_constant_p, overflow_p,
+					jump_target);
+      if (!CLEANUP_EH_ONLY (t) && !*non_constant_p)
+	/* Also evaluate the cleanup.  */
+	cxx_eval_constant_expression (ctx, CLEANUP_EXPR (t), true,
+				      non_constant_p, overflow_p,
+				      jump_target);
+      break;
+
       /* These differ from cxx_eval_unary_expression in that this doesn't
 	 check for a constant operand or result; an address can be
 	 constant without its operand being, and vice versa.  */
@@ -4581,8 +4713,9 @@ cxx_eval_constant_expression (const cons
       break;
 
     case SIZEOF_EXPR:
-      r = fold_sizeof_expr (t);
-      VERIFY_CONSTANT (r);
+      r = cxx_eval_constant_expression (ctx, fold_sizeof_expr (t), lval,
+					non_constant_p, overflow_p,
+					jump_target);
       break;
 
     case COMPOUND_EXPR:
@@ -4702,16 +4835,18 @@ cxx_eval_constant_expression (const cons
       break;
 
     case COND_EXPR:
+    case IF_STMT:
       if (jump_target && *jump_target)
 	{
 	  tree orig_jump = *jump_target;
+	  tree arg = ((TREE_CODE (t) != IF_STMT || TREE_OPERAND (t, 1))
+		      ? TREE_OPERAND (t, 1) : void_node);
 	  /* When jumping to a label, the label might be either in the
 	     then or else blocks, so process then block first in skipping
 	     mode first, and if we are still in the skipping mode at its end,
 	     process the else block too.  */
-	  r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1),
-					    lval, non_constant_p, overflow_p,
-					    jump_target);
+	  r = cxx_eval_constant_expression (ctx, arg, lval, non_constant_p,
+					    overflow_p, jump_target);
 	  /* It's possible that we found the label in the then block.  But
 	     it could have been followed by another jumping statement, e.g.
 	     say we're looking for case 1:
@@ -4725,9 +4860,12 @@ cxx_eval_constant_expression (const cons
 	     in which case we need not go looking to the else block.
 	     (goto is not allowed in a constexpr function.)  */
 	  if (*jump_target == orig_jump)
-	    r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 2),
-					      lval, non_constant_p, overflow_p,
-					      jump_target);
+	    {
+	      arg = ((TREE_CODE (t) != IF_STMT || TREE_OPERAND (t, 2))
+		     ? TREE_OPERAND (t, 2) : void_node);
+	      r = cxx_eval_constant_expression (ctx, arg, lval, non_constant_p,
+						overflow_p, jump_target);
+	    }
 	  break;
 	}
       r = cxx_eval_conditional_expression (ctx, t, lval,
@@ -4971,11 +5109,15 @@ cxx_eval_constant_expression (const cons
       break;
 
     case LOOP_EXPR:
+    case DO_STMT:
+    case WHILE_STMT:
+    case FOR_STMT:
       cxx_eval_loop_expr (ctx, t,
 			  non_constant_p, overflow_p, jump_target);
       break;
 
     case SWITCH_EXPR:
+    case SWITCH_STMT:
       cxx_eval_switch_expr (ctx, t,
 			    non_constant_p, overflow_p, jump_target);
       break;
--- gcc/cp/lambda.c.jj	2019-02-20 23:40:16.326153207 +0100
+++ gcc/cp/lambda.c	2019-02-21 01:10:15.132407008 +0100
@@ -1087,8 +1087,7 @@ maybe_add_lambda_conv_op (tree type)
   tree optype = TREE_TYPE (callop);
   tree fn_result = TREE_TYPE (optype);
 
-  tree thisarg = build_nop (TREE_TYPE (DECL_ARGUMENTS (callop)),
-			    null_pointer_node);
+  tree thisarg = build_int_cst (TREE_TYPE (DECL_ARGUMENTS (callop)), 0);
   if (generic_lambda_p)
     {
       ++processing_template_decl;
--- gcc/testsuite/g++.dg/ubsan/vptr-4.C.jj	2019-02-20 23:40:15.980158867 +0100
+++ gcc/testsuite/g++.dg/ubsan/vptr-4.C	2019-02-21 01:10:15.133406992 +0100
@@ -15,11 +15,11 @@ struct T : S {
   int b;
   int g() { return 0; }
   virtual int v() { return 1; }
-  constexpr const T *foo() { return (const T *) reinterpret_cast<const S *> (this); }
+  constexpr const T *foo() { return (const T *) reinterpret_cast<const S *> (this); } // { dg-error "is not a constant expression" }
 };
 
 constexpr T t;
-constexpr const T *p = t.foo ();
+constexpr const T *p = t.foo ();	// { dg-message "expansion of" }
 
 template <typename U>
 struct V {
@@ -39,16 +39,17 @@ struct W : V<U> {
 };
 
 constexpr W<int> w;
-constexpr const W<int> *s = w.foo ();
+constexpr const W<int> *s = w.foo ();	// { dg-error "is not a constant expression" }
+// { dg-message "expansion of" "" { target *-*-* } .-1 }
 
 template <typename U>
 int foo (void)
 {
   static constexpr T t;
-  static constexpr const T *p = t.foo ();
+  static constexpr const T *p = t.foo ();	// { dg-message "expansion of" }
   static constexpr W<U> w;
-  static constexpr const W<U> *s = w.foo ();
-  return t.b + w.b;
+  static constexpr const W<U> *s = w.foo ();	// { dg-error "is not a constant expression" }
+  return t.b + w.b;				// { dg-message "expansion of" "" { target *-*-* } .-1 }
 }
 
 int x = foo <char> ();
--- gcc/testsuite/g++.dg/cpp1y/constexpr-84192.C.jj	2019-02-20 23:40:16.100156905 +0100
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-84192.C	2019-02-21 01:10:15.133406992 +0100
@@ -12,7 +12,7 @@ void
 f2 ()
 { 
   for (;;)
-    constexpr bool b = ({ break; false; }) && false;	// { dg-error "statement is not a constant expression" }
+    constexpr bool b = ({ break; false; }) && false;	// { dg-error "is not a constant expression" }
 }
 
 constexpr bool
--- gcc/testsuite/g++.dg/cpp1y/constexpr-70265-2.C.jj	2019-02-20 23:40:16.154156020 +0100
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-70265-2.C	2019-02-21 01:10:15.133406992 +0100
@@ -5,8 +5,8 @@ constexpr int
 foo (int p)
 {
   int t = 0;
-  while (1)
-    t = 0;  // { dg-error "count exceeds" }
+  while (1)  // { dg-error "count exceeds" }
+    t = 0;
   return t;
 }
 
--- gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C.jj	2019-02-21 01:10:15.133406992 +0100
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-89285.C	2019-02-21 01:10:15.133406992 +0100
@@ -0,0 +1,20 @@
+// PR c++/89285
+// { dg-do compile { target c++14 } }
+
+struct A {
+  int a {};
+};
+struct B {
+  int b {};
+  constexpr B (A *x) {
+    int *c = &x->a;
+    while (*c)
+      c = reinterpret_cast<int *>((reinterpret_cast<char *>(c) + *c));
+    *c = reinterpret_cast<char *>(this) - reinterpret_cast<char *>(c);	// { dg-error "reinterpret_cast" }
+  }
+};
+struct C : A {
+  B bar {this};
+};
+
+constexpr C foo {};	// { dg-message "expansion of" }
--- gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow.C.jj	2019-02-20 23:40:16.042157853 +0100
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow.C	2019-02-21 01:10:15.156406617 +0100
@@ -27,6 +27,7 @@
 
 #define Assert(expr) static_assert ((expr), #expr)
 
+#if __cplusplus >= 201402L
 template <class T>
 constexpr T add (T x, T y, T z = T ())
 {
@@ -48,6 +49,11 @@ constexpr T mul (T x, T y, T z = T ())
 #define TEST_ADD(T, x, y, z) Assert (z == add<T>(x, y))
 #define TEST_SUB(T, x, y, z) Assert (z == sub<T>(x, y))
 #define TEST_MUL(T, x, y, z) Assert (z == mul<T>(x, y))
+#else
+#define TEST_ADD(T, x, y, z) Assert (true)
+#define TEST_SUB(T, x, y, z) Assert (true)
+#define TEST_MUL(T, x, y, z) Assert (true)
+#endif
 
 
 TEST_ADD (signed char,  0,         0,         0);
--- gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow2.C.jj	2019-02-21 01:10:15.156406617 +0100
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-arith-overflow2.C	2019-02-21 01:10:15.156406617 +0100
@@ -0,0 +1,31 @@
+// PR c++/70507 - integer overflow builtins not constant expressions
+// The constexpr-arith-overflow.C testcase covers this for C++14 and later.
+// { dg-do compile }
+// { dg-options "-std=c++11" }
+
+#define Assert(expr) static_assert ((expr), #expr)
+
+template <class T>
+constexpr T add (T x, T y, T z = T ())
+{
+  return __builtin_add_overflow (x, y, &z) ? 0 : z;	// { dg-error "is not a constant expression" }
+}
+
+template <class T>
+constexpr T sub (T x, T y, T z = T ())
+{
+  return __builtin_sub_overflow (x, y, &z) ? 0 : z;	// { dg-error "is not a constant expression" }
+}
+
+template <class T>
+constexpr T mul (T x, T y, T z = T ())
+{
+  return __builtin_mul_overflow (x, y, &z) ? 0 : z;	// { dg-error "is not a constant expression" }
+}
+
+Assert (0 == add<int>(0, 0));	// { dg-error "non-constant condition for static assertion" }
+Assert (0 == sub<int>(0, 0));	// { dg-error "non-constant condition for static assertion" }
+Assert (0 == mul<int>(0, 0));	// { dg-error "non-constant condition for static assertion" }
+// { dg-message "expansion of" "" { target *-*-* } .-3 }
+// { dg-message "expansion of" "" { target *-*-* } .-3 }
+// { dg-message "expansion of" "" { target *-*-* } .-3 }


	Jakub

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [C++ PATCH] Ensure constexpr evaluation is done on pre-cp_fold_function bodies (PR c++/89285, take 4)
  2019-02-21  9:47           ` [C++ PATCH] Ensure constexpr evaluation is done on pre-cp_fold_function bodies (PR c++/89285, take 4) Jakub Jelinek
@ 2019-02-21 21:36             ` Jason Merrill
  0 siblings, 0 replies; 9+ messages in thread
From: Jason Merrill @ 2019-02-21 21:36 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

On 2/20/19 9:08 PM, Jakub Jelinek wrote:
> On Wed, Feb 20, 2019 at 01:58:58PM -1000, Jason Merrill wrote:
>> On 2/20/19 11:52 AM, Jakub Jelinek wrote:
>>> On Wed, Feb 20, 2019 at 11:12:07AM -1000, Jason Merrill wrote:
>>>>> (z = REALPART_EXPR <SAVE_EXPR <.ADD_OVERFLOW (0, 0)>>;, (bool) IMAGPART_EXPR <SAVE_EXPR <.ADD_OVERFLOW (0, 0)>>;);
>>>>> which is not potential_constant_expression, but nothing called it again
>>>>> and cxx_eval_constant_expression can handle it.
>>>>
>>>> Yeah, that seems like a bug; C++11 shouldn't allow modification of z this
>>>> way either.
>>>
>>> Ack.  The following version stops accepting that for C++11, but keeps
>>> accepting for C++14 and later (so for that still needs to test
>>> potential_constant_expression instead of is_constant_expression, plus
>>> added a new inline function so that the code is more readable).
>>
>> Can we unconditionally change is_constant_expression to
>> potential_constant_expression there?
> 
> Yes, passed bootstrap/regtest on x86_64-linux and i686-linux that way
> and when I've tried to construct some builtin where it could make a
> difference (e.g. __builtin_memcpy (&a, "b", 1);) it still didn't make a
> difference.  So is this version ok for trunk?

OK, thanks.

Jason

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2019-02-21 21:18 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-02-18 22:46 [C++ PATCH] Ensure constexpr evaluation is done on pre-cp_fold_function bodies (PR c++/89285) Jakub Jelinek
2019-02-19  2:04 ` Jason Merrill
2019-02-19  4:31   ` Jakub Jelinek
2019-02-20  7:46     ` [C++ PATCH] Ensure constexpr evaluation is done on pre-cp_fold_function bodies (PR c++/89285, take 2) Jakub Jelinek
2019-02-20 21:22     ` [C++ PATCH] Ensure constexpr evaluation is done on pre-cp_fold_function bodies (PR c++/89285) Jason Merrill
2019-02-20 22:16       ` [C++ PATCH] Ensure constexpr evaluation is done on pre-cp_fold_function bodies (PR c++/89285, take 3) Jakub Jelinek
2019-02-21  0:00         ` Jason Merrill
2019-02-21  9:47           ` [C++ PATCH] Ensure constexpr evaluation is done on pre-cp_fold_function bodies (PR c++/89285, take 4) Jakub Jelinek
2019-02-21 21:36             ` 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).