/* Handle the hair of processing (but not expanding) inline functions.
Also manage function and variable name overloading.
- Copyright (C) 1987, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+ Copyright (C) 1987, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
+ 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
Contributed by Michael Tiemann (tiemann@cygnus.com)
This file is part of GCC.
-
+
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
#include "toplev.h"
#include "tm_p.h"
#include "target.h"
+#include "tree-pass.h"
+#include "diagnostic.h"
/* Various flags to control the mangling process. */
static tree locate_dtor (tree, void *);
static tree locate_ctor (tree, void *);
static tree locate_copy (tree, void *);
-#ifdef ASM_OUTPUT_DEF
static tree make_alias_for_thunk (tree);
-#endif
/* Called once to initialize method.c. */
{
init_mangle ();
}
-
-\f
-/* Set the mangled name (DECL_ASSEMBLER_NAME) for DECL. */
-
-void
-set_mangled_name_for_decl (tree decl)
-{
- if (processing_template_decl)
- /* There's no need to mangle the name of a template function. */
- return;
-
- mangle_decl (decl);
-}
-
-\f
-/* This function takes an identifier, ID, and attempts to figure out what
- it means. There are a number of possible scenarios, presented in increasing
- order of hair:
-
- 1) not in a class's scope
- 2) in class's scope, member name of the class's method
- 3) in class's scope, but not a member name of the class
- 4) in class's scope, member name of a class's variable
-
- NAME is $1 from the bison rule. It is an IDENTIFIER_NODE.
- VALUE is $$ from the bison rule. It is the value returned by lookup_name ($1)
-
- As a last ditch, try to look up the name as a label and return that
- address.
-
- Values which are declared as being of REFERENCE_TYPE are
- automatically dereferenced here (as a hack to make the
- compiler faster). */
-
-tree
-hack_identifier (tree value, tree name)
-{
- tree type;
-
- if (value == error_mark_node)
- return error_mark_node;
-
- type = TREE_TYPE (value);
- if (TREE_CODE (value) == FIELD_DECL)
- value = finish_non_static_data_member (value, current_class_ref,
- /*qualifying_scope=*/NULL_TREE);
- else if ((TREE_CODE (value) == FUNCTION_DECL
- && DECL_FUNCTION_MEMBER_P (value))
- || (TREE_CODE (value) == OVERLOAD
- && DECL_FUNCTION_MEMBER_P (OVL_CURRENT (value))))
- {
- tree decl;
-
- if (TREE_CODE (value) == OVERLOAD)
- value = OVL_CURRENT (value);
-
- decl = maybe_dummy_object (DECL_CONTEXT (value), 0);
- value = finish_class_member_access_expr (decl, name);
- }
- else if (really_overloaded_fn (value))
- ;
- else if (TREE_CODE (value) == OVERLOAD)
- /* not really overloaded function */
- mark_used (OVL_FUNCTION (value));
- else if (TREE_CODE (value) == TREE_LIST)
- {
- /* Ambiguous reference to base members, possibly other cases?. */
- tree t = value;
- while (t && TREE_CODE (t) == TREE_LIST)
- {
- mark_used (TREE_VALUE (t));
- t = TREE_CHAIN (t);
- }
- }
- else if (TREE_CODE (value) == NAMESPACE_DECL)
- {
- error ("use of namespace `%D' as expression", value);
- return error_mark_node;
- }
- else if (DECL_CLASS_TEMPLATE_P (value))
- {
- error ("use of class template `%T' as expression", value);
- return error_mark_node;
- }
- else
- mark_used (value);
-
- if (TREE_CODE (value) == VAR_DECL || TREE_CODE (value) == PARM_DECL
- || TREE_CODE (value) == RESULT_DECL)
- {
- tree context = decl_function_context (value);
- if (context != NULL_TREE && context != current_function_decl
- && ! TREE_STATIC (value))
- {
- error ("use of %s from containing function",
- (TREE_CODE (value) == VAR_DECL
- ? "`auto' variable" : "parameter"));
- cp_error_at (" `%#D' declared here", value);
- value = error_mark_node;
- }
- }
-
- if (DECL_P (value) && DECL_NONLOCAL (value))
- {
- if (DECL_CLASS_SCOPE_P (value)
- && DECL_CONTEXT (value) != current_class_type)
- {
- tree path;
- path = currently_open_derived_class (DECL_CONTEXT (value));
- perform_or_defer_access_check (TYPE_BINFO (path), value);
- }
- }
- else if (TREE_CODE (value) == TREE_LIST
- && TREE_TYPE (value) == error_mark_node)
- {
- error ("\
-request for member `%D' is ambiguous in multiple inheritance lattice",
- name);
- print_candidates (value);
- return error_mark_node;
- }
-
- if (! processing_template_decl)
- value = convert_from_reference (value);
- return value;
-}
-
\f
/* Return a this or result adjusting thunk to FUNCTION. THIS_ADJUSTING
indicates whether it is a this or result adjusting thunk.
{
HOST_WIDE_INT d;
tree thunk;
-
- my_friendly_assert (TREE_CODE (function) == FUNCTION_DECL, 20021025);
+
+ gcc_assert (TREE_CODE (function) == FUNCTION_DECL);
/* We can have this thunks to covariant thunks, but not vice versa. */
- my_friendly_assert (!DECL_THIS_THUNK_P (function), 20021127);
-
+ gcc_assert (!DECL_THIS_THUNK_P (function));
+ gcc_assert (!DECL_RESULT_THUNK_P (function) || this_adjusting);
+
/* Scale the VIRTUAL_OFFSET to be in terms of bytes. */
if (this_adjusting && virtual_offset)
- virtual_offset
+ virtual_offset
= size_binop (MULT_EXPR,
- virtual_offset,
- convert (ssizetype,
- TYPE_SIZE_UNIT (vtable_entry_type)));
-
+ virtual_offset,
+ convert (ssizetype,
+ TYPE_SIZE_UNIT (vtable_entry_type)));
+
d = tree_low_cst (fixed_offset, 0);
-
+
/* See if we already have the thunk in question. For this_adjusting
thunks VIRTUAL_OFFSET will be an INTEGER_CST, for covariant thunks it
will be a BINFO. */
for (thunk = DECL_THUNKS (function); thunk; thunk = TREE_CHAIN (thunk))
if (DECL_THIS_THUNK_P (thunk) == this_adjusting
- && THUNK_FIXED_OFFSET (thunk) == d
- && (this_adjusting
- ? (!THUNK_VIRTUAL_OFFSET (thunk) == !virtual_offset
- && (!virtual_offset
- || tree_int_cst_equal (THUNK_VIRTUAL_OFFSET (thunk),
- virtual_offset)))
- : THUNK_VIRTUAL_OFFSET (thunk) == virtual_offset))
+ && THUNK_FIXED_OFFSET (thunk) == d
+ && !virtual_offset == !THUNK_VIRTUAL_OFFSET (thunk)
+ && (!virtual_offset
+ || (this_adjusting
+ ? tree_int_cst_equal (THUNK_VIRTUAL_OFFSET (thunk),
+ virtual_offset)
+ : THUNK_VIRTUAL_OFFSET (thunk) == virtual_offset)))
return thunk;
-
+
/* All thunks must be created before FUNCTION is actually emitted;
the ABI requires that all thunks be emitted together with the
function to which they transfer control. */
- my_friendly_assert (!TREE_ASM_WRITTEN (function), 20021025);
+ gcc_assert (!TREE_ASM_WRITTEN (function));
+ /* Likewise, we can only be adding thunks to a function declared in
+ the class currently being laid out. */
+ gcc_assert (TYPE_SIZE (DECL_CONTEXT (function))
+ && TYPE_BEING_DEFINED (DECL_CONTEXT (function)));
thunk = build_decl (FUNCTION_DECL, NULL_TREE, TREE_TYPE (function));
DECL_LANG_SPECIFIC (thunk) = DECL_LANG_SPECIFIC (function);
cxx_dup_lang_specific_decl (thunk);
+ DECL_THUNKS (thunk) = NULL_TREE;
+
DECL_CONTEXT (thunk) = DECL_CONTEXT (function);
TREE_READONLY (thunk) = TREE_READONLY (function);
TREE_THIS_VOLATILE (thunk) = TREE_THIS_VOLATILE (function);
THUNK_TARGET (thunk) = function;
THUNK_FIXED_OFFSET (thunk) = d;
THUNK_VIRTUAL_OFFSET (thunk) = virtual_offset;
-
+ THUNK_ALIAS (thunk) = NULL_TREE;
+
/* The thunk itself is not a constructor or destructor, even if
the thing it is thunking to is. */
DECL_INTERFACE_KNOWN (thunk) = 1;
DECL_SAVED_FUNCTION_DATA (thunk) = NULL;
DECL_DESTRUCTOR_P (thunk) = 0;
DECL_CONSTRUCTOR_P (thunk) = 0;
- /* And neither is it a clone. */
- DECL_CLONED_FUNCTION (thunk) = NULL_TREE;
DECL_EXTERNAL (thunk) = 1;
DECL_ARTIFICIAL (thunk) = 1;
/* Even if this thunk is a member of a local class, we don't
DECL_DECLARED_INLINE_P (thunk) = 0;
/* Nor has it been deferred. */
DECL_DEFERRED_FN (thunk) = 0;
+
/* Add it to the list of thunks associated with FUNCTION. */
TREE_CHAIN (thunk) = DECL_THUNKS (function);
DECL_THUNKS (function) = thunk;
tree fixed_offset = ssize_int (THUNK_FIXED_OFFSET (thunk));
tree virtual_offset = THUNK_VIRTUAL_OFFSET (thunk);
- my_friendly_assert (!DECL_NAME (thunk) && DECL_THUNK_P (thunk), 20021127);
+ gcc_assert (!DECL_NAME (thunk) && DECL_THUNK_P (thunk));
if (virtual_offset && DECL_RESULT_THUNK_P (thunk))
virtual_offset = BINFO_VPTR_FIELD (virtual_offset);
function = THUNK_TARGET (thunk);
name = mangle_thunk (function, DECL_THIS_THUNK_P (thunk),
fixed_offset, virtual_offset);
+
+ /* We can end up with declarations of (logically) different
+ covariant thunks, that do identical adjustments. The two thunks
+ will be adjusting between within different hierarchies, which
+ happen to have the same layout. We must nullify one of them to
+ refer to the other. */
+ if (DECL_RESULT_THUNK_P (thunk))
+ {
+ tree cov_probe;
+
+ for (cov_probe = DECL_THUNKS (function);
+ cov_probe; cov_probe = TREE_CHAIN (cov_probe))
+ if (DECL_NAME (cov_probe) == name)
+ {
+ gcc_assert (!DECL_THUNKS (thunk));
+ THUNK_ALIAS (thunk) = (THUNK_ALIAS (cov_probe)
+ ? THUNK_ALIAS (cov_probe) : cov_probe);
+ break;
+ }
+ }
+
DECL_NAME (thunk) = name;
SET_DECL_ASSEMBLER_NAME (thunk, name);
}
{
if (this_adjusting)
/* Adjust the pointer by the constant. */
- ptr = fold (build (PLUS_EXPR, TREE_TYPE (ptr), ptr,
- ssize_int (fixed_offset)));
+ ptr = fold_build2 (PLUS_EXPR, TREE_TYPE (ptr), ptr,
+ ssize_int (fixed_offset));
/* If there's a virtual offset, look up that value in the vtable and
adjust the pointer again. */
ptr = save_expr (ptr);
/* The vptr is always at offset zero in the object. */
vtable = build1 (NOP_EXPR,
- build_pointer_type (build_pointer_type
+ build_pointer_type (build_pointer_type
(vtable_entry_type)),
ptr);
/* Form the vtable address. */
vtable = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (vtable)), vtable);
/* Find the entry with the vcall offset. */
- vtable = build (PLUS_EXPR, TREE_TYPE (vtable), vtable, virtual_offset);
+ vtable = build2 (PLUS_EXPR, TREE_TYPE (vtable), vtable, virtual_offset);
/* Get the offset itself. */
vtable = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (vtable)), vtable);
/* Adjust the `this' pointer. */
- ptr = fold (build (PLUS_EXPR, TREE_TYPE (ptr), ptr, vtable));
+ ptr = fold_build2 (PLUS_EXPR, TREE_TYPE (ptr), ptr, vtable);
}
-
+
if (!this_adjusting)
/* Adjust the pointer by the constant. */
- ptr = fold (build (PLUS_EXPR, TREE_TYPE (ptr), ptr,
- ssize_int (fixed_offset)));
+ ptr = fold_build2 (PLUS_EXPR, TREE_TYPE (ptr), ptr,
+ ssize_int (fixed_offset));
return ptr;
}
-/* Garbage collector tables contains thunk_labelno even when places
- inside ifdef block. */
static GTY (()) int thunk_labelno;
-#ifdef ASM_OUTPUT_DEF
/* Create a static alias to function. */
-static tree
-make_alias_for_thunk (tree function)
+tree
+make_alias_for (tree function, tree newid)
{
- tree alias;
- char buf[256];
-
- ASM_GENERATE_INTERNAL_LABEL (buf, "LTHUNK", thunk_labelno);
- thunk_labelno++;
- alias = build_decl (FUNCTION_DECL, get_identifier (buf),
- TREE_TYPE (function));
+ tree alias = build_decl (FUNCTION_DECL, newid, TREE_TYPE (function));
DECL_LANG_SPECIFIC (alias) = DECL_LANG_SPECIFIC (function);
cxx_dup_lang_specific_decl (alias);
DECL_CONTEXT (alias) = NULL;
TREE_USED (alias) = 1;
SET_DECL_ASSEMBLER_NAME (alias, DECL_NAME (alias));
TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (alias)) = 1;
+ return alias;
+}
+
+static tree
+make_alias_for_thunk (tree function)
+{
+ tree alias;
+ char buf[256];
+
+ ASM_GENERATE_INTERNAL_LABEL (buf, "LTHUNK", thunk_labelno);
+ thunk_labelno++;
+
+ alias = make_alias_for (function, get_identifier (buf));
+
if (!flag_syntax_only)
assemble_alias (alias, DECL_ASSEMBLER_NAME (function));
+
return alias;
}
-#endif
/* Emit the definition of a C++ multiple inheritance or covariant
return vtable thunk. If EMIT_P is nonzero, the thunk is emitted
void
use_thunk (tree thunk_fndecl, bool emit_p)
{
- tree function, alias;
+ tree a, t, function, alias;
tree virtual_offset;
HOST_WIDE_INT fixed_offset, virtual_value;
bool this_adjusting = DECL_THIS_THUNK_P (thunk_fndecl);
/* We should have called finish_thunk to give it a name. */
- my_friendly_assert (DECL_NAME (thunk_fndecl), 20021127);
+ gcc_assert (DECL_NAME (thunk_fndecl));
+
+ /* We should never be using an alias, always refer to the
+ aliased thunk. */
+ gcc_assert (!THUNK_ALIAS (thunk_fndecl));
if (TREE_ASM_WRITTEN (thunk_fndecl))
return;
-
+
function = THUNK_TARGET (thunk_fndecl);
if (DECL_RESULT (thunk_fndecl))
/* We already turned this thunk into an ordinary function.
There's no need to process this thunk again. */
return;
+ if (DECL_THUNK_P (function))
+ /* The target is itself a thunk, process it now. */
+ use_thunk (function, emit_p);
+
/* Thunks are always addressable; they only appear in vtables. */
TREE_ADDRESSABLE (thunk_fndecl) = 1;
this translation unit. */
TREE_ADDRESSABLE (function) = 1;
mark_used (function);
- TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (function)) = 1;
if (!emit_p)
return;
-#ifdef ASM_OUTPUT_DEF
- alias = make_alias_for_thunk (function);
-#else
- alias = function;
-#endif
+ if (TARGET_USE_LOCAL_THUNK_ALIAS_P (function))
+ alias = make_alias_for_thunk (function);
+ else
+ alias = function;
fixed_offset = THUNK_FIXED_OFFSET (thunk_fndecl);
virtual_offset = THUNK_VIRTUAL_OFFSET (thunk_fndecl);
if (!this_adjusting)
virtual_offset = BINFO_VPTR_FIELD (virtual_offset);
virtual_value = tree_low_cst (virtual_offset, /*pos=*/0);
- my_friendly_assert (virtual_value, 20021026);
+ gcc_assert (virtual_value);
}
else
virtual_value = 0;
-
+
/* And, if we need to emit the thunk, it's used. */
mark_used (thunk_fndecl);
/* This thunk is actually defined. */
/* The linkage of the function may have changed. FIXME in linkage
rewrite. */
TREE_PUBLIC (thunk_fndecl) = TREE_PUBLIC (function);
+ DECL_VISIBILITY (thunk_fndecl) = DECL_VISIBILITY (function);
+ DECL_VISIBILITY_SPECIFIED (thunk_fndecl)
+ = DECL_VISIBILITY_SPECIFIED (function);
+ if (flag_weak && TREE_PUBLIC (thunk_fndecl))
+ comdat_linkage (thunk_fndecl);
if (flag_syntax_only)
{
push_to_top_level ();
-#ifdef ASM_OUTPUT_DEF
- if (targetm.have_named_sections)
+ if (TARGET_USE_LOCAL_THUNK_ALIAS_P (function)
+ && targetm.have_named_sections)
{
resolve_unique_section (function, 0, flag_function_sections);
DECL_SECTION_NAME (thunk_fndecl) = DECL_SECTION_NAME (function);
}
}
-#endif
/* The back-end expects DECL_INITIAL to contain a BLOCK, so we
create one. */
DECL_INITIAL (thunk_fndecl) = make_node (BLOCK);
- BLOCK_VARS (DECL_INITIAL (thunk_fndecl)) = DECL_ARGUMENTS (thunk_fndecl);
-
+
+ /* Set up cloned argument trees for the thunk. */
+ t = NULL_TREE;
+ for (a = DECL_ARGUMENTS (function); a; a = TREE_CHAIN (a))
+ {
+ tree x = copy_node (a);
+ TREE_CHAIN (x) = t;
+ DECL_CONTEXT (x) = thunk_fndecl;
+ SET_DECL_RTL (x, NULL_RTX);
+ t = x;
+ }
+ a = nreverse (t);
+ DECL_ARGUMENTS (thunk_fndecl) = a;
+ BLOCK_VARS (DECL_INITIAL (thunk_fndecl)) = a;
+
if (this_adjusting
&& targetm.asm_out.can_output_mi_thunk (thunk_fndecl, fixed_offset,
virtual_value, alias))
assemble_end_function (thunk_fndecl, fnname);
current_function_decl = 0;
cfun = 0;
- /* Because init_function_start increments this, we must
- decrement it. */
- immediate_size_expand--;
TREE_ASM_WRITTEN (thunk_fndecl) = 1;
}
else
just makes a call to the real function. Unfortunately, this
doesn't work for varargs. */
- tree a, t;
-
if (varargs_function_p (function))
- error ("generic thunk code fails for method `%#D' which uses `...'",
+ error ("generic thunk code fails for method %q#D which uses %<...%>",
function);
- /* Set up cloned argument trees for the thunk. */
- t = NULL_TREE;
- for (a = DECL_ARGUMENTS (function); a; a = TREE_CHAIN (a))
- {
- tree x = copy_node (a);
- TREE_CHAIN (x) = t;
- DECL_CONTEXT (x) = thunk_fndecl;
- t = x;
- }
- a = nreverse (t);
- DECL_ARGUMENTS (thunk_fndecl) = a;
DECL_RESULT (thunk_fndecl) = NULL_TREE;
- start_function (NULL_TREE, thunk_fndecl, NULL_TREE, SF_PRE_PARSED);
+ start_preparsed_function (thunk_fndecl, NULL_TREE, SF_PRE_PARSED);
/* We don't bother with a body block for thunks. */
/* There's no need to check accessibility inside the thunk body. */
if (this_adjusting)
t = thunk_adjust (t, /*this_adjusting=*/1,
fixed_offset, virtual_offset);
-
+
/* Build up the call to the real function. */
t = tree_cons (NULL_TREE, t, NULL_TREE);
for (a = TREE_CHAIN (a); a; a = TREE_CHAIN (a))
t = tree_cons (NULL_TREE, a, t);
t = nreverse (t);
t = build_call (alias, t);
- if (!this_adjusting)
- t = thunk_adjust (t, /*this_adjusting=*/0,
- fixed_offset, virtual_offset);
-
+ CALL_FROM_THUNK_P (t) = 1;
+
if (VOID_TYPE_P (TREE_TYPE (t)))
finish_expr_stmt (t);
else
- finish_return_stmt (t);
+ {
+ if (!this_adjusting)
+ {
+ tree cond = NULL_TREE;
+
+ if (TREE_CODE (TREE_TYPE (t)) == POINTER_TYPE)
+ {
+ /* If the return type is a pointer, we need to
+ protect against NULL. We know there will be an
+ adjustment, because that's why we're emitting a
+ thunk. */
+ t = save_expr (t);
+ cond = cp_convert (boolean_type_node, t);
+ }
+
+ t = thunk_adjust (t, /*this_adjusting=*/0,
+ fixed_offset, virtual_offset);
+ if (cond)
+ t = build3 (COND_EXPR, TREE_TYPE (t), cond, t,
+ cp_convert (TREE_TYPE (t), integer_zero_node));
+ }
+ t = force_target_expr (TREE_TYPE (t), t);
+ finish_return_stmt (t);
+ }
/* Since we want to emit the thunk, we explicitly mark its name as
referenced. */
- TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (thunk_fndecl)) = 1;
+ mark_decl_referenced (thunk_fndecl);
/* But we don't want debugging information about it. */
DECL_IGNORED_P (thunk_fndecl) = 1;
/* Re-enable access control. */
pop_deferring_access_checks ();
- expand_or_defer_fn (finish_function (0));
+ thunk_fndecl = finish_function (0);
+ tree_lowering_passes (thunk_fndecl);
+ expand_body (thunk_fndecl);
}
pop_from_top_level ();
do_build_copy_constructor (tree fndecl)
{
tree parm = FUNCTION_FIRST_USER_PARM (fndecl);
- tree t;
parm = convert_from_reference (parm);
if *this is a base subobject. */;
else if (TYPE_HAS_TRIVIAL_INIT_REF (current_class_type))
{
- t = build (INIT_EXPR, void_type_node, current_class_ref, parm);
+ tree t = build2 (INIT_EXPR, void_type_node, current_class_ref, parm);
finish_expr_stmt (t);
}
else
{
tree fields = TYPE_FIELDS (current_class_type);
- int n_bases = CLASSTYPE_N_BASECLASSES (current_class_type);
- tree binfos = TYPE_BINFO_BASETYPES (current_class_type);
tree member_init_list = NULL_TREE;
int cvquals = cp_type_quals (TREE_TYPE (parm));
int i;
+ tree binfo, base_binfo;
+ VEC(tree,gc) *vbases;
/* Initialize all the base-classes with the parameter converted
to their type so that we get their copy constructor and not
another constructor that takes current_class_type. We must
deal with the binfo's directly as a direct base might be
inaccessible due to ambiguity. */
- for (t = CLASSTYPE_VBASECLASSES (current_class_type); t;
- t = TREE_CHAIN (t))
+ for (vbases = CLASSTYPE_VBASECLASSES (current_class_type), i = 0;
+ VEC_iterate (tree, vbases, i, binfo); i++)
{
- tree binfo = TREE_VALUE (t);
-
- member_init_list
+ member_init_list
= tree_cons (binfo,
build_tree_list (NULL_TREE,
build_base_path (PLUS_EXPR, parm,
member_init_list);
}
- for (i = 0; i < n_bases; ++i)
+ for (binfo = TYPE_BINFO (current_class_type), i = 0;
+ BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
{
- tree binfo = TREE_VEC_ELT (binfos, i);
- if (TREE_VIA_VIRTUAL (binfo))
- continue;
+ if (BINFO_VIRTUAL_P (base_binfo))
+ continue;
- member_init_list
- = tree_cons (binfo,
+ member_init_list
+ = tree_cons (base_binfo,
build_tree_list (NULL_TREE,
build_base_path (PLUS_EXPR, parm,
- binfo, 1)),
+ base_binfo, 1)),
member_init_list);
}
for (; fields; fields = TREE_CHAIN (fields))
{
- tree init;
+ tree init = parm;
tree field = fields;
tree expr_type;
if (TREE_CODE (field) != FIELD_DECL)
continue;
- init = parm;
+ expr_type = TREE_TYPE (field);
if (DECL_NAME (field))
{
if (VFIELD_NAME_P (DECL_NAME (field)))
continue;
-
- /* True for duplicate members. */
- if (IDENTIFIER_CLASS_VALUE (DECL_NAME (field)) != field)
- continue;
}
- else if ((t = TREE_TYPE (field)) != NULL_TREE
- && ANON_AGGR_TYPE_P (t)
- && TYPE_FIELDS (t) != NULL_TREE)
+ else if (ANON_AGGR_TYPE_P (expr_type) && TYPE_FIELDS (expr_type))
/* Just use the field; anonymous types can't have
nontrivial copy ctors or assignment ops. */;
else
the field is "T", then the type will usually be "const
T". (There are no cv-qualified variants of reference
types.) */
- expr_type = TREE_TYPE (field);
if (TREE_CODE (expr_type) != REFERENCE_TYPE)
- expr_type = cp_build_qualified_type (expr_type, cvquals);
- init = build (COMPONENT_REF, expr_type, init, field);
+ {
+ int quals = cvquals;
+
+ if (DECL_MUTABLE_P (field))
+ quals &= ~TYPE_QUAL_CONST;
+ expr_type = cp_build_qualified_type (expr_type, quals);
+ }
+
+ init = build3 (COMPONENT_REF, expr_type, init, field, NULL_TREE);
init = build_tree_list (NULL_TREE, init);
- member_init_list
- = tree_cons (field, init, member_init_list);
+ member_init_list = tree_cons (field, init, member_init_list);
}
finish_mem_initializers (member_init_list);
}
tree parm = TREE_CHAIN (DECL_ARGUMENTS (fndecl));
tree compound_stmt;
- compound_stmt = begin_compound_stmt (/*has_no_scope=*/0);
+ compound_stmt = begin_compound_stmt (0);
parm = convert_from_reference (parm);
if (TYPE_HAS_TRIVIAL_ASSIGN_REF (current_class_type)
if *this is a base subobject. */;
else if (TYPE_HAS_TRIVIAL_ASSIGN_REF (current_class_type))
{
- tree t = build (MODIFY_EXPR, void_type_node, current_class_ref, parm);
+ tree t = build2 (MODIFY_EXPR, void_type_node, current_class_ref, parm);
finish_expr_stmt (t);
}
else
tree fields;
int cvquals = cp_type_quals (TREE_TYPE (parm));
int i;
+ tree binfo, base_binfo;
- /* Assign to each of thedirect base classes. */
- for (i = 0; i < CLASSTYPE_N_BASECLASSES (current_class_type); ++i)
+ /* Assign to each of the direct base classes. */
+ for (binfo = TYPE_BINFO (current_class_type), i = 0;
+ BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
{
- tree binfo;
tree converted_parm;
- binfo = BINFO_BASETYPE (TYPE_BINFO (current_class_type), i);
/* We must convert PARM directly to the base class
explicitly since the base class may be ambiguous. */
- converted_parm = build_base_path (PLUS_EXPR, parm, binfo, 1);
+ converted_parm = build_base_path (PLUS_EXPR, parm, base_binfo, 1);
/* Call the base class assignment operator. */
- finish_expr_stmt
- (build_special_member_call (current_class_ref,
+ finish_expr_stmt
+ (build_special_member_call (current_class_ref,
ansi_assopname (NOP_EXPR),
- build_tree_list (NULL_TREE,
+ build_tree_list (NULL_TREE,
converted_parm),
- binfo,
+ base_binfo,
LOOKUP_NORMAL | LOOKUP_NONVIRTUAL));
}
/* Assign to each of the non-static data members. */
- for (fields = TYPE_FIELDS (current_class_type);
- fields;
+ for (fields = TYPE_FIELDS (current_class_type);
+ fields;
fields = TREE_CHAIN (fields))
{
- tree comp, init, t;
+ tree comp = current_class_ref;
+ tree init = parm;
tree field = fields;
+ tree expr_type;
+ int quals;
if (TREE_CODE (field) != FIELD_DECL || DECL_ARTIFICIAL (field))
continue;
- if (CP_TYPE_CONST_P (TREE_TYPE (field)))
+ expr_type = TREE_TYPE (field);
+
+ if (CP_TYPE_CONST_P (expr_type))
{
- error ("non-static const member `%#D', can't use default assignment operator", field);
+ error ("non-static const member %q#D, can't use default "
+ "assignment operator", field);
continue;
}
- else if (TREE_CODE (TREE_TYPE (field)) == REFERENCE_TYPE)
+ else if (TREE_CODE (expr_type) == REFERENCE_TYPE)
{
- error ("non-static reference member `%#D', can't use default assignment operator", field);
+ error ("non-static reference member %q#D, can't use "
+ "default assignment operator", field);
continue;
}
- comp = current_class_ref;
- init = parm;
-
if (DECL_NAME (field))
{
if (VFIELD_NAME_P (DECL_NAME (field)))
continue;
-
- /* True for duplicate members. */
- if (IDENTIFIER_CLASS_VALUE (DECL_NAME (field)) != field)
- continue;
}
- else if ((t = TREE_TYPE (field)) != NULL_TREE
- && ANON_AGGR_TYPE_P (t)
- && TYPE_FIELDS (t) != NULL_TREE)
+ else if (ANON_AGGR_TYPE_P (expr_type)
+ && TYPE_FIELDS (expr_type) != NULL_TREE)
/* Just use the field; anonymous types can't have
nontrivial copy ctors or assignment ops. */;
else
continue;
- comp = build (COMPONENT_REF, TREE_TYPE (field), comp, field);
- init = build (COMPONENT_REF,
- cp_build_qualified_type (TREE_TYPE (field), cvquals),
- init, field);
+ comp = build3 (COMPONENT_REF, expr_type, comp, field, NULL_TREE);
+
+ /* Compute the type of init->field */
+ quals = cvquals;
+ if (DECL_MUTABLE_P (field))
+ quals &= ~TYPE_QUAL_CONST;
+ expr_type = cp_build_qualified_type (expr_type, quals);
+
+ init = build3 (COMPONENT_REF, expr_type, init, field, NULL_TREE);
if (DECL_NAME (field))
- finish_expr_stmt (build_modify_expr (comp, NOP_EXPR, init));
+ init = build_modify_expr (comp, NOP_EXPR, init);
else
- finish_expr_stmt (build (MODIFY_EXPR, TREE_TYPE (comp), comp,
- init));
+ init = build2 (MODIFY_EXPR, TREE_TYPE (comp), comp, init);
+ finish_expr_stmt (init);
}
}
finish_return_stmt (current_class_ref);
- finish_compound_stmt (/*has_no_scope=*/0, compound_stmt);
+ finish_compound_stmt (compound_stmt);
}
+/* Synthesize FNDECL, a non-static member function. */
+
void
synthesize_method (tree fndecl)
{
tree context = decl_function_context (fndecl);
bool need_body = true;
tree stmt;
+ location_t save_input_location = input_location;
+ int error_count = errorcount;
+ int warning_count = warningcount;
- if (at_eof)
- import_export_decl (fndecl);
+ /* Reset the source location, we might have been previously
+ deferred, and thus have saved where we were first needed. */
+ DECL_SOURCE_LOCATION (fndecl)
+ = DECL_SOURCE_LOCATION (TYPE_NAME (DECL_CONTEXT (fndecl)));
/* If we've been asked to synthesize a clone, just synthesize the
cloned function instead. Doing so will automatically fill in the
body for the clone. */
if (DECL_CLONED_FUNCTION_P (fndecl))
- {
- synthesize_method (DECL_CLONED_FUNCTION (fndecl));
- return;
- }
+ fndecl = DECL_CLONED_FUNCTION (fndecl);
/* We may be in the middle of deferred access check. Disable
it now. */
else if (nested)
push_function_context_to (context);
- /* Put the function definition at the position where it is needed,
- rather than within the body of the class. That way, an error
- during the generation of the implicit body points at the place
- where the attempt to generate the function occurs, giving the
- user a hint as to why we are attempting to generate the
- function. */
- DECL_SOURCE_LOCATION (fndecl) = input_location;
-
- interface_unknown = 1;
- start_function (NULL_TREE, fndecl, NULL_TREE, SF_DEFAULT | SF_PRE_PARSED);
- clear_last_expr ();
+ input_location = DECL_SOURCE_LOCATION (fndecl);
+
+ start_preparsed_function (fndecl, NULL_TREE, SF_DEFAULT | SF_PRE_PARSED);
stmt = begin_function_body ();
if (DECL_OVERLOADED_OPERATOR_P (fndecl) == NOP_EXPR)
if (need_body)
{
tree compound_stmt;
- compound_stmt = begin_compound_stmt (/*has_no_scope=*/0);
- finish_compound_stmt (/*has_no_scope=*/0, compound_stmt);
+ compound_stmt = begin_compound_stmt (BCS_FN_BODY);
+ finish_compound_stmt (compound_stmt);
}
finish_function_body (stmt);
expand_or_defer_fn (finish_function (0));
- extract_interface_info ();
+ input_location = save_input_location;
+
if (! context)
pop_from_top_level ();
else if (nested)
pop_function_context_from (context);
pop_deferring_access_checks ();
+
+ if (error_count != errorcount || warning_count != warningcount)
+ inform ("%Hsynthesized method %qD first required here ",
+ &input_location, fndecl);
}
/* Use EXTRACTOR to locate the relevant function called for each base &
static tree
synthesize_exception_spec (tree type, tree (*extractor) (tree, void*),
- void *client)
+ void *client)
{
tree raises = empty_except_spec;
tree fields = TYPE_FIELDS (type);
- int i, n_bases = CLASSTYPE_N_BASECLASSES (type);
- tree binfos = TYPE_BINFO_BASETYPES (type);
+ tree binfo, base_binfo;
+ int i;
- for (i = 0; i != n_bases; i++)
+ for (binfo = TYPE_BINFO (type), i = 0;
+ BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
{
- tree base = BINFO_TYPE (TREE_VEC_ELT (binfos, i));
- tree fn = (*extractor) (base, client);
+ tree fn = (*extractor) (BINFO_TYPE (base_binfo), client);
if (fn)
- {
- tree fn_raises = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn));
-
- raises = merge_exception_specifiers (raises, fn_raises);
- }
+ {
+ tree fn_raises = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn));
+
+ raises = merge_exception_specifiers (raises, fn_raises);
+ }
}
for (; fields; fields = TREE_CHAIN (fields))
{
tree type = TREE_TYPE (fields);
tree fn;
-
+
if (TREE_CODE (fields) != FIELD_DECL || DECL_ARTIFICIAL (fields))
- continue;
+ continue;
while (TREE_CODE (type) == ARRAY_TYPE)
- type = TREE_TYPE (type);
+ type = TREE_TYPE (type);
if (TREE_CODE (type) != RECORD_TYPE)
- continue;
-
+ continue;
+
fn = (*extractor) (type, client);
if (fn)
- {
- tree fn_raises = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn));
-
- raises = merge_exception_specifiers (raises, fn_raises);
- }
+ {
+ tree fn_raises = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (fn));
+
+ raises = merge_exception_specifiers (raises, fn_raises);
+ }
}
return raises;
}
static tree
locate_dtor (tree type, void *client ATTRIBUTE_UNUSED)
{
- tree fns;
-
- if (!TYPE_HAS_DESTRUCTOR (type))
- return NULL_TREE;
- fns = TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (type),
- CLASSTYPE_DESTRUCTOR_SLOT);
- return fns;
+ return CLASSTYPE_DESTRUCTORS (type);
}
/* Locate the default ctor of TYPE. */
locate_ctor (tree type, void *client ATTRIBUTE_UNUSED)
{
tree fns;
-
+
if (!TYPE_HAS_DEFAULT_CONSTRUCTOR (type))
return NULL_TREE;
-
- fns = TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (type),
- CLASSTYPE_CONSTRUCTOR_SLOT);
- for (; fns; fns = OVL_NEXT (fns))
+
+ /* Call lookup_fnfields_1 to create the constructor declarations, if
+ necessary. */
+ if (CLASSTYPE_LAZY_DEFAULT_CTOR (type))
+ return lazily_declare_fn (sfk_constructor, type);
+
+ for (fns = CLASSTYPE_CONSTRUCTORS (type); fns; fns = OVL_NEXT (fns))
{
tree fn = OVL_CURRENT (fns);
tree parms = TYPE_ARG_TYPES (TREE_TYPE (fn));
-
+
if (sufficient_parms_p (TREE_CHAIN (parms)))
- return fn;
+ return fn;
}
return NULL_TREE;
}
{
struct copy_data *client = (struct copy_data *)client_;
tree fns;
- int ix = -1;
tree best = NULL_TREE;
bool excess_p = false;
-
+
if (client->name)
{
- if (TYPE_HAS_ASSIGN_REF (type))
- ix = lookup_fnfields_1 (type, client->name);
+ int ix;
+ ix = lookup_fnfields_1 (type, client->name);
+ if (ix < 0)
+ return NULL_TREE;
+ fns = VEC_index (tree, CLASSTYPE_METHOD_VEC (type), ix);
}
else if (TYPE_HAS_INIT_REF (type))
- ix = CLASSTYPE_CONSTRUCTOR_SLOT;
- if (ix < 0)
+ {
+ /* If construction of the copy constructor was postponed, create
+ it now. */
+ if (CLASSTYPE_LAZY_COPY_CTOR (type))
+ lazily_declare_fn (sfk_copy_constructor, type);
+ fns = CLASSTYPE_CONSTRUCTORS (type);
+ }
+ else
return NULL_TREE;
- fns = TREE_VEC_ELT (CLASSTYPE_METHOD_VEC (type), ix);
-
for (; fns; fns = OVL_NEXT (fns))
{
tree fn = OVL_CURRENT (fns);
tree src_type;
int excess;
int quals;
-
+
parms = TREE_CHAIN (parms);
if (!parms)
- continue;
+ continue;
src_type = non_reference (TREE_VALUE (parms));
if (!same_type_ignoring_top_level_qualifiers_p (src_type, type))
- continue;
+ continue;
if (!sufficient_parms_p (TREE_CHAIN (parms)))
- continue;
+ continue;
quals = cp_type_quals (src_type);
if (client->quals & ~quals)
- continue;
+ continue;
excess = quals & ~client->quals;
if (!best || (excess_p && !excess))
- {
- best = fn;
- excess_p = excess;
- }
+ {
+ best = fn;
+ excess_p = excess;
+ }
else
- /* Ambiguous */
- return NULL_TREE;
+ /* Ambiguous */
+ return NULL_TREE;
}
return best;
}
/* Implicitly declare the special function indicated by KIND, as a
member of TYPE. For copy constructors and assignment operators,
CONST_P indicates whether these functions should take a const
- reference argument or a non-const reference. */
+ reference argument or a non-const reference. Returns the
+ FUNCTION_DECL for the implicitly declared function. */
tree
implicitly_declare_fn (special_function_kind kind, tree type, bool const_p)
{
- tree declspecs = NULL_TREE;
- tree fn, args = NULL_TREE;
+ tree fn;
+ tree parameter_types = void_list_node;
+ tree return_type;
+ tree fn_type;
tree raises = empty_except_spec;
- bool retref = false;
- bool has_parm = false;
- tree name = constructor_name (type);
+ tree rhs_parm_type = NULL_TREE;
+ tree name;
+ HOST_WIDE_INT saved_processing_template_decl;
+
+ /* Because we create declarations for implictly declared functions
+ lazily, we may be creating the declaration for a member of TYPE
+ while in some completely different context. However, TYPE will
+ never be a dependent class (because we never want to do lookups
+ for implicitly defined functions in a dependent class).
+ Furthermore, we must set PROCESSING_TEMPLATE_DECL to zero here
+ because we only create clones for constructors and destructors
+ when not in a template. */
+ gcc_assert (!dependent_type_p (type));
+ saved_processing_template_decl = processing_template_decl;
+ processing_template_decl = 0;
+
+ type = TYPE_MAIN_VARIANT (type);
+
+ if (targetm.cxx.cdtor_returns_this () && !TYPE_FOR_JAVA (type))
+ {
+ if (kind == sfk_destructor)
+ /* See comment in check_special_function_return_type. */
+ return_type = build_pointer_type (void_type_node);
+ else
+ return_type = build_pointer_type (type);
+ }
+ else
+ return_type = void_type_node;
switch (kind)
{
case sfk_destructor:
/* Destructor. */
- name = build_nt (BIT_NOT_EXPR, name);
- args = void_list_node;
+ name = constructor_name (type);
raises = synthesize_exception_spec (type, &locate_dtor, 0);
break;
case sfk_constructor:
/* Default constructor. */
- args = void_list_node;
+ name = constructor_name (type);
raises = synthesize_exception_spec (type, &locate_ctor, 0);
break;
case sfk_assignment_operator:
{
struct copy_data data;
- tree argtype = type;
-
- has_parm = true;
+
data.name = NULL;
data.quals = 0;
if (kind == sfk_assignment_operator)
- {
- retref = true;
- declspecs = build_tree_list (NULL_TREE, type);
+ {
+ return_type = build_reference_type (type);
+ name = ansi_assopname (NOP_EXPR);
+ data.name = name;
+ }
+ else
+ name = constructor_name (type);
- name = ansi_assopname (NOP_EXPR);
- data.name = name;
- }
if (const_p)
- {
- data.quals = TYPE_QUAL_CONST;
- argtype = build_qualified_type (argtype, TYPE_QUAL_CONST);
- }
-
- argtype = build_reference_type (argtype);
- args = build_tree_list (hash_tree_chain (argtype, NULL_TREE),
- get_identifier ("_ctor_arg"));
- args = tree_cons (NULL_TREE, args, void_list_node);
-
+ {
+ data.quals = TYPE_QUAL_CONST;
+ rhs_parm_type = build_qualified_type (type, TYPE_QUAL_CONST);
+ }
+ else
+ rhs_parm_type = type;
+ rhs_parm_type = build_reference_type (rhs_parm_type);
+ parameter_types = tree_cons (NULL_TREE, rhs_parm_type, parameter_types);
raises = synthesize_exception_spec (type, &locate_copy, &data);
break;
}
default:
- abort ();
+ gcc_unreachable ();
}
- TREE_PARMLIST (args) = 1;
-
- {
- tree declarator = make_call_declarator (name, args, NULL_TREE, raises);
-
- if (retref)
- declarator = build_nt (ADDR_EXPR, declarator);
-
- fn = grokfield (declarator, declspecs, NULL_TREE, NULL_TREE, NULL_TREE);
- if (has_parm)
- TREE_USED (FUNCTION_FIRST_USER_PARM (fn)) = 1;
- }
-
- my_friendly_assert (TREE_CODE (fn) == FUNCTION_DECL, 20000408);
+ /* Create the function. */
+ fn_type = build_method_type_directly (type, return_type, parameter_types);
+ if (raises)
+ fn_type = build_exception_variant (fn_type, raises);
+ fn = build_lang_decl (FUNCTION_DECL, name, fn_type);
+ DECL_SOURCE_LOCATION (fn) = DECL_SOURCE_LOCATION (TYPE_NAME (type));
+ if (kind == sfk_constructor || kind == sfk_copy_constructor)
+ DECL_CONSTRUCTOR_P (fn) = 1;
+ else if (kind == sfk_destructor)
+ DECL_DESTRUCTOR_P (fn) = 1;
+ else
+ {
+ DECL_ASSIGNMENT_OPERATOR_P (fn) = 1;
+ SET_OVERLOADED_OPERATOR_CODE (fn, NOP_EXPR);
+ }
+ /* Create the argument list. The call to "grokclassfn" will add the
+ "this" parameter and any other implicit parameters. */
+ if (rhs_parm_type)
+ {
+ /* Note that this parameter is *not* marked DECL_ARTIFICIAL; we
+ want its type to be included in the mangled function
+ name. */
+ DECL_ARGUMENTS (fn) = cp_build_parm_decl (NULL_TREE, rhs_parm_type);
+ TREE_READONLY (DECL_ARGUMENTS (fn)) = 1;
+ }
+ grokclassfn (type, fn, kind == sfk_destructor ? DTOR_FLAG : NO_SPECIAL,
+ TYPE_UNQUALIFIED);
+ grok_special_member_properties (fn);
+ set_linkage_according_to_type (type, fn);
+ rest_of_decl_compilation (fn, toplevel_bindings_p (), at_eof);
+ DECL_IN_AGGR_P (fn) = 1;
DECL_ARTIFICIAL (fn) = 1;
DECL_NOT_REALLY_EXTERN (fn) = 1;
DECL_DECLARED_INLINE_P (fn) = 1;
DECL_INLINE (fn) = 1;
- defer_fn (fn);
-
+ gcc_assert (!TREE_USED (fn));
+
+ /* Restore PROCESSING_TEMPLATE_DECL. */
+ processing_template_decl = saved_processing_template_decl;
+
+ return fn;
+}
+
+/* Add an implicit declaration to TYPE for the kind of function
+ indicated by SFK. Return the FUNCTION_DECL for the new implicit
+ declaration. */
+
+tree
+lazily_declare_fn (special_function_kind sfk, tree type)
+{
+ tree fn;
+ bool const_p;
+
+ /* Figure out whether or not the argument has a const reference
+ type. */
+ if (sfk == sfk_copy_constructor)
+ const_p = TYPE_HAS_CONST_INIT_REF (type);
+ else if (sfk == sfk_assignment_operator)
+ const_p = TYPE_HAS_CONST_ASSIGN_REF (type);
+ else
+ /* In this case, CONST_P will be ignored. */
+ const_p = false;
+ /* Declare the function. */
+ fn = implicitly_declare_fn (sfk, type, const_p);
+ /* A destructor may be virtual. */
+ if (sfk == sfk_destructor)
+ check_for_override (fn, type);
+ /* Add it to CLASSTYPE_METHOD_VEC. */
+ add_method (type, fn, NULL_TREE);
+ /* Add it to TYPE_METHODS. */
+ if (sfk == sfk_destructor
+ && DECL_VIRTUAL_P (fn)
+ && abi_version_at_least (2))
+ /* The ABI requires that a virtual destructor go at the end of the
+ vtable. */
+ TYPE_METHODS (type) = chainon (TYPE_METHODS (type), fn);
+ else
+ {
+ /* G++ 3.2 put the implicit destructor at the *beginning* of the
+ TYPE_METHODS list, which cause the destructor to be emitted
+ in an incorrect location in the vtable. */
+ if (warn_abi && DECL_VIRTUAL_P (fn))
+ warning (0, "vtable layout for class %qT may not be ABI-compliant"
+ "and may change in a future version of GCC due to "
+ "implicit virtual destructor",
+ type);
+ TREE_CHAIN (fn) = TYPE_METHODS (type);
+ TYPE_METHODS (type) = fn;
+ }
+ maybe_add_class_template_decl_list (type, fn, /*friend_p=*/0);
+ if (sfk == sfk_assignment_operator)
+ CLASSTYPE_LAZY_ASSIGNMENT_OP (type) = 0;
+ else
+ {
+ /* Remember that the function has been created. */
+ if (sfk == sfk_constructor)
+ CLASSTYPE_LAZY_DEFAULT_CTOR (type) = 0;
+ else if (sfk == sfk_copy_constructor)
+ CLASSTYPE_LAZY_COPY_CTOR (type) = 0;
+ else if (sfk == sfk_destructor)
+ CLASSTYPE_LAZY_DESTRUCTOR (type) = 0;
+ /* Create appropriate clones. */
+ clone_function_decl (fn, /*update_method_vec=*/true);
+ }
+
return fn;
}