Well-formed C++14 code with a loop in a constexpr function works fine, but we were crashing while trying to diagnose an unsuitable constexpr function because potential_constant_expression_1 didn't understand loops.

The second patch improves constexpr handling of EXIT_EXPR and loops around COMPOUND_EXPR rather than STATEMENT_LIST. This is not currently necessary, but might be in future.

Tested x86_64-pc-linux-gnu, applying to trunk.
commit e9df0f90c8502653b06a100bef9c765d6ee52ca9
Author: Jason Merrill <ja...@redhat.com>
Date:   Thu Mar 3 08:15:50 2016 -0600

    	PR c++/68206
    
    	PR c++/68530
    	* constexpr.c (potential_constant_expression_1): Handle LOOP_EXPR
    	and GOTO_EXPR.

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index ae0c973..d508660 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -4924,6 +4924,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
     case NON_DEPENDENT_EXPR:
       /* For convenience.  */
     case RETURN_EXPR:
+    case LOOP_EXPR:
+    case EXIT_EXPR:
       return RECUR (TREE_OPERAND (t, 0), want_rval);
 
     case TRY_FINALLY_EXPR:
@@ -5135,6 +5137,15 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict,
     case EMPTY_CLASS_EXPR:
       return false;
 
+    case GOTO_EXPR:
+      {
+	tree *target = &TREE_OPERAND (t, 0);
+	/* Gotos representing break and continue are OK; we should have
+	   rejected other gotos in parsing.  */
+	gcc_assert (breaks (target) || continues (target));
+	return true;
+      }
+
     default:
       if (objc_is_property_ref (t))
 	return false;
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-loop5.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-loop5.C
new file mode 100644
index 0000000..02f372d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-loop5.C
@@ -0,0 +1,19 @@
+// PR c++/68530
+// { dg-do compile { target c++14 } }
+
+struct thing {
+    void foo() {}
+};
+
+template<typename>
+constexpr int count()
+{
+    auto item = thing {};
+    for(; (item.foo(), false);); // { dg-error "foo" }
+    return 0;
+}
+
+int main()
+{
+    static_assert( count<int>() == 0, "" ); // { dg-error "" }
+}
commit 0e632adeb8c2253f6a9f9e4445c577eef51b1f4c
Author: Jason Merrill <ja...@redhat.com>
Date:   Tue Apr 19 13:59:05 2016 -0400

    	Improve constexpr handling of other loop forms.
    
    	* constexpr.c (breaks): Handle EXIT_EXPR.
    	(cxx_eval_loop_expr): Handle COMPOUND_EXPR body.
    	(cxx_eval_constant_expression): Handle EXIT_EXPR, improve handling
    	of COMPOUND_EXPR.

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index d508660..41f0b5c 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -3241,8 +3241,9 @@ static bool
 breaks (tree *jump_target)
 {
   return *jump_target
-    && TREE_CODE (*jump_target) == LABEL_DECL
-    && LABEL_DECL_BREAK (*jump_target);
+    && ((TREE_CODE (*jump_target) == LABEL_DECL
+	 && LABEL_DECL_BREAK (*jump_target))
+	|| TREE_CODE (*jump_target) == EXIT_EXPR);
 }
 
 static bool
@@ -3358,8 +3359,8 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t,
       hash_set<tree> save_exprs;
       new_ctx.save_exprs = &save_exprs;
 
-      cxx_eval_statement_list (&new_ctx, body,
-			       non_constant_p, overflow_p, jump_target);
+      cxx_eval_constant_expression (&new_ctx, body, /*lval*/false,
+				    non_constant_p, overflow_p, jump_target);
 
       /* Forget saved values of SAVE_EXPRs.  */
       for (hash_set<tree>::iterator iter = save_exprs.begin();
@@ -3750,6 +3751,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	    cxx_eval_constant_expression (ctx, op0,
 					  true, non_constant_p, overflow_p,
 					  jump_target);
+	    if (*non_constant_p)
+	      return t;
 	    op1 = TREE_OPERAND (t, 1);
 	    r = cxx_eval_constant_expression (ctx, op1,
 					      lval, non_constant_p, overflow_p,
@@ -4015,6 +4018,17 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 	}
       break;
 
+    case EXIT_EXPR:
+      {
+	tree cond = TREE_OPERAND (t, 0);
+	cond = cxx_eval_constant_expression (ctx, cond, /*lval*/false,
+					     non_constant_p, overflow_p);
+	VERIFY_CONSTANT (cond);
+	if (integer_nonzerop (cond))
+	  *jump_target = t;
+      }
+      break;
+
     case GOTO_EXPR:
       *jump_target = TREE_OPERAND (t, 0);
       gcc_assert (breaks (jump_target) || continues (jump_target));

Reply via email to