c++: Fix constexpr cleanup error handling.
authorJason Merrill <jason@redhat.com>
Wed, 28 Oct 2020 21:30:05 +0000 (17:30 -0400)
committerJason Merrill <jason@redhat.com>
Thu, 29 Oct 2020 17:09:19 +0000 (13:09 -0400)
In this testcase, the primary evaluation successfully produces 'true', and
then running one of the cleanups hits a double delete, making the whole
thing not a valid constant expression.  So we were returning 'true' wrapped
in a NOP_EXPR to indicate its non-constancy, but evaluating that again is a
perfectly acceptable constant expression, so we weren't getting the verbose
diagnostic we were looking for.

So if non_constant_p gets set other than for overflow, go back to the
original expression.

With this change, we should never hit the manifestly_const_eval test, and
the is-constant-evaluated1.C test passes without it.

gcc/cp/ChangeLog:

PR c++/97388
* constexpr.c (cxx_eval_outermost_constant_expr): Revert to
original expression if evaluation sets non_constant_p.

gcc/testsuite/ChangeLog:

PR c++/97388
* g++.dg/cpp2a/constexpr-dtor8.C: New test.

gcc/cp/constexpr.c
gcc/testsuite/g++.dg/cpp2a/constexpr-dtor8.C [new file with mode: 0644]

index c959b532ec3bcd44749b172f37971346b60fa5f6..b46824f128dbee7834d13715b5292afa5d939133 100644 (file)
@@ -6925,6 +6925,10 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
       non_constant_p = true;
     }
 
+  if (non_constant_p)
+    /* If we saw something bad, go back to our argument.  The wrapping below is
+       only for the cases of TREE_CONSTANT argument or overflow.  */
+    r = t;
 
   if (!non_constant_p && overflow_p)
     non_constant_p = true;
@@ -6941,12 +6945,6 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant,
     return r;
   else if (non_constant_p && TREE_CONSTANT (r))
     {
-      /* If __builtin_is_constant_evaluated () was evaluated to true
-        and the result is not a valid constant expression, we need to
-        punt.  */
-      if (manifestly_const_eval)
-       return cxx_eval_outermost_constant_expr (t, true, strict,
-                                                false, false, object);
       /* This isn't actually constant, so unset TREE_CONSTANT.
         Don't clear TREE_CONSTANT on ADDR_EXPR, as the middle-end requires
         it to be set if it is invariant address, even when it is not
diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-dtor8.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-dtor8.C
new file mode 100644 (file)
index 0000000..3048110
--- /dev/null
@@ -0,0 +1,19 @@
+// PR c++/97388
+// { dg-do compile { target c++20 } }
+
+struct S {
+  int *s;
+  constexpr S () : s(new int) {}
+  S (const S &) = delete;
+  S &operator= (const S &) = delete;
+  constexpr ~S () { delete s; }        // { dg-error "already deallocated" }
+};
+
+constexpr bool
+foo (S v)
+{
+  delete v.s;
+  return true;
+}
+
+static_assert (foo (S ()));    // { dg-error "non-constant condition for static assertion" }