call.c (build_new_op): Fix copy error.

* call.c (build_new_op): Fix copy error.
	(build_op_new_call): New fn.
	(build_op_delete_call): New fn.
	* cp-tree.h: Declare them.
	* init.c (build_new): Use them.  Support placement delete.
	(build_x_delete): Use build_op_delete_call.
	(build_delete): Likewise.
	* decl2.c (delete_sanity): Likewise.
	(coerce_delete_type): Don't complain about placement delete.

Support placement delete.

From-SVN: r16501
This commit is contained in:
Jason Merrill 1997-11-15 08:36:38 +00:00 committed by Jason Merrill
parent df4791b95d
commit da4768fe2e
5 changed files with 187 additions and 55 deletions

View File

@ -1,3 +1,15 @@
Sat Nov 15 00:30:51 1997 Jason Merrill <jason@yorick.cygnus.com>
* call.c (build_new_op): Fix copy error.
(build_op_new_call): New fn.
(build_op_delete_call): New fn.
* cp-tree.h: Declare them.
* init.c (build_new): Use them. Support placement delete.
(build_x_delete): Use build_op_delete_call.
(build_delete): Likewise.
* decl2.c (delete_sanity): Likewise.
(coerce_delete_type): Don't complain about placement delete.
Thu Nov 13 01:52:36 1997 Jason Merrill <jason@yorick.cygnus.com>
* call.c (build_new_function_call): Remove unused 'obj' parm.

View File

@ -4861,8 +4861,8 @@ build_new_op (code, flags, arg1, arg2, arg3)
templates = scratch_tree_cons (NULL_TREE, fn, templates);
candidates
= add_template_candidate (candidates, fn, NULL_TREE,
this_arglist, TREE_TYPE
(fnname), LOOKUP_NORMAL);
this_arglist, TREE_TYPE (fnname),
flags);
}
else
candidates = add_function_candidate
@ -5071,6 +5071,143 @@ builtin:
}
}
/* Build up a call to operator new. This has to be handled differently
from other operators in the way lookup is handled; first members are
considered, then globals. CODE is either NEW_EXPR or VEC_NEW_EXPR.
TYPE is the type to be created. ARGS are any new-placement args.
FLAGS are the usual overloading flags. */
tree
build_op_new_call (code, type, args, flags)
enum tree_code code;
tree type, args;
int flags;
{
tree fnname = ansi_opname[code];
if (IS_AGGR_TYPE (type) && ! (flags & LOOKUP_GLOBAL)
&& (TYPE_GETS_NEW (type) & (1 << (code == VEC_NEW_EXPR))))
{
tree dummy = build1 (NOP_EXPR, build_pointer_type (type),
error_mark_node);
dummy = build_indirect_ref (dummy, "new");
return build_method_call (dummy, fnname, args, NULL_TREE, flags);
}
else
return build_new_function_call (lookup_name_nonclass (fnname), args);
}
/* Build a call to operator delete. This has to be handled very specially,
because the restrictions on what signatures match are different from all
other call instances. For a normal delete, only a delete taking (void *)
or (void *, size_t) is accepted. For a placement delete, only an exact
match with the placement new is accepted.
CODE is either DELETE_EXPR or VEC_DELETE_EXPR.
ADDR is the pointer to be deleted. For placement delete, it is also
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. */
tree
build_op_delete_call (code, addr, size, flags)
enum tree_code code;
tree addr, size;
int flags;
{
tree fn, fns, fnname, fntype, argtypes, args, type;
int placement;
if (addr == error_mark_node)
return error_mark_node;
type = TREE_TYPE (TREE_TYPE (addr));
fnname = ansi_opname[code];
if (IS_AGGR_TYPE (type) && ! (flags & LOOKUP_GLOBAL))
/* Here we make assumptions about how instantiate_type works. This comes
out as a simple TREE_LIST, so it looks like overloaded globals to
instantiate_type; this works out fine. If something changes we
might have to build this up like build_offset_ref does. */
fns = lookup_fnfields (TYPE_BINFO (type), fnname, 0);
else
fns = NULL_TREE;
if (fns == NULL_TREE)
fns = lookup_name_nonclass (fnname);
/* We can recognize a placement delete because of LOOKUP_SPECULATIVELY;
if we are doing placement delete we do nothing if we don't find a
matching op delete. */
placement = !!(flags & LOOKUP_SPECULATIVELY);
if (placement)
{
/* If placement, we are coming from build_new, and we know that addr
is the allocation expression, so extract the info we need from it.
Obviously, if the build_new process changes this may have to
change as well. */
/* The SAVE_EXPR. */
tree t = TREE_OPERAND (addr, 0);
/* The CALL_EXPR. */
t = TREE_OPERAND (t, 0);
/* The function. */
argtypes = TREE_OPERAND (TREE_OPERAND (t, 0), 0);
/* The second parm type. */
argtypes = TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (argtypes)));
/* The second argument. */
args = TREE_CHAIN (TREE_OPERAND (t, 1));
}
else
{
/* First try it without the size argument. */
argtypes = void_list_node;
args = NULL_TREE;
}
argtypes = tree_cons (NULL_TREE, ptr_type_node, argtypes);
fntype = build_function_type (void_type_node, argtypes);
/* Strip const and volatile from addr. */
if (type != TYPE_MAIN_VARIANT (type))
addr = cp_convert (build_pointer_type (TYPE_MAIN_VARIANT (type)), addr);
/* instantiate_type will always return a plain function; pretend it's
overloaded. */
if (TREE_CODE (fns) == FUNCTION_DECL)
fns = scratch_tree_cons (NULL_TREE, fns, NULL_TREE);
fn = instantiate_type (fntype, fns, 0);
if (fn != error_mark_node)
{
if (TREE_PURPOSE (fns))
/* TREE_PURPOSE is only set for lists of member functions. */
enforce_access (TREE_PURPOSE (fns), fn);
return build_function_call (fn, expr_tree_cons (NULL_TREE, addr, args));
}
if (placement)
return NULL_TREE;
/* Normal delete; now try to find a match including the size argument. */
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, 0);
if (fn != error_mark_node)
return build_function_call
(fn, expr_tree_cons (NULL_TREE, addr,
build_expr_list (NULL_TREE, size)));
cp_error ("no suitable operator delete for `%T'", type);
return error_mark_node;
}
/* If the current scope isn't allowed to access FUNCTION along
BASETYPE_PATH, give an error. */
static void
enforce_access (basetype_path, function)
tree basetype_path, function;

