#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "tree.h"
#include "cp-tree.h"
#include "rtl.h"
#include "expr.h"
#include "output.h"
#include "flags.h"
#include "toplev.h"
#include "tm_p.h"
#include "target.h"
enum mangling_flags
{
mf_none = 0,
mf_maybe_uninstantiated = 1,
mf_use_underscores_around_value = 2
};
typedef enum mangling_flags mangling_flags;
static tree thunk_adjust (tree, bool, HOST_WIDE_INT, tree);
static void do_build_assign_ref (tree);
static void do_build_copy_constructor (tree);
static tree synthesize_exception_spec (tree, tree (*) (tree, void *), void *);
static tree locate_dtor (tree, void *);
static tree locate_ctor (tree, void *);
static tree locate_copy (tree, void *);
static tree make_alias_for_thunk (tree);
void
init_method (void)
{
init_mangle ();
}
tree
make_thunk (tree function, bool this_adjusting,
tree fixed_offset, tree virtual_offset)
{
HOST_WIDE_INT d;
tree thunk;
gcc_assert (TREE_CODE (function) == FUNCTION_DECL);
gcc_assert (!DECL_THIS_THUNK_P (function));
gcc_assert (!DECL_RESULT_THUNK_P (function) || this_adjusting);
if (this_adjusting && virtual_offset)
virtual_offset
= size_binop (MULT_EXPR,
virtual_offset,
convert (ssizetype,
TYPE_SIZE_UNIT (vtable_entry_type)));
d = tree_low_cst (fixed_offset, 0);
for (thunk = DECL_THUNKS (function); thunk; thunk = TREE_CHAIN (thunk))
if (DECL_THIS_THUNK_P (thunk) == this_adjusting
&& 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;
gcc_assert (!TREE_ASM_WRITTEN (function));
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);
TREE_PUBLIC (thunk) = TREE_PUBLIC (function);
if (flag_weak)
comdat_linkage (thunk);
SET_DECL_THUNK_P (thunk, this_adjusting);
THUNK_TARGET (thunk) = function;
THUNK_FIXED_OFFSET (thunk) = d;
THUNK_VIRTUAL_OFFSET (thunk) = virtual_offset;
THUNK_ALIAS (thunk) = NULL_TREE;
DECL_INTERFACE_KNOWN (thunk) = 1;
DECL_NOT_REALLY_EXTERN (thunk) = 1;
DECL_SAVED_FUNCTION_DATA (thunk) = NULL;
DECL_DESTRUCTOR_P (thunk) = 0;
DECL_CONSTRUCTOR_P (thunk) = 0;
DECL_EXTERNAL (thunk) = 1;
DECL_ARTIFICIAL (thunk) = 1;
DECL_NO_STATIC_CHAIN (thunk) = 1;
DECL_PENDING_INLINE_P (thunk) = 0;
DECL_INLINE (thunk) = 0;
DECL_DECLARED_INLINE_P (thunk) = 0;
DECL_DEFERRED_FN (thunk) = 0;
TREE_CHAIN (thunk) = DECL_THUNKS (function);
DECL_THUNKS (function) = thunk;
return thunk;
}
void
finish_thunk (tree thunk)
{
tree function, name;
tree fixed_offset = ssize_int (THUNK_FIXED_OFFSET (thunk));
tree virtual_offset = THUNK_VIRTUAL_OFFSET (thunk);
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);
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);
}
static tree
thunk_adjust (tree ptr, bool this_adjusting,
HOST_WIDE_INT fixed_offset, tree virtual_offset)
{
if (this_adjusting)
ptr = fold (build2 (PLUS_EXPR, TREE_TYPE (ptr), ptr,
ssize_int (fixed_offset)));
if (virtual_offset)
{
tree vtable;
ptr = save_expr (ptr);
vtable = build1 (NOP_EXPR,
build_pointer_type (build_pointer_type
(vtable_entry_type)),
ptr);
vtable = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (vtable)), vtable);
vtable = build2 (PLUS_EXPR, TREE_TYPE (vtable), vtable, virtual_offset);
vtable = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (vtable)), vtable);
ptr = fold (build2 (PLUS_EXPR, TREE_TYPE (ptr), ptr, vtable));
}
if (!this_adjusting)
ptr = fold (build2 (PLUS_EXPR, TREE_TYPE (ptr), ptr,
ssize_int (fixed_offset)));
return ptr;
}
static GTY (()) int thunk_labelno;
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;
}
void
use_thunk (tree thunk_fndecl, bool emit_p)
{
tree a, t, function, alias;
tree virtual_offset;
HOST_WIDE_INT fixed_offset, virtual_value;
bool this_adjusting = DECL_THIS_THUNK_P (thunk_fndecl);
gcc_assert (DECL_NAME (thunk_fndecl));
gcc_assert (!THUNK_ALIAS (thunk_fndecl));
if (TREE_ASM_WRITTEN (thunk_fndecl))
return;
function = THUNK_TARGET (thunk_fndecl);
if (DECL_RESULT (thunk_fndecl))
return;
if (DECL_THUNK_P (function))
use_thunk (function, emit_p);
TREE_ADDRESSABLE (thunk_fndecl) = 1;
TREE_ADDRESSABLE (function) = 1;
mark_used (function);
if (!emit_p)
return;
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 (virtual_offset)
{
if (!this_adjusting)
virtual_offset = BINFO_VPTR_FIELD (virtual_offset);
virtual_value = tree_low_cst (virtual_offset, 0);
gcc_assert (virtual_value);
}
else
virtual_value = 0;
mark_used (thunk_fndecl);
DECL_EXTERNAL (thunk_fndecl) = 0;
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)
{
TREE_ASM_WRITTEN (thunk_fndecl) = 1;
return;
}
push_to_top_level ();
if (TARGET_USE_LOCAL_THUNK_ALIAS_P (function)
&& 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);
DECL_SECTION_NAME (thunk_fndecl) = DECL_SECTION_NAME (function);
}
}
DECL_INITIAL (thunk_fndecl) = make_node (BLOCK);
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))
{
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);
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, alias);
assemble_end_function (thunk_fndecl, fnname);
current_function_decl = 0;
cfun = 0;
TREE_ASM_WRITTEN (thunk_fndecl) = 1;
}
else
{
if (varargs_function_p (function))
error ("generic thunk code fails for method %q#D which uses %<...%>",
function);
DECL_RESULT (thunk_fndecl) = NULL_TREE;
start_preparsed_function (thunk_fndecl, NULL_TREE, SF_PRE_PARSED);
push_deferring_access_checks (dk_no_check);
t = a;
if (this_adjusting)
t = thunk_adjust (t, 1,
fixed_offset, virtual_offset);
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);
CALL_FROM_THUNK_P (t) = 1;
if (VOID_TYPE_P (TREE_TYPE (t)))
finish_expr_stmt (t);
else
{
t = force_target_expr (TREE_TYPE (t), t);
if (!this_adjusting)
t = thunk_adjust (t, 0,
fixed_offset, virtual_offset);
finish_return_stmt (t);
}
mark_decl_referenced (thunk_fndecl);
DECL_IGNORED_P (thunk_fndecl) = 1;
pop_deferring_access_checks ();
expand_body (finish_function (0));
}
pop_from_top_level ();
}
static void
do_build_copy_constructor (tree fndecl)
{
tree parm = FUNCTION_FIRST_USER_PARM (fndecl);
parm = convert_from_reference (parm);
if (TYPE_HAS_TRIVIAL_INIT_REF (current_class_type)
&& is_empty_class (current_class_type))
;
else if (TYPE_HAS_TRIVIAL_INIT_REF (current_class_type))
{
tree t = build2 (INIT_EXPR, void_type_node, current_class_ref, parm);
finish_expr_stmt (t);
}
else
{
tree fields = TYPE_FIELDS (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) *vbases;
for (vbases = CLASSTYPE_VBASECLASSES (current_class_type), i = 0;
VEC_iterate (tree, vbases, i, binfo); i++)
{
member_init_list
= tree_cons (binfo,
build_tree_list (NULL_TREE,
build_base_path (PLUS_EXPR, parm,
binfo, 1)),
member_init_list);
}
for (binfo = TYPE_BINFO (current_class_type), i = 0;
BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
{
if (BINFO_VIRTUAL_P (base_binfo))
continue;
member_init_list
= tree_cons (base_binfo,
build_tree_list (NULL_TREE,
build_base_path (PLUS_EXPR, parm,
base_binfo, 1)),
member_init_list);
}
for (; fields; fields = TREE_CHAIN (fields))
{
tree init = parm;
tree field = fields;
tree expr_type;
if (TREE_CODE (field) != FIELD_DECL)
continue;
expr_type = TREE_TYPE (field);
if (DECL_NAME (field))
{
if (VFIELD_NAME_P (DECL_NAME (field)))
continue;
}
else if (ANON_AGGR_TYPE_P (expr_type) && TYPE_FIELDS (expr_type))
;
else
continue;
if (TREE_CODE (expr_type) != REFERENCE_TYPE)
{
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);
}
finish_mem_initializers (member_init_list);
}
}
static void
do_build_assign_ref (tree fndecl)
{
tree parm = TREE_CHAIN (DECL_ARGUMENTS (fndecl));
tree compound_stmt;
compound_stmt = begin_compound_stmt (0);
parm = convert_from_reference (parm);
if (TYPE_HAS_TRIVIAL_ASSIGN_REF (current_class_type)
&& is_empty_class (current_class_type))
;
else if (TYPE_HAS_TRIVIAL_ASSIGN_REF (current_class_type))
{
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;
for (binfo = TYPE_BINFO (current_class_type), i = 0;
BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
{
tree converted_parm;
converted_parm = build_base_path (PLUS_EXPR, parm, base_binfo, 1);
finish_expr_stmt
(build_special_member_call (current_class_ref,
ansi_assopname (NOP_EXPR),
build_tree_list (NULL_TREE,
converted_parm),
base_binfo,
LOOKUP_NORMAL | LOOKUP_NONVIRTUAL));
}
for (fields = TYPE_FIELDS (current_class_type);
fields;
fields = TREE_CHAIN (fields))
{
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;
expr_type = TREE_TYPE (field);
if (CP_TYPE_CONST_P (expr_type))
{
error ("non-static const member %q#D, can't use default "
"assignment operator", field);
continue;
}
else if (TREE_CODE (expr_type) == REFERENCE_TYPE)
{
error ("non-static reference member %q#D, can't use "
"default assignment operator", field);
continue;
}
if (DECL_NAME (field))
{
if (VFIELD_NAME_P (DECL_NAME (field)))
continue;
}
else if (ANON_AGGR_TYPE_P (expr_type)
&& TYPE_FIELDS (expr_type) != NULL_TREE)
;
else
continue;
comp = build3 (COMPONENT_REF, expr_type, comp, field, NULL_TREE);
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))
init = build_modify_expr (comp, NOP_EXPR, init);
else
init = build2 (MODIFY_EXPR, TREE_TYPE (comp), comp, init);
finish_expr_stmt (init);
}
}
finish_return_stmt (current_class_ref);
finish_compound_stmt (compound_stmt);
}
void
synthesize_method (tree fndecl)
{
bool nested = (current_function_decl != NULL_TREE);
tree context = decl_function_context (fndecl);
bool need_body = true;
tree stmt;
location_t save_input_location = input_location;
if (DECL_CLONED_FUNCTION_P (fndecl))
{
DECL_SOURCE_LOCATION (DECL_CLONED_FUNCTION (fndecl)) =
DECL_SOURCE_LOCATION (fndecl);
synthesize_method (DECL_CLONED_FUNCTION (fndecl));
return;
}
push_deferring_access_checks (dk_no_deferred);
if (! context)
push_to_top_level ();
else if (nested)
push_function_context_to (context);
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)
{
do_build_assign_ref (fndecl);
need_body = false;
}
else if (DECL_CONSTRUCTOR_P (fndecl))
{
tree arg_chain = FUNCTION_FIRST_USER_PARMTYPE (fndecl);
if (arg_chain != void_list_node)
do_build_copy_constructor (fndecl);
else if (TYPE_NEEDS_CONSTRUCTING (current_class_type))
finish_mem_initializers (NULL_TREE);
}
if (need_body)
{
tree 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));
input_location = save_input_location;
if (! context)
pop_from_top_level ();
else if (nested)
pop_function_context_from (context);
pop_deferring_access_checks ();
}
static tree
synthesize_exception_spec (tree type, tree (*extractor) (tree, void*),
void *client)
{
tree raises = empty_except_spec;
tree fields = TYPE_FIELDS (type);
tree binfo, base_binfo;
int i;
for (binfo = TYPE_BINFO (type), i = 0;
BINFO_BASE_ITERATE (binfo, i, base_binfo); i++)
{
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);
}
}
for (; fields; fields = TREE_CHAIN (fields))
{
tree type = TREE_TYPE (fields);
tree fn;
if (TREE_CODE (fields) != FIELD_DECL || DECL_ARTIFICIAL (fields))
continue;
while (TREE_CODE (type) == ARRAY_TYPE)
type = TREE_TYPE (type);
if (TREE_CODE (type) != RECORD_TYPE)
continue;
fn = (*extractor) (type, client);
if (fn)
{
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)
{
return CLASSTYPE_DESTRUCTORS (type);
}
static tree
locate_ctor (tree type, void *client ATTRIBUTE_UNUSED)
{
tree fns;
if (!TYPE_HAS_DEFAULT_CONSTRUCTOR (type))
return NULL_TREE;
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 NULL_TREE;
}
struct copy_data
{
tree name;
int quals;
};
static tree
locate_copy (tree type, void *client_)
{
struct copy_data *client = (struct copy_data *)client_;
tree fns;
tree best = NULL_TREE;
bool excess_p = false;
if (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))
{
if (CLASSTYPE_LAZY_COPY_CTOR (type))
lazily_declare_fn (sfk_copy_constructor, type);
fns = CLASSTYPE_CONSTRUCTORS (type);
}
else
return NULL_TREE;
for (; fns; fns = OVL_NEXT (fns))
{
tree fn = OVL_CURRENT (fns);
tree parms = TYPE_ARG_TYPES (TREE_TYPE (fn));
tree src_type;
int excess;
int quals;
parms = TREE_CHAIN (parms);
if (!parms)
continue;
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)))
continue;
quals = cp_type_quals (src_type);
if (client->quals & ~quals)
continue;
excess = quals & ~client->quals;
if (!best || (excess_p && !excess))
{
best = fn;
excess_p = excess;
}
else
return NULL_TREE;
}
return best;
}
tree
implicitly_declare_fn (special_function_kind kind, tree type, bool const_p)
{
tree fn;
tree parameter_types = void_list_node;
tree return_type;
tree fn_type;
tree raises = empty_except_spec;
tree rhs_parm_type = NULL_TREE;
tree name;
type = TYPE_MAIN_VARIANT (type);
if (targetm.cxx.cdtor_returns_this () && !TYPE_FOR_JAVA (type))
{
if (kind == sfk_destructor)
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:
name = constructor_name (type);
raises = synthesize_exception_spec (type, &locate_dtor, 0);
break;
case sfk_constructor:
name = constructor_name (type);
raises = synthesize_exception_spec (type, &locate_ctor, 0);
break;
case sfk_copy_constructor:
case sfk_assignment_operator:
{
struct copy_data data;
data.name = NULL;
data.quals = 0;
if (kind == sfk_assignment_operator)
{
return_type = build_reference_type (type);
name = ansi_assopname (NOP_EXPR);
data.name = name;
}
else
name = constructor_name (type);
if (const_p)
{
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:
gcc_unreachable ();
}
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);
}
if (rhs_parm_type)
{
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;
gcc_assert (!TREE_USED (fn));
return fn;
}
tree
lazily_declare_fn (special_function_kind sfk, tree type)
{
tree fn;
bool const_p;
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
const_p = false;
fn = implicitly_declare_fn (sfk, type, const_p);
if (sfk == sfk_destructor)
check_for_override (fn, type);
add_method (type, fn);
if (sfk == sfk_destructor
&& DECL_VIRTUAL_P (fn)
&& abi_version_at_least (2))
TYPE_METHODS (type) = chainon (TYPE_METHODS (type), fn);
else
{
if (warn_abi && DECL_VIRTUAL_P (fn))
warning ("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, 0);
if (sfk == sfk_assignment_operator)
CLASSTYPE_LAZY_ASSIGNMENT_OP (type) = 0;
else
{
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;
clone_function_decl (fn, true);
}
return fn;
}
tree
skip_artificial_parms_for (tree fn, tree list)
{
if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fn))
list = TREE_CHAIN (list);
else
return list;
if (DECL_HAS_IN_CHARGE_PARM_P (fn))
list = TREE_CHAIN (list);
if (DECL_HAS_VTT_PARM_P (fn))
list = TREE_CHAIN (list);
return list;
}
#include "gt-cp-method.h"