diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index eda85ecfa411ce134798a99e0ad582091f3b96a6..5d6ca8f60209210afaa5ab4de69dacd4c69a1619 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,11 @@
+2001-05-21  Mark Mitchell  <mark@codesourcery.com>
+
+	* call.c (build_op_delete_call): Ignore exception-specifications
+	when looking for matching delete operators.
+	* init.c (build_new_1): Compute whether or not the allocation
+	function used is a placement allocation function or not, and
+	communicate this information to build_op_delete_call.
+
 2001-05-21  Jason Merrill  <jason_merrill@redhat.com>
 
 	* class.c (build_vtable_entry_ref): Lose vtbl parm.  Fix for new abi.
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 6694f563d4bd877648dfdf6488171b6465a50005..5367cc55805472547a8f5119465b1c910d7ee326 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -3545,7 +3545,7 @@ builtin:
      used to determine what the corresponding new looked like.
    SIZE is the size of the memory block to be deleted.
    FLAGS are the usual overloading flags.
-   PLACEMENT is the corresponding placement new call, or 0.  */
+   PLACEMENT is the corresponding placement new call, or NULL_TREE.  */
 
 tree
 build_op_delete_call (code, addr, size, flags, placement)
@@ -3620,23 +3620,50 @@ build_op_delete_call (code, addr, size, flags, placement)
 	argtypes = tree_cons (NULL_TREE, ptr_type_node,
 			      tree_cons (NULL_TREE, sizetype, 
 					 void_list_node));
-
       fntype = build_function_type (void_type_node, argtypes);
-      fn = instantiate_type (fntype, fns, itf_no_attributes);
 
-      if (fn != error_mark_node)
+      /* Go through the `operator delete' functions looking for one
+	 with a matching type.  */
+      for (fn = BASELINK_P (fns) ? TREE_VALUE (fns) : fns; 
+	   fn; 
+	   fn = OVL_NEXT (fn))
 	{
-	  /* Member functions.  */
-	  if (BASELINK_P (fns))
-	    enforce_access (type, fn);
-
-	  if (pass == 0)
-	    args = tree_cons (NULL_TREE, addr, args);
-	  else
-	    args = tree_cons (NULL_TREE, addr, 
-			      build_tree_list (NULL_TREE, size));
-	  return build_function_call (fn, args);
+	  tree t;
+
+	  /* Exception specifications on the `delete' operator do not
+	     matter.  */
+	  t = build_exception_variant (TREE_TYPE (OVL_CURRENT (fn)),
+				       NULL_TREE);
+	  /* We also don't compare attributes.  We're really just
+	     trying to check the types of the first two parameters.  */
+	  if (comptypes (t, fntype, COMPARE_NO_ATTRIBUTES))
+	    break;
 	}
+
+      /* If we found a match, we're done.  */
+      if (fn)
+	break;
+    }
+
+  /* If we have a matching function, call it.  */
+  if (fn)
+    {
+      /* Make sure we have the actual function, and not an
+	 OVERLOAD.  */
+      fn = OVL_CURRENT (fn);
+
+      /* If the FN is a member function, make sure that it is
+	 accessible.  */
+      if (DECL_CLASS_SCOPE_P (fn))
+	enforce_access (type, fn);
+
+      if (pass == 0)
+	args = tree_cons (NULL_TREE, addr, args);
+      else
+	args = tree_cons (NULL_TREE, addr, 
+			  build_tree_list (NULL_TREE, size));
+
+      return build_function_call (fn, args);
     }
 
   /* If we are doing placement delete we do nothing if we don't find a
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index 1ac6f739de8d586804c90f5b0662df2be477afeb..6bce1937652eda80f04f6c76778c0407ac093893 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -2293,6 +2293,9 @@ build_new_1 (exp)
      beginning of the storage allocated for an array-new expression in
      order to store the number of elements.  */
   tree cookie_size = NULL_TREE;
+  /* True if the function we are calling is a placement allocation
+     function.  */
+  bool placement_allocation_fn_p;
 
   placement = TREE_OPERAND (exp, 0);
   type = TREE_OPERAND (exp, 1);
@@ -2418,8 +2421,25 @@ build_new_1 (exp)
   if (alloc_call == error_mark_node)
     return error_mark_node;
 
-  if (alloc_call == NULL_TREE)
-    abort ();
+  /* The ALLOC_CALL should be a CALL_EXPR, and the first operand
+     should be the address of a known FUNCTION_DECL.  */
+  my_friendly_assert (TREE_CODE (alloc_call) == CALL_EXPR, 20000521);
+  t = TREE_OPERAND (alloc_call, 0);
+  my_friendly_assert (TREE_CODE (t) == ADDR_EXPR, 20000521);
+  t = TREE_OPERAND (t, 0);
+  my_friendly_assert (TREE_CODE (t) == FUNCTION_DECL, 20000521);
+  /* Now, check to see if this function is actually a placement
+     allocation function.  This can happen even when PLACEMENT is NULL
+     because we might have something like:
+
+       struct S { void* operator new (size_t, int i = 0); };
+
+     A call to `new S' will get this allocation function, even though
+     there is no explicit placement argument.  If there is more than
+     one argument, or there are variable arguments, then this is a
+     placement allocation function.  */
+  placement_allocation_fn_p 
+    = (type_num_arguments (TREE_TYPE (t)) > 1 || varargs_function_p (t));
 
   /*        unless an allocation function is declared with an empty  excep-
      tion-specification  (_except.spec_),  throw(), it indicates failure to
@@ -2536,7 +2556,8 @@ build_new_1 (exp)
 	  flags |= LOOKUP_SPECULATIVELY;
 
 	  cleanup = build_op_delete_call (dcode, alloc_node, size, flags,
-					  alloc_call);
+					  (placement_allocation_fn_p 
+					   ? alloc_call : NULL_TREE));
 
 	  /* Ack!  First we allocate the memory.  Then we set our sentry
 	     variable to true, and expand a cleanup that deletes the memory
diff --git a/gcc/testsuite/g++.old-deja/g++.other/new7.C b/gcc/testsuite/g++.old-deja/g++.other/new7.C
new file mode 100644
index 0000000000000000000000000000000000000000..35ec0bbac88c4d19701ce803fb9737a3a3f4b2b1
--- /dev/null
+++ b/gcc/testsuite/g++.old-deja/g++.other/new7.C
@@ -0,0 +1,39 @@
+// Origin: philip_martin@ntlworld.com
+
+#include <new>
+
+extern "C" void abort();
+
+bool new_flag = false;
+bool delete_flag = false;
+
+struct X {
+  X()
+  {
+    throw 1;
+  }
+  void* operator new ( std::size_t n ) throw ( std::bad_alloc )
+  {
+    new_flag = true;
+    return ::operator new( n );
+  }
+  void operator delete( void* p, std::size_t n ) throw()
+  {
+    delete_flag = true;
+    ::operator delete( p );
+  }
+};
+
+int
+main()
+{
+  try
+    {
+      X* x = new X; // gcc 3.0 fails to call operator delete when X::X throws
+    }
+  catch ( ... )
+    {
+    }
+  if ( ! new_flag || ! delete_flag )
+    ::abort();
+}