View File

@ -1957,6 +1957,8 @@ extern tree type_decays_to PROTO((tree));
extern tree build_user_type_conversion PROTO((tree, tree, int));
extern tree build_new_function_call PROTO((tree, tree));
extern tree build_new_op PROTO((enum tree_code, int, tree, tree, tree));
extern tree build_op_new_call PROTO((enum tree_code, tree, tree, int));
extern tree build_op_delete_call PROTO((enum tree_code, tree, tree, int));
extern int can_convert PROTO((tree, tree));
extern int can_convert_arg PROTO((tree, tree, tree));

View File

@ -1310,8 +1310,8 @@ delete_sanity (exp, size, doing_vec, use_global_delete)
{
/* Only do access checking here; we'll be calling op delete
from the destructor. */
tree tmp = build_opfncall (DELETE_EXPR, LOOKUP_NORMAL, t,
size_zero_node, NULL_TREE);
tree tmp = build_op_delete_call (DELETE_EXPR, t,
size_zero_node, LOOKUP_NORMAL);
if (tmp == error_mark_node)
return error_mark_node;
}
@ -2403,6 +2403,7 @@ coerce_delete_type (type)
|| TREE_VALUE (arg_types) != ptr_type_node)
e2 = 1, error ("`operator delete' takes type `void *' as first parameter");
#if 0
if (arg_types
&& TREE_CHAIN (arg_types)
&& TREE_CHAIN (arg_types) != void_list_node)
@ -2434,8 +2435,12 @@ coerce_delete_type (type)
arg_types = tree_cons (NULL_TREE, ptr_type_node, TREE_CHAIN (arg_types));
}
else e3 |= e1;
#endif
if (e3)
if (e2)
arg_types = tree_cons (NULL_TREE, ptr_type_node,
arg_types ? TREE_CHAIN (arg_types): NULL_TREE);
if (e2 || e1)
type = build_function_type (void_type_node, arg_types);
return type;

View File

