This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
C++ PATCH for c++/87594, constexpr rejects-valid with range-based for
- From: Marek Polacek <polacek at redhat dot com>
- To: Jason Merrill <jason at redhat dot com>, GCC Patches <gcc-patches at gcc dot gnu dot org>
- Date: Thu, 11 Oct 2018 20:56:01 -0400
- Subject: C++ PATCH for c++/87594, constexpr rejects-valid with range-based for
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 <polacek@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 ();
+}