From 3f41ffd8c39bedd88fbf0f7fcc42559397d69103 Mon Sep 17 00:00:00 2001 From: Mark Mitchell Date: Tue, 22 May 2001 00:31:36 +0000 Subject: [PATCH] call.c (build_op_delete_call): Ignore exception-specifications when looking for matching delete operators. * 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. From-SVN: r42413 --- gcc/cp/ChangeLog | 8 ++++ gcc/cp/call.c | 53 ++++++++++++++++----- gcc/cp/init.c | 27 +++++++++-- gcc/testsuite/g++.old-deja/g++.other/new7.C | 39 +++++++++++++++ 4 files changed, 111 insertions(+), 16 deletions(-) create mode 100644 gcc/testsuite/g++.old-deja/g++.other/new7.C diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index eda85ecfa41..5d6ca8f6020 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,11 @@ +2001-05-21 Mark Mitchell + + * 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 * 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 6694f563d4b..5367cc55805 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); + tree t; - 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); + /* 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 1ac6f739de8..6bce1937652 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 00000000000..35ec0bbac88 --- /dev/null +++ b/gcc/testsuite/g++.old-deja/g++.other/new7.C @@ -0,0 +1,39 @@ +// Origin: philip_martin@ntlworld.com + +#include + +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(); +}