Message ID | 20181010155850.GD19003@redhat.com |
---|---|
State | New |
Headers | show |
Series | C++ PATCH for c++/87567, constexpr rejects call to non-constexpr function | expand |
On Wed, Oct 10, 2018 at 11:58 AM Marek Polacek <polacek@redhat.com> wrote: > > In this testcase, the call to f() can never be a constant expression, but > that's not a problem because it is never reached. We handle a similar scenario > for IF_STMT, so we can just do the same. The RANGE_FOR_STMT case seems to > never be reached in the whole testsuite, so I didn't change that. That seems like a hole in the testsuite. How about this? constexpr int fn3() { struct empty_range { constexpr int* begin() { return 0; } constexpr int* end() { return 0; } } e; for (auto x : e) f(); return 0; } The patch is OK; you can address fn3 in a follow-up patch. Jason
On Wed, Oct 10, 2018 at 03:48:34PM -0400, Jason Merrill wrote: > On Wed, Oct 10, 2018 at 11:58 AM Marek Polacek <polacek@redhat.com> wrote: > > > > In this testcase, the call to f() can never be a constant expression, but > > that's not a problem because it is never reached. We handle a similar scenario > > for IF_STMT, so we can just do the same. The RANGE_FOR_STMT case seems to > > never be reached in the whole testsuite, so I didn't change that. > > That seems like a hole in the testsuite. How about this? > > constexpr int > fn3() > { > struct empty_range { > constexpr int* begin() { return 0; } > constexpr int* end() { return 0; } > } e; > for (auto x : e) > f(); > return 0; > } > > The patch is OK; you can address fn3 in a follow-up patch. Thanks. fn3() still only triggers case FOR_STMT, because RANGE_FOR_STMT are only created in templates, and are converted to FOR_STMTs in tsubst_expr. But I've now found a testcase that triggers the RANGE_FOR_STMT case: int f() { return 1; } template<typename> constexpr void fn4 () { struct empty_range { constexpr int* begin() { return 0; } constexpr int* end() { return 0; } } e; constexpr int j = ({ for (auto x : e) f(); 1; }); } void fn5 () { fn4<int>(); } However, we fail to compile both fn3 and fn4, because we're unable to evaluate __for_begin != __for_end. I'm going to try to figure out why. Marek
diff --git gcc/cp/constexpr.c gcc/cp/constexpr.c index 403edda8c47..4fa8c965a9d 100644 --- gcc/cp/constexpr.c +++ gcc/cp/constexpr.c @@ -5818,8 +5818,16 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, case FOR_STMT: if (!RECUR (FOR_INIT_STMT (t), any)) return false; - if (!RECUR (FOR_COND (t), rval)) + tmp = FOR_COND (t); + if (!RECUR (tmp, rval)) return false; + if (tmp) + { + if (!processing_template_decl) + tmp = cxx_eval_outermost_constant_expr (tmp, true); + if (integer_zerop (tmp)) + return true; + } if (!RECUR (FOR_EXPR (t), any)) return false; if (!RECUR (FOR_BODY (t), any)) @@ -5840,8 +5848,13 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, return true; case WHILE_STMT: - if (!RECUR (WHILE_COND (t), rval)) + tmp = WHILE_COND (t); + if (!RECUR (tmp, rval)) return false; + if (!processing_template_decl) + tmp = cxx_eval_outermost_constant_expr (tmp, true); + if (integer_zerop (tmp)) + return true; if (!RECUR (WHILE_BODY (t), any)) return false; if (breaks (jump_target) || continues (jump_target)) diff --git gcc/testsuite/g++.dg/cpp1y/constexpr-loop7.C gcc/testsuite/g++.dg/cpp1y/constexpr-loop7.C index e69de29bb2d..6733820fee2 100644 --- gcc/testsuite/g++.dg/cpp1y/constexpr-loop7.C +++ gcc/testsuite/g++.dg/cpp1y/constexpr-loop7.C @@ -0,0 +1,21 @@ +// PR c++/87567 +// { dg-do compile { target c++14 } } + +constexpr bool always_false() { return false; } +int f() { return 1; } + +constexpr int +fn1 () +{ + while (always_false ()) + return f(); + return 0; +} + +constexpr int +fn2 () +{ + for (;always_false();) + return f(); + return 0; +}