PR c++/85600 - virtual delete failure.
authorJason Merrill <jason@redhat.com>
Thu, 3 May 2018 23:35:20 +0000 (19:35 -0400)
committerJason Merrill <jason@gcc.gnu.org>
Thu, 3 May 2018 23:35:20 +0000 (19:35 -0400)
* init.c (build_delete): Always save_expr when deleting.

From-SVN: r259913

gcc/cp/ChangeLog
gcc/cp/init.c
gcc/testsuite/g++.dg/expr/delete2.C [new file with mode: 0644]

index 25cb43a1a7731d6383a73698c5f6ca281ba0bb78..6ae6707949141f38bf2b2e9fc8d2f2f855bdb9cc 100644 (file)
@@ -1,3 +1,8 @@
+2018-05-03  Jason Merrill  <jason@redhat.com>
+
+       PR c++/85600 - virtual delete failure.
+       * init.c (build_delete): Always save_expr when deleting.
+
 2018-05-03  Nathan Sidwell  <nathan@acm.org>
 
        * decl.c (cxx_init_decl_processing): Remove flag_friend_injection.
index 221a831b7b8198f9e3bdf16548e65e747eca4b16..b934a0039ec75fcdfb77b1b7151134b3820ebfec 100644 (file)
@@ -4601,6 +4601,9 @@ build_delete (tree otype, tree addr, special_function_kind auto_delete,
                               auto_delete, use_global_delete, complain);
     }
 
+  bool deleting = (auto_delete == sfk_deleting_destructor);
+  gcc_assert (deleting == !(flags & LOOKUP_DESTRUCTOR));
+
   if (TYPE_PTR_P (otype))
     {
       addr = mark_rvalue_use (addr);
@@ -4628,7 +4631,7 @@ build_delete (tree otype, tree addr, special_function_kind auto_delete,
                          "declared when the class is defined");
                }
            }
-         else if (auto_delete == sfk_deleting_destructor && warn_delnonvdtor
+         else if (deleting && warn_delnonvdtor
                   && MAYBE_CLASS_TYPE_P (type) && !CLASSTYPE_FINAL (type)
                   && TYPE_POLYMORPHIC_P (type))
            {
@@ -4664,9 +4667,9 @@ build_delete (tree otype, tree addr, special_function_kind auto_delete,
       addr = convert_force (build_pointer_type (type), addr, 0, complain);
     }
 
-  tree head = NULL_TREE;
-  tree do_delete = NULL_TREE;
-  tree ifexp;
+  if (deleting)
+    /* We will use ADDR multiple times so we must save it.  */
+    addr = save_expr (addr);
 
   bool virtual_p = false;
   if (type_build_dtor_call (type))
@@ -4676,13 +4679,18 @@ build_delete (tree otype, tree addr, special_function_kind auto_delete,
       virtual_p = DECL_VIRTUAL_P (CLASSTYPE_DESTRUCTOR (type));
     }
 
+  tree head = NULL_TREE;
+  tree do_delete = NULL_TREE;
+
+  if (!deleting)
+    {
+      /* Leave do_delete null.  */
+    }
   /* For `::delete x', we must not use the deleting destructor
      since then we would not be sure to get the global `operator
      delete'.  */
-  if (use_global_delete && auto_delete == sfk_deleting_destructor)
+  else if (use_global_delete)
     {
-      /* We will use ADDR multiple times so we must save it.  */
-      addr = save_expr (addr);
       head = get_target_expr (build_headof (addr));
       /* Delete the object.  */
       do_delete = build_op_delete_call (DELETE_EXPR,
@@ -4699,11 +4707,8 @@ build_delete (tree otype, tree addr, special_function_kind auto_delete,
   /* If the destructor is non-virtual, there is no deleting
      variant.  Instead, we must explicitly call the appropriate
      `operator delete' here.  */
-  else if (!virtual_p
-          && auto_delete == sfk_deleting_destructor)
+  else if (!virtual_p)
     {
-      /* We will use ADDR multiple times so we must save it.  */
-      addr = save_expr (addr);
       /* Build the call.  */
       do_delete = build_op_delete_call (DELETE_EXPR,
                                        addr,
@@ -4715,8 +4720,7 @@ build_delete (tree otype, tree addr, special_function_kind auto_delete,
       /* Call the complete object destructor.  */
       auto_delete = sfk_complete_destructor;
     }
-  else if (auto_delete == sfk_deleting_destructor
-          && TYPE_GETS_REG_DELETE (type))
+  else if (TYPE_GETS_REG_DELETE (type))
     {
       /* Make sure we have access to the member op delete, even though
         we'll actually be calling it from the destructor.  */
@@ -4735,6 +4739,9 @@ build_delete (tree otype, tree addr, special_function_kind auto_delete,
   if (expr == error_mark_node)
     return error_mark_node;
 
+  if (!deleting)
+    return expr;
+
   if (do_delete && !TREE_SIDE_EFFECTS (expr))
     expr = do_delete;
   else if (do_delete)
@@ -4750,24 +4757,20 @@ build_delete (tree otype, tree addr, special_function_kind auto_delete,
   if (head)
     expr = build2 (COMPOUND_EXPR, void_type_node, head, expr);
 
-  if (flags & LOOKUP_DESTRUCTOR)
-    /* Explicit destructor call; don't check for null pointer.  */
-    ifexp = integer_one_node;
-  else
-    {
-      /* Handle deleting a null pointer.  */
-      warning_sentinel s (warn_address);
-      ifexp = cp_build_binary_op (input_location, NE_EXPR, addr,
-                                 nullptr_node, complain);
-      if (ifexp == error_mark_node)
-       return error_mark_node;
-      /* This is a compiler generated comparison, don't emit
-        e.g. -Wnonnull-compare warning for it.  */
-      else if (TREE_CODE (ifexp) == NE_EXPR)
-       TREE_NO_WARNING (ifexp) = 1;
-    }
+  /* Handle deleting a null pointer.  */
+  warning_sentinel s (warn_address);
+  tree ifexp = cp_build_binary_op (input_location, NE_EXPR, addr,
+                                  nullptr_node, complain);
+  ifexp = cp_fully_fold (ifexp);
+
+  if (ifexp == error_mark_node)
+    return error_mark_node;
+  /* This is a compiler generated comparison, don't emit
+     e.g. -Wnonnull-compare warning for it.  */
+  else if (TREE_CODE (ifexp) == NE_EXPR)
+    TREE_NO_WARNING (ifexp) = 1;
 
-  if (ifexp != integer_one_node)
+  if (!integer_nonzerop (ifexp))
     expr = build3 (COND_EXPR, void_type_node, ifexp, expr, void_node);
 
   return expr;
diff --git a/gcc/testsuite/g++.dg/expr/delete2.C b/gcc/testsuite/g++.dg/expr/delete2.C
new file mode 100644 (file)
index 0000000..a0fc179
--- /dev/null
@@ -0,0 +1,25 @@
+// PR c++/85600
+// { dg-do run }
+
+struct A
+{
+  virtual ~A() { }
+};
+
+struct B: A { };
+
+A *p;
+int count;
+
+A *f() {
+  ++count;
+  return p;
+}
+
+int main()
+{
+  p = new B;
+  delete f();
+  if (count != 1)
+    __builtin_abort();
+}