re PR c++/88449 (__builtin_is_constant_evaluated misbehaves due to constexpr call...
authorJakub Jelinek <jakub@redhat.com>
Wed, 12 Dec 2018 08:28:43 +0000 (09:28 +0100)
committerJakub Jelinek <jakub@gcc.gnu.org>
Wed, 12 Dec 2018 08:28:43 +0000 (09:28 +0100)
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.

From-SVN: r267044

gcc/cp/ChangeLog
gcc/cp/constexpr.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/cpp2a/is-constant-evaluated1.C
gcc/testsuite/g++.dg/cpp2a/is-constant-evaluated2.C [new file with mode: 0644]

index db057d093f85cc59e23936e6f9c1ddad66f65821..0347daa2bd3d51e6e58c9cc9263b14a31300ad3e 100644 (file)
@@ -1,3 +1,13 @@
+2018-12-12  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.
+
 2018-12-11  Jakub Jelinek  <jakub@redhat.com>
 
        PR c++/87861
index 96326c3a6988afc1c70d5b91f60e348d2e719945..81ce4cdbe756d24ffb466c8ed6a86196bf14a6e5 100644 (file)
@@ -976,6 +976,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>
@@ -1055,6 +1057,8 @@ constexpr_call_hasher::equal (constexpr_call *lhs, constexpr_call *rhs)
     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;
@@ -1503,7 +1507,8 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
 {
   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)
@@ -1675,8 +1680,11 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
   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 ();
index bfe527a7ad7a2ed3f785fcab3963f457aeb29a34..aa11d0ecb2dbe81415c9f4eaa16271047f6ae956 100644 (file)
@@ -1,3 +1,12 @@
+2018-12-12  Jakub Jelinek  <jakub@redhat.com>
+
+       PR c++/88449
+       * 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.
+
 2018-12-11  Steven G. Kargl  <kargl@gcc.gnu.org>
 
        PR fortran/88155
index 3b988845e416bb7a8ce19e0733b36a197038d778..ead36c0efb116fc8c3bf4b177f1febb9a98795ff 100644 (file)
@@ -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 () ? y : 1; // initializes a to 1
 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<true> >::value, "x's type");
 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 ();
diff --git a/gcc/testsuite/g++.dg/cpp2a/is-constant-evaluated2.C b/gcc/testsuite/g++.dg/cpp2a/is-constant-evaluated2.C
new file mode 100644 (file)
index 0000000..a3b377d
--- /dev/null
@@ -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 ();
+}