This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

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


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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]