diff mbox series

[C++] Fix up __builtin_is_constant_evaluated (PR c++/88449)

Message ID 20181211163418.GW12380@tucnak
State New
Headers show
Series [C++] Fix up __builtin_is_constant_evaluated (PR c++/88449) | expand

Commit Message

Jakub Jelinek Dec. 11, 2018, 4:34 p.m. UTC
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.


	Jakub

Comments

Jason Merrill Dec. 12, 2018, 1:11 a.m. UTC | #1
On 12/11/18 11:34 AM, Jakub Jelinek wrote:
> 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?

OK.

Jason
diff mbox series

Patch

--- 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 ();
+}