c++: Fix up potential_constant_expression_1 FOR/WHILE_STMT handling [PR98672]
[gcc.git] / gcc / cp / constexpr.c
index 9dddc53ca52aedbd656408b15da83c3c6bb030c5..b787919942ba84bd835c9cdcf44bdd6134db7542 100644 (file)
@@ -7649,15 +7649,16 @@ check_automatic_or_tls (tree ref)
 struct check_for_return_continue_data {
   hash_set<tree> *pset;
   tree continue_stmt;
+  tree break_stmt;
 };
 
 /* Helper function for potential_constant_expression_1 SWITCH_STMT handling,
    called through cp_walk_tree.  Return the first RETURN_EXPR found, or note
-   the first CONTINUE_STMT if RETURN_EXPR is not found.  */
+   the first CONTINUE_STMT and/or BREAK_STMT if RETURN_EXPR is not found.  */
 static tree
 check_for_return_continue (tree *tp, int *walk_subtrees, void *data)
 {
-  tree t = *tp, s;
+  tree t = *tp, s, b;
   check_for_return_continue_data *d = (check_for_return_continue_data *) data;
   switch (TREE_CODE (t))
     {
@@ -7669,6 +7670,11 @@ check_for_return_continue (tree *tp, int *walk_subtrees, void *data)
        d->continue_stmt = t;
       break;
 
+    case BREAK_STMT:
+      if (d->break_stmt == NULL_TREE)
+       d->break_stmt = t;
+      break;
+
 #define RECUR(x) \
       if (tree r = cp_walk_tree (&x, check_for_return_continue, data,  \
                                 d->pset))                              \
@@ -7680,16 +7686,20 @@ check_for_return_continue (tree *tp, int *walk_subtrees, void *data)
       *walk_subtrees = 0;
       RECUR (DO_COND (t));
       s = d->continue_stmt;
+      b = d->break_stmt;
       RECUR (DO_BODY (t));
       d->continue_stmt = s;
+      d->break_stmt = b;
       break;
 
     case WHILE_STMT:
       *walk_subtrees = 0;
       RECUR (WHILE_COND (t));
       s = d->continue_stmt;
+      b = d->break_stmt;
       RECUR (WHILE_BODY (t));
       d->continue_stmt = s;
+      d->break_stmt = b;
       break;
 
     case FOR_STMT:
@@ -7698,16 +7708,28 @@ check_for_return_continue (tree *tp, int *walk_subtrees, void *data)
       RECUR (FOR_COND (t));
       RECUR (FOR_EXPR (t));
       s = d->continue_stmt;
+      b = d->break_stmt;
       RECUR (FOR_BODY (t));
       d->continue_stmt = s;
+      d->break_stmt = b;
       break;
 
     case RANGE_FOR_STMT:
       *walk_subtrees = 0;
       RECUR (RANGE_FOR_EXPR (t));
       s = d->continue_stmt;
+      b = d->break_stmt;
       RECUR (RANGE_FOR_BODY (t));
       d->continue_stmt = s;
+      d->break_stmt = b;
+      break;
+
+    case SWITCH_STMT:
+      *walk_subtrees = 0;
+      RECUR (SWITCH_STMT_COND (t));
+      b = d->break_stmt;
+      RECUR (SWITCH_STMT_BODY (t));
+      d->break_stmt = b;
       break;
 #undef RECUR
 
@@ -8190,7 +8212,18 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
          /* If we couldn't evaluate the condition, it might not ever be
             true.  */
          if (!integer_onep (tmp))
-           return true;
+           {
+             /* Before returning true, check if the for body can contain
+                a return.  */
+             hash_set<tree> pset;
+             check_for_return_continue_data data = { &pset, NULL_TREE,
+                                                     NULL_TREE };
+             if (tree ret_expr
+                 = cp_walk_tree (&FOR_BODY (t), check_for_return_continue,
+                                 &data, &pset))
+               *jump_target = ret_expr;
+             return true;
+           }
        }
       if (!RECUR (FOR_EXPR (t), any))
        return false;
@@ -8219,7 +8252,18 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
        tmp = cxx_eval_outermost_constant_expr (tmp, true);
       /* If we couldn't evaluate the condition, it might not ever be true.  */
       if (!integer_onep (tmp))
-       return true;
+       {
+         /* Before returning true, check if the while body can contain
+            a return.  */
+         hash_set<tree> pset;
+         check_for_return_continue_data data = { &pset, NULL_TREE,
+                                                 NULL_TREE  };
+         if (tree ret_expr
+             = cp_walk_tree (&WHILE_BODY (t), check_for_return_continue,
+                             &data, &pset))
+           *jump_target = ret_expr;
+         return true;
+       }
       if (!RECUR (WHILE_BODY (t), any))
        return false;
       if (breaks (jump_target) || continues (jump_target))
@@ -8238,7 +8282,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
       else
        {
          hash_set<tree> pset;
-         check_for_return_continue_data data = { &pset, NULL_TREE };
+         check_for_return_continue_data data = { &pset, NULL_TREE,
+                                                 NULL_TREE };
          if (tree ret_expr
              = cp_walk_tree (&SWITCH_STMT_BODY (t), check_for_return_continue,
                              &data, &pset))
@@ -8648,11 +8693,46 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now,
        return RECUR (TREE_OPERAND (t, 2), want_rval);
       else if (TREE_CODE (tmp) == INTEGER_CST)
        return RECUR (TREE_OPERAND (t, 1), want_rval);
+      tmp = *jump_target;
       for (i = 1; i < 3; ++i)
-       if (potential_constant_expression_1 (TREE_OPERAND (t, i),
-                                            want_rval, strict, now,
-                                            tf_none, jump_target))
-         return true;
+       {
+         tree this_jump_target = tmp;
+         if (potential_constant_expression_1 (TREE_OPERAND (t, i),
+                                              want_rval, strict, now,
+                                              tf_none, &this_jump_target))
+           {
+             if (returns (&this_jump_target))
+               *jump_target = this_jump_target;
+             else if (!returns (jump_target))
+               {
+                 if (breaks (&this_jump_target)
+                     || continues (&this_jump_target))
+                   *jump_target = this_jump_target;
+                 if (i == 1)
+                   {
+                     /* If the then branch is potentially constant, but
+                        does not return, check if the else branch
+                        couldn't return, break or continue.  */
+                     hash_set<tree> pset;
+                     check_for_return_continue_data data = { &pset, NULL_TREE,
+                                                             NULL_TREE };
+                     if (tree ret_expr
+                       = cp_walk_tree (&TREE_OPERAND (t, 2),
+                                       check_for_return_continue, &data,
+                                       &pset))
+                       *jump_target = ret_expr;
+                     else if (*jump_target == NULL_TREE)
+                       {
+                         if (data.continue_stmt)
+                           *jump_target = data.continue_stmt;
+                         else if (data.break_stmt)
+                           *jump_target = data.break_stmt;
+                       }
+                   }
+               }
+             return true;
+           }
+       }
       if (flags & tf_error)
        error_at (loc, "expression %qE is not a constant expression", t);
       return false;