This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[C++ PATCH] Fix up __builtin_is_constant_evaluated (PR c++/88449)
- From: Jakub Jelinek <jakub at redhat dot com>
- To: Jason Merrill <jason at redhat dot com>
- Cc: gcc-patches at gcc dot gnu dot org
- Date: Tue, 11 Dec 2018 17:34:18 +0100
- Subject: [C++ PATCH] Fix up __builtin_is_constant_evaluated (PR c++/88449)
- Reply-to: Jakub Jelinek <jakub at redhat dot com>
Hi!
While working on the libstdc++ patch for P0595R2, I've noticed that while
__builtin_is_constant_evaluated () directly works, when wrapped into
an constexpr inline noexcept function, it in some cases doesn't. The
problem is that the constexpr call cache didn't take
ctx->pretend_const_required into account.
The following patch fixes it by just treating it like another magic
parameter. Another option would be (but much more involved) to remember
during the constexpr.c processing whether we've seen during the evaluation
any calls to __builtin_is_constant_evaluated (that would require propagating
in all the spots that use a new context back to the old context). Then
we could in the constexpr hash remember either that during the evaluation
of that constexpr call there was no __builtin_is_constant_evaluated seen,
or, if it has been seen, whether it was in ctx->pretend_const_required
mode or in !ctx->pretend_const_required mode.
I've bootstrapped/regtested on x86_64-linux and i686-linux the following
simpler version, ok for trunk?
2018-12-11 Jakub Jelinek <jakub@redhat.com>
PR c++/88449
* constexpr.c (struct constexpr_call): Add pretend_const_required
member.
(constexpr_call_hasher::equal): Return false if pretend_const_required
members differ.
(cxx_eval_call_expression): Adjust new_call initialization. Hash in
ctx->pretend_const_required.
* g++.dg/cpp2a/is-constant-evaluated1.C: Change from dg-do compile
to dg-do run.
(e): Adjust comment with correct expected value.
(main): Expect e == 1.
* g++.dg/cpp2a/is-constant-evaluated2.C: New test.
--- gcc/cp/constexpr.c.jj 2018-12-07 16:18:42.481847741 +0100
+++ gcc/cp/constexpr.c 2018-12-11 12:01:27.968941683 +0100
@@ -973,6 +973,8 @@ struct GTY((for_user)) constexpr_call {
/* The hash of this call; we remember it here to avoid having to
recalculate it when expanding the hash table. */
hashval_t hash;
+ /* Whether __builtin_is_constant_evaluated() should evaluate to true. */
+ bool pretend_const_required;
};
struct constexpr_call_hasher : ggc_ptr_hash<constexpr_call>
@@ -1052,6 +1054,8 @@ constexpr_call_hasher::equal (constexpr_
return true;
if (lhs->hash != rhs->hash)
return false;
+ if (lhs->pretend_const_required != rhs->pretend_const_required)
+ return false;
if (!constexpr_fundef_hasher::equal (lhs->fundef, rhs->fundef))
return false;
lhs_bindings = lhs->bindings;
@@ -1500,7 +1504,8 @@ cxx_eval_call_expression (const constexp
{
location_t loc = cp_expr_loc_or_loc (t, input_location);
tree fun = get_function_named_in_call (t);
- constexpr_call new_call = { NULL, NULL, NULL, 0 };
+ constexpr_call new_call
+ = { NULL, NULL, NULL, 0, ctx->pretend_const_required };
bool depth_ok;
if (fun == NULL_TREE)
@@ -1642,8 +1647,11 @@ cxx_eval_call_expression (const constexp
constexpr_call *entry = NULL;
if (depth_ok && !non_constant_args && ctx->strict)
{
- new_call.hash = iterative_hash_template_arg
- (new_call.bindings, constexpr_fundef_hasher::hash (new_call.fundef));
+ new_call.hash = constexpr_fundef_hasher::hash (new_call.fundef);
+ new_call.hash
+ = iterative_hash_template_arg (new_call.bindings, new_call.hash);
+ new_call.hash
+ = iterative_hash_object (ctx->pretend_const_required, new_call.hash);
/* If we have seen this call before, we are done. */
maybe_initialize_constexpr_call_table ();
--- gcc/testsuite/g++.dg/cpp2a/is-constant-evaluated1.C.jj 2018-08-26 22:41:13.778935483 +0200
+++ gcc/testsuite/g++.dg/cpp2a/is-constant-evaluated1.C 2018-12-11 11:57:55.027418581 +0100
@@ -1,5 +1,5 @@
// P0595R1
-// { dg-do compile { target c++14 } }
+// { dg-do run { target c++14 } }
template<int N> struct X { int v = N; };
X<__builtin_is_constant_evaluated ()> x; // type X<true>
@@ -8,7 +8,7 @@ int a = __builtin_is_constant_evaluated
int b = __builtin_is_constant_evaluated () ? 2 : y; // initializes b to 2
int c = y + (__builtin_is_constant_evaluated () ? 2 : y); // initializes c to 2*y
int d = __builtin_is_constant_evaluated (); // initializes d to 1
-int e = d + __builtin_is_constant_evaluated (); // initializes e to 0
+int e = d + __builtin_is_constant_evaluated (); // initializes e to 1 + 0
struct false_type { static constexpr bool value = false; };
struct true_type { static constexpr bool value = true; };
@@ -50,7 +50,7 @@ static_assert (is_same<decltype (x), X<t
int
main ()
{
- if (a != 1 || b != 2 || c != 8 || d != 1 || e != 0 || p != 26 || q != 56)
+ if (a != 1 || b != 2 || c != 8 || d != 1 || e != 1 || p != 26 || q != 56)
__builtin_abort ();
if (s.a != 3 || s.b != 4 || t.a != 2 || t.b != 4)
__builtin_abort ();
--- gcc/testsuite/g++.dg/cpp2a/is-constant-evaluated2.C.jj 2018-12-11 11:37:03.415897434 +0100
+++ gcc/testsuite/g++.dg/cpp2a/is-constant-evaluated2.C 2018-12-11 11:58:14.296103289 +0100
@@ -0,0 +1,72 @@
+// P0595R1
+// { dg-do run { target c++14 } }
+
+constexpr inline bool
+is_constant_evaluated () noexcept
+{
+ return __builtin_is_constant_evaluated ();
+}
+
+template<int N> struct X { int v = N; };
+X<is_constant_evaluated ()> x; // type X<true>
+int y = 4;
+int a = is_constant_evaluated () ? y : 1; // initializes a to 1
+int b = is_constant_evaluated () ? 2 : y; // initializes b to 2
+int c = y + (is_constant_evaluated () ? 2 : y); // initializes c to 2*y
+int d = is_constant_evaluated (); // initializes d to 1
+int e = d + is_constant_evaluated (); // initializes e to 1 + 0
+
+struct false_type { static constexpr bool value = false; };
+struct true_type { static constexpr bool value = true; };
+template<class T, class U>
+struct is_same : false_type {};
+template<class T>
+struct is_same<T, T> : true_type {};
+
+constexpr int
+foo (int x)
+{
+ const int n = is_constant_evaluated () ? 13 : 17; // n == 13
+ int m = is_constant_evaluated () ? 13 : 17; // m might be 13 or 17 (see below)
+ char arr[n] = {}; // char[13]
+ return m + sizeof (arr) + x;
+}
+
+constexpr int
+bar ()
+{
+ const int n = is_constant_evaluated() ? 13 : 17;
+ X<n> x1;
+ X<is_constant_evaluated() ? 13 : 17> x2;
+ static_assert (is_same<decltype (x1), decltype (x2)>::value, "x1/x2's type");
+ return x1.v + x2.v;
+}
+
+int p = foo (0); // m == 13; initialized to 26
+int q = p + foo (0); // m == 17 for this call; initialized to 56
+static_assert (bar () == 26, "bar");
+
+struct S { int a, b; };
+
+S s = { is_constant_evaluated () ? 2 : 3, y };
+S t = { is_constant_evaluated () ? 2 : 3, 4 };
+
+static_assert (is_same<decltype (x), X<true> >::value, "x's type");
+
+int
+main ()
+{
+ if (a != 1 || b != 2 || c != 8 || d != 1 || e != 1 || p != 26 || q != 56)
+ __builtin_abort ();
+ if (s.a != 3 || s.b != 4 || t.a != 2 || t.b != 4)
+ __builtin_abort ();
+ if (foo (y) != 34)
+ __builtin_abort ();
+#if __cplusplus >= 201703L
+ if constexpr (foo (0) != 26)
+ __builtin_abort ();
+#endif
+ constexpr int w = foo (0);
+ if (w != 26)
+ __builtin_abort ();
+}
Jakub