/* 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.
+ 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
Contributed by Michael Tiemann (tiemann@cygnus.com)
This file is part of GCC.
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. */
}
\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,
- /*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));
- enforce_access (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.
FIXED_OFFSET and VIRTUAL_OFFSET indicate how to do the adjustment
tree thunk;
my_friendly_assert (TREE_CODE (function) == FUNCTION_DECL, 20021025);
- /* We can have this thunks to covariant thunks, but not vice versa. */
+ /* We can have this thunks to covariant thunks, but not vice versa. */
my_friendly_assert (!DECL_THIS_THUNK_P (function), 20021127);
+ my_friendly_assert (!DECL_RESULT_THUNK_P (function) || this_adjusting,
+ 20031123);
/* Scale the VIRTUAL_OFFSET to be in terms of bytes. */
if (this_adjusting && virtual_offset)
/* 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. */
+ 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);
+ /* Likewise, we can only be adding thunks to a function declared in
+ the class currently being laid out. */
+ my_friendly_assert (TYPE_SIZE (DECL_CONTEXT (function))
+ && TYPE_BEING_DEFINED (DECL_CONTEXT (function)),
+ 20031211);
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_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;
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)
+ {
+ my_friendly_assert (!DECL_THUNKS (thunk), 20031023);
+ THUNK_ALIAS (thunk) = (THUNK_ALIAS (cov_probe)
+ ? THUNK_ALIAS (cov_probe) : cov_probe);
+ break;
+ }
+ }
+
DECL_NAME (thunk) = name;
SET_DECL_ASSEMBLER_NAME (thunk, name);
}
/* Adjust PTR by the constant FIXED_OFFSET, and by the vtable
offset indicated by VIRTUAL_OFFSET, if that is
- non-null. THIS_ADJUSTING is non-zero for a this adjusting thunk and
- zero for a result adjusting thunk. */
+ non-null. THIS_ADJUSTING is nonzero for a this adjusting thunk and
+ zero for a result adjusting thunk. */
static tree
thunk_adjust (tree ptr, bool this_adjusting,
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 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));
+ DECL_LANG_SPECIFIC (alias) = DECL_LANG_SPECIFIC (function);
+ cxx_dup_lang_specific_decl (alias);
+ DECL_CONTEXT (alias) = NULL;
+ TREE_READONLY (alias) = TREE_READONLY (function);
+ TREE_THIS_VOLATILE (alias) = TREE_THIS_VOLATILE (function);
+ TREE_PUBLIC (alias) = 0;
+ DECL_INTERFACE_KNOWN (alias) = 1;
+ DECL_NOT_REALLY_EXTERN (alias) = 1;
+ DECL_THIS_STATIC (alias) = 1;
+ DECL_SAVED_FUNCTION_DATA (alias) = NULL;
+ DECL_DESTRUCTOR_P (alias) = 0;
+ DECL_CONSTRUCTOR_P (alias) = 0;
+ DECL_CLONED_FUNCTION (alias) = NULL_TREE;
+ DECL_EXTERNAL (alias) = 0;
+ DECL_ARTIFICIAL (alias) = 1;
+ DECL_NO_STATIC_CHAIN (alias) = 1;
+ DECL_PENDING_INLINE_P (alias) = 0;
+ DECL_INLINE (alias) = 0;
+ DECL_DECLARED_INLINE_P (alias) = 0;
+ DECL_DEFERRED_FN (alias) = 0;
+ DECL_USE_TEMPLATE (alias) = 0;
+ DECL_TEMPLATE_INSTANTIATED (alias) = 0;
+ DECL_TEMPLATE_INFO (alias) = NULL;
+ DECL_INITIAL (alias) = error_mark_node;
+ TREE_ADDRESSABLE (alias) = 1;
+ TREE_USED (alias) = 1;
+ SET_DECL_ASSEMBLER_NAME (alias, DECL_NAME (alias));
+ TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (alias)) = 1;
+ 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
immediately. */
void
use_thunk (tree thunk_fndecl, bool emit_p)
{
- tree function;
+ 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. */
+ /* We should have called finish_thunk to give it a name. */
my_friendly_assert (DECL_NAME (thunk_fndecl), 20021127);
+ /* We should never be using an alias, always refer to the
+ aliased thunk. */
+ my_friendly_assert (!THUNK_ALIAS (thunk_fndecl), 20031023);
+
if (TREE_ASM_WRITTEN (thunk_fndecl))
return;
this translation unit. */
TREE_ADDRESSABLE (function) = 1;
mark_used (function);
- TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (function)) = 1;
+ mark_referenced (DECL_ASSEMBLER_NAME (function));
if (!emit_p)
return;
+#ifdef ASM_OUTPUT_DEF
+ alias = make_alias_for_thunk (function);
+#else
+ alias = function;
+#endif
+
fixed_offset = THUNK_FIXED_OFFSET (thunk_fndecl);
virtual_offset = THUNK_VIRTUAL_OFFSET (thunk_fndecl);
/* 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);
+ 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)
+ {
+ resolve_unique_section (function, 0, flag_function_sections);
+
+ if (DECL_SECTION_NAME (function) != NULL && DECL_ONE_ONLY (function))
+ {
+ resolve_unique_section (thunk_fndecl, 0, flag_function_sections);
+
+ /* Output the thunk into the same section as function. */
+ 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, function))
+ virtual_value, alias))
{
const char *fnname;
current_function_decl = thunk_fndecl;
DECL_RESULT (thunk_fndecl)
= build_decl (RESULT_DECL, 0, integer_type_node);
fnname = XSTR (XEXP (DECL_RTL (thunk_fndecl), 0), 0);
- init_function_start (thunk_fndecl, input_filename, input_line);
+ init_function_start (thunk_fndecl);
current_function_is_thunk = 1;
assemble_start_function (thunk_fndecl, fnname);
targetm.asm_out.output_mi_thunk (asm_out_file, thunk_fndecl,
- fixed_offset, virtual_value, function);
+ fixed_offset, virtual_value, alias);
assemble_end_function (thunk_fndecl, fnname);
current_function_decl = 0;
just makes a call to the real function. Unfortunately, this
doesn't work for varargs. */
- tree a, t;
- int saved_check_access;
-
if (varargs_function_p (function))
error ("generic thunk code fails for method `%#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);
/* We don't bother with a body block for thunks. */
/* There's no need to check accessibility inside the thunk body. */
- saved_check_access = scope_chain->check_access;
- scope_chain->check_access = 0;
+ push_deferring_access_checks (dk_no_check);
t = a;
if (this_adjusting)
for (a = TREE_CHAIN (a); a; a = TREE_CHAIN (a))
t = tree_cons (NULL_TREE, a, t);
t = nreverse (t);
- t = build_call (function, t);
- if (!this_adjusting)
- t = thunk_adjust (t, /*this_adjusting=*/0,
- fixed_offset, virtual_offset);
+ t = build_call (alias, t);
+ CALL_FROM_THUNK_P (t) = 1;
if (VOID_TYPE_P (TREE_TYPE (t)))
finish_expr_stmt (t);
else
- finish_return_stmt (t);
+ {
+ t = force_target_expr (TREE_TYPE (t), t);
+ if (!this_adjusting)
+ t = thunk_adjust (t, /*this_adjusting=*/0,
+ fixed_offset, virtual_offset);
+ 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_referenced (DECL_ASSEMBLER_NAME (thunk_fndecl));
/* But we don't want debugging information about it. */
DECL_IGNORED_P (thunk_fndecl) = 1;
/* Re-enable access control. */
- scope_chain->check_access = saved_check_access;
+ pop_deferring_access_checks ();
expand_body (finish_function (0));
}
tree parm = TREE_CHAIN (DECL_ARGUMENTS (fndecl));
tree compound_stmt;
- compound_stmt = begin_compound_stmt (/*has_no_scope=*/0);
+ compound_stmt = begin_compound_stmt (/*has_no_scope=*/false);
parm = convert_from_reference (parm);
if (TYPE_HAS_TRIVIAL_ASSIGN_REF (current_class_type)
int cvquals = cp_type_quals (TREE_TYPE (parm));
int i;
- /* Assign to each of thedirect base classes. */
+ /* Assign to each of the direct base classes. */
for (i = 0; i < CLASSTYPE_N_BASECLASSES (current_class_type); ++i)
{
tree binfo;
}
}
finish_return_stmt (current_class_ref);
- finish_compound_stmt (/*has_no_scope=*/0, compound_stmt);
+ finish_compound_stmt (compound_stmt);
}
void
return;
}
+ /* We may be in the middle of deferred access check. Disable
+ it now. */
+ push_deferring_access_checks (dk_no_deferred);
+
if (! context)
push_to_top_level ();
else if (nested)
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_LINE (fndecl) = input_line;
- DECL_SOURCE_FILE (fndecl) = input_filename;
+ DECL_SOURCE_LOCATION (fndecl) = input_location;
interface_unknown = 1;
start_function (NULL_TREE, fndecl, NULL_TREE, SF_DEFAULT | SF_PRE_PARSED);
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 (/*has_no_scope=*/false);
+ finish_compound_stmt (compound_stmt);
}
finish_function_body (stmt);
- expand_body (finish_function (0));
+ expand_or_defer_fn (finish_function (0));
extract_interface_info ();
if (! context)
pop_from_top_level ();
else if (nested)
pop_function_context_from (context);
+
+ pop_deferring_access_checks ();
}
/* Use EXTRACTOR to locate the relevant function called for each base &
parms = TREE_CHAIN (parms);
if (!parms)
continue;
- src_type = TREE_VALUE (parms);
- if (TREE_CODE (src_type) == REFERENCE_TYPE)
- src_type = TREE_TYPE (src_type);
+ src_type = non_reference (TREE_VALUE (parms));
if (!same_type_ignoring_top_level_qualifiers_p (src_type, type))
continue;
if (!sufficient_parms_p (TREE_CHAIN (parms)))
list = TREE_CHAIN (list);
return list;
}
+
+#include "gt-cp-method.h"