@ -2493,18 +2493,9 @@ build_new (placement, decl, init, use_global_new)
}
/* Allocate the object. */
if (! use_global_new && TYPE_LANG_SPECIFIC (true_type)
&& (TYPE_GETS_NEW (true_type) & (1 << has_array)))
rval = build_opfncall (code, LOOKUP_NORMAL,
build_pointer_type (true_type), size, placement);
else if (placement)
{
rval = build_opfncall (code, LOOKUP_GLOBAL|LOOKUP_COMPLAIN,
ptr_type_node, size, placement);
rval = cp_convert (build_pointer_type (true_type), rval);
}
else if (! has_array && flag_this_is_variable > 0
&& TYPE_NEEDS_CONSTRUCTING (true_type) && init != void_type_node)
if (! has_array && ! placement && flag_this_is_variable > 0
&& TYPE_NEEDS_CONSTRUCTING (true_type) && init != void_type_node)
{
if (init == NULL_TREE || TREE_CODE (init) == TREE_LIST)
rval = NULL_TREE;
@ -2516,10 +2507,10 @@ build_new (placement, decl, init, use_global_new)
}
else
{
rval = build_builtin_call (build_pointer_type (true_type),
has_array ? BIVN : BIN,
build_expr_list (NULL_TREE, size));
TREE_CALLS_NEW (rval) = 1;
rval = build_op_new_call
(code, true_type, expr_tree_cons (NULL_TREE, size, placement),
LOOKUP_NORMAL | (use_global_new * LOOKUP_GLOBAL));
rval = cp_convert (build_pointer_type (true_type), rval);
}
if (flag_exceptions && rval)
@ -2719,27 +2710,27 @@ build_new (placement, decl, init, use_global_new)
an exception and the new-expression does not contain a
new-placement, then the deallocation function is called to free
the memory in which the object was being constructed. */
/* FIXME: handle placement delete. */
if (flag_exceptions && ! placement)
if (flag_exceptions && alloc_expr)
{
tree cleanup = alloc_expr;
enum tree_code dcode = has_array? VEC_DELETE_EXPR : DELETE_EXPR;
tree cleanup, args = NULL_TREE;
int flags = LOOKUP_NORMAL | (use_global_new * LOOKUP_GLOBAL);
/* All cleanups must last longer than normal. */
int yes = suspend_momentary ();
if (! use_global_new && TYPE_LANG_SPECIFIC (true_type)
&& (TYPE_GETS_DELETE (true_type) & (1 << has_array)))
cleanup = build_opfncall (has_array? VEC_DELETE_EXPR : DELETE_EXPR,
LOOKUP_NORMAL, cleanup, size, NULL_TREE);
else
cleanup = build_builtin_call
(void_type_node, has_array ? BIVD : BID,
build_expr_list (NULL_TREE, cleanup));
if (placement)
flags |= LOOKUP_SPECULATIVELY;
cleanup = build_op_delete_call (dcode, alloc_expr, size, flags);
resume_momentary (yes);
rval = build (TRY_CATCH_EXPR, TREE_TYPE (rval), rval, cleanup);
rval = build (COMPOUND_EXPR, TREE_TYPE (rval), alloc_expr, rval);
if (cleanup)
{
rval = build (TRY_CATCH_EXPR, TREE_TYPE (rval), rval, cleanup);
rval = build (COMPOUND_EXPR, TREE_TYPE (rval), alloc_expr, rval);
}
}
}
else if (TYPE_READONLY (true_type))
@ -3196,16 +3187,10 @@ build_x_delete (type, addr, which_delete, virtual_size)
{
int use_global_delete = which_delete & 1;
int use_vec_delete = !!(which_delete & 2);
tree rval;
enum tree_code code = use_vec_delete ? VEC_DELETE_EXPR : DELETE_EXPR;
int flags = LOOKUP_NORMAL | (use_global_delete * LOOKUP_GLOBAL);
if (! use_global_delete && TYPE_LANG_SPECIFIC (TREE_TYPE (type))
&& (TYPE_GETS_DELETE (TREE_TYPE (type)) & (1 << use_vec_delete)))
rval = build_opfncall (code, LOOKUP_NORMAL, addr, virtual_size, NULL_TREE);
else
rval = build_builtin_call (void_type_node, use_vec_delete ? BIVD : BID,
build_expr_list (NULL_TREE, addr));
return rval;
return build_op_delete_call (code, addr, virtual_size, flags);
}
/* Generate a call to a destructor. TYPE is the type to cast ADDR to.
@ -3302,18 +3287,9 @@ build_delete (type, addr, auto_delete, flags, use_global_delete)
if (auto_delete == integer_zero_node)
return void_zero_node;
/* Pass the size of the object down to the operator delete() in
addition to the ADDR. */
if (TYPE_GETS_REG_DELETE (type) && !use_global_delete)
{
tree virtual_size = c_sizeof_nowarn (type);
return build_opfncall (DELETE_EXPR, LOOKUP_NORMAL, addr,
virtual_size, NULL_TREE);
}
/* Call the builtin operator delete. */
return build_builtin_call (void_type_node, BID,
build_expr_list (NULL_TREE, addr));
return build_op_delete_call
(DELETE_EXPR, addr, c_sizeof_nowarn (type),
LOOKUP_NORMAL | (use_global_delete * LOOKUP_GLOBAL));
}
/* Below, we will reverse the order in which these calls are made.