Here potential_constant_expression_1 rejects the testcase because the body of the for loop calls a non-constexpr function. But the range is empty so the function would never get called. The trick with evaluating the for-condition doesn't work here, because we're dealing with a converted range-based for and can't evaluate
__for_begin != __for_end because the constexpr cache doesn't have the values of these two VAR_DECLs. So either we can use this ugly hack (more specialized), or just not check the body of the loop (more general), similarly to what we do (don't do) with switch. Bootstrapped/regtested on x86_64-linux, ok for trunk? 2018-10-11 Marek Polacek <pola...@redhat.com> PR c++/87594 - constexpr rejects-valid with range-based for. * constexpr.c (potential_constant_expression_1) <case FOR_STMT>: Return true for a converted ange-based for-statement. * g++.dg/cpp1y/constexpr-loop8.C: New test. diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c index 4fa8c965a9d..685ca743859 100644 --- gcc/cp/constexpr.c +++ gcc/cp/constexpr.c @@ -5827,6 +5827,17 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, tmp = cxx_eval_outermost_constant_expr (tmp, true); if (integer_zerop (tmp)) return true; + /* See if this is + __for_begin != __for_end + cp_convert_range_for created for us. If so, this is a converted + range-based for-statement, and we're not able to evaluate this + condition, so we might end up skipping the body entirely. */ + else if (TREE_CODE (tmp) == NE_EXPR + && VAR_P (TREE_OPERAND (tmp, 0)) + && DECL_NAME (TREE_OPERAND (tmp, 0)) == for_begin_identifier + && VAR_P (TREE_OPERAND (tmp, 1)) + && DECL_NAME (TREE_OPERAND (tmp, 1)) == for_end_identifier) + return true; } if (!RECUR (FOR_EXPR (t), any)) return false; diff --git gcc/testsuite/g++.dg/cpp1y/constexpr-loop8.C gcc/testsuite/g++.dg/cpp1y/constexpr-loop8.C index e69de29bb2d..bf132f2484e 100644 --- gcc/testsuite/g++.dg/cpp1y/constexpr-loop8.C +++ gcc/testsuite/g++.dg/cpp1y/constexpr-loop8.C @@ -0,0 +1,44 @@ +// PR c++/87594 +// { dg-do compile { target c++14 } } + +constexpr bool always_false() { return false; } +int f() { return 1; } + +constexpr int +fn1() +{ + struct empty_range { + constexpr int* begin() { return 0; } + constexpr int* end() { return 0; } + } e; + for (auto x : e) + f(); + return 0; +} + +constexpr int +fn2 () +{ + int a[] = { 1, 2, 3 }; + for (auto x : a) + f(); // { dg-error "call to non-.constexpr. function" } + return 0; +} + +constexpr int +fn3 () +{ + __extension__ int a[] = { }; + for (auto x : a) + f(); + return 0; +} + + +void +bar () +{ + constexpr int i1 = fn1 (); + constexpr int i2 = fn2 (); // { dg-message "in .constexpr. expansion of " } + constexpr int i3 = fn3 (); +}