* 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: r16500
+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.
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
}
}
+/* 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;
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));
{
/* 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;
}
|| 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)
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;
}
/* 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;
}
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)
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))
{
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.
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.