re PR c++/91369 (Implement P0784R7: constexpr new)
authorJakub Jelinek <jakub@redhat.com>
Tue, 7 Jan 2020 07:13:50 +0000 (08:13 +0100)
committerJakub Jelinek <jakub@gcc.gnu.org>
Tue, 7 Jan 2020 07:13:50 +0000 (08:13 +0100)
PR c++/91369
* constexpr.c (struct constexpr_global_ctx): Add heap_alloc_count
member, initialize it to zero in ctor.
(cxx_eval_call_expression): Bump heap_dealloc_count when deleting
a heap object.  Don't cache calls to functions which allocate some
heap objects and don't deallocate them or deallocate some heap
objects they didn't allocate.

* g++.dg/cpp1y/constexpr-new.C: Expect an error explaining why
static_assert failed for C++2a.
* g++.dg/cpp2a/constexpr-new9.C: New test.

From-SVN: r279943

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

index abcbba5..77c7b10 100644 (file)
@@ -1,3 +1,13 @@
+2020-01-07  Jakub Jelinek  <jakub@redhat.com>
+
+       PR c++/91369
+       * constexpr.c (struct constexpr_global_ctx): Add heap_alloc_count
+       member, initialize it to zero in ctor.
+       (cxx_eval_call_expression): Bump heap_dealloc_count when deleting
+       a heap object.  Don't cache calls to functions which allocate some
+       heap objects and don't deallocate them or deallocate some heap
+       objects they didn't allocate.
+
 2020-01-06  Jason Merrill  <jason@redhat.com>
 
        PR c++/92552 - ICE with inherited constrained default ctor.
index c8852bd..417af18 100644 (file)
@@ -1041,8 +1041,11 @@ struct constexpr_global_ctx {
   auto_vec<tree, 16> heap_vars;
   /* Cleanups that need to be evaluated at the end of CLEANUP_POINT_EXPR.  */
   vec<tree> *cleanups;
+  /* Number of heap VAR_DECL deallocations.  */
+  unsigned heap_dealloc_count;
   /* Constructor.  */
-  constexpr_global_ctx () : constexpr_ops_count (0), cleanups (NULL) {}
+  constexpr_global_ctx ()
+    : constexpr_ops_count (0), cleanups (NULL), heap_dealloc_count (0) {}
 };
 
 /* The constexpr expansion context.  CALL is the current function
@@ -2056,6 +2059,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
                    {
                      DECL_NAME (var) = heap_deleted_identifier;
                      ctx->global->values.remove (var);
+                     ctx->global->heap_dealloc_count++;
                      return void_node;
                    }
                  else if (DECL_NAME (var) == heap_deleted_identifier)
@@ -2281,6 +2285,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
     }
   else
     {
+      bool cacheable = true;
       if (result && result != error_mark_node)
        /* OK */;
       else if (!DECL_SAVED_TREE (fun))
@@ -2346,6 +2351,8 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
          auto_vec<tree, 10> save_exprs;
          ctx_with_save_exprs.save_exprs = &save_exprs;
          ctx_with_save_exprs.call = &new_call;
+         unsigned save_heap_alloc_count = ctx->global->heap_vars.length ();
+         unsigned save_heap_dealloc_count = ctx->global->heap_dealloc_count;
 
          tree jump_target = NULL_TREE;
          cxx_eval_constant_expression (&ctx_with_save_exprs, body,
@@ -2417,6 +2424,33 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
 
          /* Make the unshared function copy we used available for re-use.  */
          save_fundef_copy (fun, copy);
+
+         /* If the call allocated some heap object that hasn't been
+            deallocated during the call, or if it deallocated some heap
+            object it has not allocated, the call isn't really stateless
+            for the constexpr evaluation and should not be cached.
+            It is fine if the call allocates something and deallocates it
+            too.  */
+         if (entry
+             && (save_heap_alloc_count != ctx->global->heap_vars.length ()
+                 || (save_heap_dealloc_count
+                     != ctx->global->heap_dealloc_count)))
+           {
+             tree heap_var;
+             unsigned int i;
+             if ((ctx->global->heap_vars.length ()
+                  - ctx->global->heap_dealloc_count)
+                 != save_heap_alloc_count - save_heap_dealloc_count)
+               cacheable = false;
+             else
+               FOR_EACH_VEC_ELT_FROM (ctx->global->heap_vars, i, heap_var,
+                                      save_heap_alloc_count)
+                 if (DECL_NAME (heap_var) != heap_deleted_identifier)
+                   {
+                     cacheable = false;
+                     break;
+                   }
+           }
        }
 
       if (result == error_mark_node)
@@ -2426,7 +2460,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
       else if (!result)
        result = void_node;
       if (entry)
-       entry->result = result;
+       entry->result = cacheable ? result : error_mark_node;
     }
 
   /* The result of a constexpr function must be completely initialized.
index 3e778e9..517c851 100644 (file)
@@ -1,3 +1,10 @@
+2020-01-07  Jakub Jelinek  <jakub@redhat.com>
+
+       PR c++/91369
+       * g++.dg/cpp1y/constexpr-new.C: Expect an error explaining why
+       static_assert failed for C++2a.
+       * g++.dg/cpp2a/constexpr-new9.C: New test.
+
 2020-01-06  Richard Sandiford  <richard.sandiford@arm.com>
 
        * gcc.target/aarch64/sve/asrdiv_1.c: Remove trailing %s.
index 90cb425..6316ff2 100644 (file)
@@ -5,7 +5,7 @@ constexpr int *f4(bool b) {
     return nullptr;
   } else {
     return new int{42}; // { dg-error "call to non-.constexpr." "" { target c++17_down } }
-  }
+  }                    // { dg-error "is not a constant expression because allocated storage has not been deallocated" "" { target c++2a } .-1 }
 }
 static_assert(f4(true) == nullptr, "");
 static_assert(f4(false) == nullptr, ""); // { dg-error "non-.constant. condition|" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-new9.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-new9.C
new file mode 100644 (file)
index 0000000..552d3c1
--- /dev/null
@@ -0,0 +1,15 @@
+// PR c++/91369
+// { dg-do compile { target c++2a } }
+
+struct S {
+  constexpr S (int *i) : i{i} {}
+  constexpr ~S () { delete[] i; }
+  int *i;
+};
+
+constexpr S foo (int x) { return { new int[x] () }; }
+constexpr bool bar () { foo (1); return true; }
+constexpr bool baz () { foo (1); return false; }
+
+static_assert (bar ());
+static_assert (!baz ());