public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [C++ PATCH] Fix up __builtin_is_constant_evaluated (PR c++/88449)
@ 2018-12-11 16:34 Jakub Jelinek
  2018-12-12  1:11 ` Jason Merrill
  0 siblings, 1 reply; 2+ messages in thread
From: Jakub Jelinek @ 2018-12-11 16:34 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches

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

^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [C++ PATCH] Fix up __builtin_is_constant_evaluated (PR c++/88449)
  2018-12-11 16:34 [C++ PATCH] Fix up __builtin_is_constant_evaluated (PR c++/88449) Jakub Jelinek
@ 2018-12-12  1:11 ` Jason Merrill
  0 siblings, 0 replies; 2+ messages in thread
From: Jason Merrill @ 2018-12-12  1:11 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: gcc-patches

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

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2018-12-12  1:11 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-12-11 16:34 [C++ PATCH] Fix up __builtin_is_constant_evaluated (PR c++/88449) Jakub Jelinek
2018-12-12  1:11 ` Jason Merrill

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).