Hi! The following testcase is rejected even when it is valid. The problem is that potential_constant_expression_1 doesn't have the accurate *jump_target tracking cxx_eval_* has, and when the loop has a condition that isn't guaranteed to be always true, the body isn't walked at all. That is mostly a correct conservative behavior, except that it doesn't detect if there are any return statements in the body, which means the loop might return instead of falling through to the next statement. We already have code for return stmt discovery in code snippets we don't try to evaluate for switches, so this patch reuses that for FOR_STMT and WHILE_STMT bodies.
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? Note, I haven't touched FOR_EXPR, with statement expressions it could have return stmts in it too, or it could have break or continue statements that wouldn't bind to the current loop but to something outer. That case is clearly mishandled by potential_constant_expression_1 even when the condition is missing or is always true, and it wouldn't surprise me if cxx_eval_* didn't handle it right either, so I'm deferring that to separate PR for later. We'd need proper test coverage for all of that. 2021-01-15 Jakub Jelinek <ja...@redhat.com> PR c++/98672 * constexpr.c (potential_constant_expression_1) <case FOR_STMT>, <case WHILE_STMT>: If the condition isn't constant true, check if the loop body can contain a return stmt. * g++.dg/cpp1y/constexpr-98672.C: New test. --- gcc/cp/constexpr.c.jj 2021-01-13 19:19:44.368469462 +0100 +++ gcc/cp/constexpr.c 2021-01-14 12:02:27.347042704 +0100 @@ -8190,7 +8190,17 @@ potential_constant_expression_1 (tree t, /* 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 }; + 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 +8229,17 @@ potential_constant_expression_1 (tree t, 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 }; + 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)) --- gcc/testsuite/g++.dg/cpp1y/constexpr-98672.C.jj 2021-01-14 12:19:24.842438847 +0100 +++ gcc/testsuite/g++.dg/cpp1y/constexpr-98672.C 2021-01-14 12:07:33.935551155 +0100 @@ -0,0 +1,35 @@ +// PR c++/98672 +// { dg-do compile { target c++14 } } + +void +foo () +{ +} + +constexpr int +bar () +{ + for (int i = 0; i < 5; ++i) + return i; + foo (); + return 0; +} + +constexpr int +baz () +{ + int i = 0; + while (i < 5) + { + if (i == 3) + return i; + else + ++i; + } + foo (); + return 0; +} + +constexpr int i = bar (); +constexpr int j = baz (); +static_assert (i == 0 && j == 3, ""); Jakub