#include "config.h"
#include "system.h"
#include "obstack.h"
#include "tree.h"
#include "cp-tree.h"
#include "flags.h"
#include "assert.h"
#include "toplev.h"
extern struct obstack *current_obstack;
extern struct obstack permanent_obstack;
extern struct obstack *saveable_obstack;
static tree save_this PROTO((tree));
static tree build_sptr_ref PROTO((tree));
static tree build_member_function_pointer PROTO((tree));
static void undo_casts PROTO((tree));
static tree build_signature_pointer_or_reference_name
PROTO((tree, int, int));
static void build_signature_pointer_or_reference_decl
PROTO((tree, tree));
static tree build_signature_pointer_or_reference_type
PROTO((tree, int, int));
static tree get_sigtable_name PROTO((tree, tree));
static tree build_signature_table_constructor PROTO((tree, tree));
static int match_method_types PROTO((tree, tree));
static tree build_sigtable PROTO((tree, tree, tree));
static int global_sigtable_name_counter;
static tree
build_signature_pointer_or_reference_name (to_type, type_quals, refp)
tree to_type;
int type_quals;
int refp;
{
const char * sig_name = TYPE_NAME_STRING (to_type);
int name_len = TYPE_NAME_LENGTH (to_type) + 3 ;
char * name;
const char *const_rep = (type_quals & TYPE_QUAL_CONST) ? "C" : "";
const char *restrict_rep = (type_quals & TYPE_QUAL_RESTRICT) ? "R" : "";
const char *volatile_rep = (type_quals & TYPE_QUAL_VOLATILE) ? "C" : "";
if (refp)
{
name = (char *) alloca (name_len + sizeof (SIGNATURE_REFERENCE_NAME) +2);
sprintf (name, SIGNATURE_REFERENCE_NAME_FORMAT,
const_rep, volatile_rep, restrict_rep, sig_name);
}
else
{
name = (char *) alloca (name_len + sizeof (SIGNATURE_POINTER_NAME) + 2);
sprintf (name, SIGNATURE_POINTER_NAME_FORMAT,
const_rep, volatile_rep, restrict_rep, sig_name);
}
return get_identifier (name);
}
static void
build_signature_pointer_or_reference_decl (type, name)
tree type, name;
{
tree decl;
decl = build_decl (TYPE_DECL, name, type);
TYPE_NAME (type) = decl;
TREE_CHAIN (type) = decl;
}
static tree
build_signature_pointer_or_reference_type (to_type, type_quals, refp)
tree to_type;
int type_quals;
int refp;
{
register tree t, m;
register struct obstack *ambient_obstack = current_obstack;
register struct obstack *ambient_saveable_obstack = saveable_obstack;
m = refp ? SIGNATURE_REFERENCE_TO (to_type) : SIGNATURE_POINTER_TO (to_type);
if (m == NULL_TREE && type_quals != TYPE_UNQUALIFIED)
m = build_signature_pointer_or_reference_type (to_type,
TYPE_UNQUALIFIED, refp);
refp = !!refp;
if (m && !flag_gen_aux_info)
for (t = m; t; t = TYPE_NEXT_VARIANT (t))
if (type_quals == CP_TYPE_QUALS (TREE_TYPE (TREE_TYPE
(TYPE_FIELDS (t)))))
return t;
if (TREE_PERMANENT (to_type))
{
current_obstack = &permanent_obstack;
saveable_obstack = &permanent_obstack;
}
t = make_lang_type (RECORD_TYPE);
{
tree obj_type = build_qualified_type (void_type_node, type_quals);
tree optr_type = build_pointer_type (obj_type);
tree optr, sptr;
optr = build_lang_field_decl (FIELD_DECL,
get_identifier (SIGNATURE_OPTR_NAME),
optr_type);
DECL_FIELD_CONTEXT (optr) = t;
DECL_CLASS_CONTEXT (optr) = t;
if (m)
sptr = TREE_CHAIN (TYPE_FIELDS (m));
else
{
tree sig_tbl_type =
cp_build_qualified_type (to_type, TYPE_QUAL_CONST);
sptr = build_lang_field_decl (FIELD_DECL,
get_identifier (SIGNATURE_SPTR_NAME),
build_pointer_type (sig_tbl_type));
DECL_FIELD_CONTEXT (sptr) = t;
DECL_CLASS_CONTEXT (sptr) = t;
TREE_CHAIN (sptr) = NULL_TREE;
}
TREE_CHAIN (optr) = sptr;
TYPE_FIELDS (t) = optr;
TYPE_ALIGN (t) = MAX (TYPE_ALIGN (double_type_node),
TYPE_ALIGN (optr_type));
SET_IS_AGGR_TYPE (t, 0);
}
{
tree name = build_signature_pointer_or_reference_name (to_type,
type_quals,
refp);
build_signature_pointer_or_reference_decl (t, name);
}
CLASSTYPE_GOT_SEMICOLON (t) = 1;
IS_SIGNATURE_POINTER (t) = ! refp;
IS_SIGNATURE_REFERENCE (t) = refp;
SIGNATURE_TYPE (t) = to_type;
if (m)
{
TYPE_NEXT_VARIANT (t) = TYPE_NEXT_VARIANT (m);
TYPE_NEXT_VARIANT (m) = t;
}
else if (refp)
SIGNATURE_REFERENCE_TO (to_type) = t;
else
SIGNATURE_POINTER_TO (to_type) = t;
layout_type (t);
current_obstack = ambient_obstack;
saveable_obstack = ambient_saveable_obstack;
rest_of_type_compilation (t, 1);
return t;
}
tree
build_signature_pointer_type (to_type)
tree to_type;
{
return
build_signature_pointer_or_reference_type (TYPE_MAIN_VARIANT (to_type),
CP_TYPE_QUALS (to_type), 0);
}
tree
build_signature_reference_type (to_type)
tree to_type;
{
return
build_signature_pointer_or_reference_type (TYPE_MAIN_VARIANT (to_type),
CP_TYPE_QUALS (to_type), 1);
}
static tree
get_sigtable_name (sig_type, rhs_type)
tree sig_type, rhs_type;
{
tree sig_type_id = build_typename_overload (sig_type);
tree rhs_type_id = build_typename_overload (rhs_type);
char *buf = (char *) alloca (sizeof (SIGTABLE_NAME_FORMAT_LONG)
+ IDENTIFIER_LENGTH (sig_type_id)
+ IDENTIFIER_LENGTH (rhs_type_id) + 20);
const char *sig_ptr = IDENTIFIER_POINTER (sig_type_id);
const char *rhs_ptr = IDENTIFIER_POINTER (rhs_type_id);
int i, j;
for (i = 0; sig_ptr[i] == OPERATOR_TYPENAME_FORMAT[i]; i++)
;
while (sig_ptr[i] >= '0' && sig_ptr[i] <= '9')
i += 1;
for (j = 0; rhs_ptr[j] == OPERATOR_TYPENAME_FORMAT[j]; j++)
;
while (rhs_ptr[j] >= '0' && rhs_ptr[j] <= '9')
j += 1;
if (IS_SIGNATURE (rhs_type))
sprintf (buf, SIGTABLE_NAME_FORMAT_LONG, sig_ptr+i, rhs_ptr+j,
global_sigtable_name_counter++);
else
sprintf (buf, SIGTABLE_NAME_FORMAT, sig_ptr+i, rhs_ptr+j);
return get_identifier (buf);
}
static tree
build_member_function_pointer (member)
tree member;
{
const char *namstr = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (member));
int namlen = IDENTIFIER_LENGTH (DECL_ASSEMBLER_NAME (member));
char *name;
tree entry;
name = (char *) alloca (namlen + sizeof (SIGNATURE_FIELD_NAME) + 2);
sprintf (name, SIGNATURE_FIELD_NAME_FORMAT, namstr);
GNU_xref_ref (current_function_decl, name);
entry = build_lang_field_decl (FIELD_DECL, get_identifier (name),
sigtable_entry_type);
TREE_CONSTANT (entry) = 1;
TREE_READONLY (entry) = 1;
GNU_xref_decl (current_function_decl, entry);
return entry;
}
void
append_signature_fields (t)
tree t;
{
tree x;
tree mfptr;
tree last_mfptr = NULL_TREE;
tree mfptr_list = NULL_TREE;
for (x = TYPE_METHODS (t); x; x = TREE_CHAIN (x))
{
if (TREE_CODE (x) == FUNCTION_DECL)
{
mfptr = build_member_function_pointer (x);
DECL_MEMFUNC_POINTER_TO (x) = mfptr;
DECL_MEMFUNC_POINTING_TO (mfptr) = x;
DECL_IGNORED_P (x) = 1;
DECL_IN_AGGR_P (mfptr) = 1;
if (! mfptr_list)
mfptr_list = last_mfptr = mfptr;
else
{
TREE_CHAIN (last_mfptr) = mfptr;
last_mfptr = mfptr;
}
}
}
if (last_mfptr)
TYPE_FIELDS (t) = chainon (TYPE_FIELDS (t), mfptr_list);
}
static int
match_method_types (sig_mtype, class_mtype)
tree sig_mtype, class_mtype;
{
tree sig_return_type = TREE_TYPE (sig_mtype);
tree sig_arg_types = TYPE_ARG_TYPES (sig_mtype);
tree class_return_type = TREE_TYPE (class_mtype);
tree class_arg_types = TYPE_ARG_TYPES (class_mtype);
if (!same_type_p (sig_return_type, class_return_type))
return 0;
{
tree sig_this
= TREE_TYPE (TREE_TYPE (TYPE_FIELDS (TREE_VALUE (sig_arg_types))));
tree class_this = TREE_VALUE (class_arg_types);
if (TREE_CODE (class_this) == RECORD_TYPE)
class_this = TREE_TYPE (TREE_TYPE (TYPE_FIELDS (class_this)));
else
class_this = TREE_TYPE (class_this);
if (!at_least_as_qualified_p (class_this, sig_this))
return 0;
}
sig_arg_types = TREE_CHAIN (sig_arg_types);
class_arg_types = TREE_CHAIN (class_arg_types);
return compparms (sig_arg_types, class_arg_types);
}
static void
undo_casts (sig_ty)
tree sig_ty;
{
tree field = TYPE_FIELDS (sig_ty);
for (; field && TREE_CODE (field) != FIELD_DECL; field = TREE_CHAIN (field))
if (TYPE_MAIN_VARIANT (TREE_TYPE (field)) == opaque_type_node)
TREE_TYPE (TREE_TYPE (field)) = TREE_TYPE (ptr_type_node);
}
static tree
build_signature_table_constructor (sig_ty, rhs)
tree sig_ty, rhs;
{
tree rhstype = TREE_TYPE (rhs);
tree sig_field = TYPE_FIELDS (sig_ty);
tree result = NULL_TREE;
tree first_rhs_field = NULL_TREE;
tree last_rhs_field = NULL_TREE;
int sig_ptr_p = IS_SIGNATURE (rhstype);
int offset_p = sig_ptr_p;
rhstype = sig_ptr_p ? rhstype : TREE_TYPE (rhstype);
if (CLASSTYPE_TAGS (sig_ty))
{
sorry ("conformance check with signature containing class declarations");
return error_mark_node;
}
for (; sig_field; sig_field = TREE_CHAIN (sig_field))
{
tree basetype_path, baselink, basetypes;
tree sig_method, sig_mname, sig_mtype;
tree rhs_method, tbl_entry;
if (TREE_CODE (sig_field) == TYPE_DECL)
{
tree sig_field_type = TREE_TYPE (sig_field);
if (TYPE_MAIN_VARIANT (sig_field_type) == opaque_type_node)
{
tree oty_name = DECL_NAME (sig_field);
tree oty_type = lookup_field (rhstype, oty_name, 1, 1);
if (oty_type == NULL_TREE || oty_type == error_mark_node)
{
cp_error ("class `%T' does not contain type `%T'",
rhstype, oty_type);
undo_casts (sig_ty);
return error_mark_node;
}
oty_type = TREE_TYPE (oty_type);
TREE_TYPE (sig_field_type) = TREE_TYPE (oty_type);
}
continue;
}
sig_method = DECL_MEMFUNC_POINTING_TO (sig_field);
sig_mname = DECL_NAME (sig_method);
sig_mtype = TREE_TYPE (sig_method);
basetype_path = TYPE_BINFO (rhstype);
baselink = lookup_fnfields (basetype_path, sig_mname, 0);
if (baselink == NULL_TREE || baselink == error_mark_node)
{
if (! IS_DEFAULT_IMPLEMENTATION (sig_method))
{
cp_error ("class `%T' does not contain method `%D'",
rhstype, sig_mname);
undo_casts (sig_ty);
return error_mark_node;
}
else
{
rhs_method = sig_method;
}
}
else
{
tree rhs_methods;
basetypes = TREE_PURPOSE (baselink);
if (TREE_CODE (basetypes) == TREE_LIST)
basetypes = TREE_VALUE (basetypes);
rhs_methods = TREE_VALUE (baselink);
for (; rhs_methods; rhs_methods = OVL_NEXT (rhs_methods))
if ((rhs_method = OVL_CURRENT (rhs_methods))
&& sig_mname == DECL_NAME (rhs_method)
&& ! DECL_STATIC_FUNCTION_P (rhs_method)
&& match_method_types (sig_mtype, TREE_TYPE (rhs_method)))
break;
if (rhs_methods == NULL_TREE
|| !accessible_p (basetypes, rhs_method))
{
cp_error ("`%T' does not contain a method conforming to `%#D'",
rhstype, sig_method);
undo_casts (sig_ty);
return error_mark_node;
}
}
if (sig_ptr_p && rhs_method != sig_method)
{
tree rhs_field = DECL_MEMFUNC_POINTER_TO (rhs_method);
if (first_rhs_field == NULL_TREE)
{
first_rhs_field = rhs_field;
last_rhs_field = rhs_field;
}
else if (TREE_CHAIN (last_rhs_field) == rhs_field)
last_rhs_field = rhs_field;
else
offset_p = 0;
tbl_entry = build_component_ref (rhs, DECL_NAME (rhs_field),
NULL_TREE, 1);
}
else
{
tree tag, vb_off, delta, idx, pfn = NULL_TREE, vt_off = NULL_TREE;
tree tag_decl, vb_off_decl, delta_decl, index_decl;
tree pfn_decl, vt_off_decl;
if (rhs_method == sig_method)
{
tag = build_unary_op (NEGATE_EXPR, integer_one_node, 0);
vb_off = build_unary_op (NEGATE_EXPR, integer_one_node, 0);
delta = integer_zero_node;
idx = integer_zero_node;
pfn = build_addr_func (rhs_method);
TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (rhs_method)) = 1;
TREE_TYPE (pfn) = ptr_type_node;
TREE_ADDRESSABLE (rhs_method) = 1;
offset_p = 0;
}
else if (DECL_VINDEX (rhs_method))
{
tag = integer_one_node;
vb_off = build_unary_op (NEGATE_EXPR, integer_one_node, 0);
if (flag_vtable_thunks)
delta = BINFO_OFFSET
(get_binfo (DECL_CONTEXT (rhs_method), rhstype, 1));
else
delta = BINFO_OFFSET
(get_binfo (DECL_CLASS_CONTEXT (rhs_method), rhstype, 1));
idx = DECL_VINDEX (rhs_method);
vt_off = get_vfield_offset (get_binfo (DECL_CONTEXT (rhs_method),
rhstype, 0));
}
else
{
tag = integer_zero_node;
vb_off = build_unary_op (NEGATE_EXPR, integer_one_node, 0);
delta = BINFO_OFFSET (get_binfo (DECL_CLASS_CONTEXT (rhs_method),
rhstype, 1));
idx = integer_zero_node;
pfn = build_addr_func (rhs_method);
TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (rhs_method)) = 1;
TREE_TYPE (pfn) = ptr_type_node;
TREE_ADDRESSABLE (rhs_method) = 1;
}
tag_decl = TYPE_FIELDS (sigtable_entry_type);
vb_off_decl = TREE_CHAIN (tag_decl);
delta_decl = TREE_CHAIN (vb_off_decl);
index_decl = TREE_CHAIN (delta_decl);
pfn_decl = TREE_CHAIN (index_decl);
vt_off_decl = TREE_CHAIN (pfn_decl);
tag = cp_convert (TREE_TYPE (tag_decl), tag);
vb_off = cp_convert (TREE_TYPE (vb_off_decl), vb_off);
delta = cp_convert (TREE_TYPE (delta_decl), delta);
idx = cp_convert (TREE_TYPE (index_decl), idx);
if (DECL_VINDEX (rhs_method))
{
vt_off = cp_convert (TREE_TYPE (vt_off_decl), vt_off);
tbl_entry = build_tree_list (vt_off_decl, vt_off);
}
else
{
pfn = cp_convert (TREE_TYPE (pfn_decl), pfn);
tbl_entry = build_tree_list (pfn_decl, pfn);
}
tbl_entry = tree_cons (delta_decl, delta,
tree_cons (index_decl, idx, tbl_entry));
tbl_entry = tree_cons (tag_decl, tag,
tree_cons (vb_off_decl, vb_off, tbl_entry));
tbl_entry = build (CONSTRUCTOR, sigtable_entry_type,
NULL_TREE, tbl_entry);
TREE_CONSTANT (tbl_entry) = 1;
}
if (result)
result = tree_cons (NULL_TREE, tbl_entry, result);
else
result = build_tree_list (NULL_TREE, tbl_entry);
}
if (result == NULL_TREE)
{
undo_casts (sig_ty);
return NULL_TREE;
}
if (offset_p)
{
if (first_rhs_field == TYPE_FIELDS (rhstype))
{
undo_casts (sig_ty);
return integer_zero_node;
}
else
{
undo_casts (sig_ty);
return build_component_ref (rhs, DECL_NAME (first_rhs_field),
NULL_TREE, 0);
}
}
result = build_nt (CONSTRUCTOR, NULL_TREE, nreverse (result));
TREE_HAS_CONSTRUCTOR (result) = 1;
TREE_CONSTANT (result) = !sig_ptr_p;
undo_casts (sig_ty);
return result;
}
static tree
build_sigtable (sig_type, rhs_type, init_from)
tree sig_type, rhs_type, init_from;
{
tree name = NULL_TREE;
tree decl = NULL_TREE;
tree init_expr;
push_obstacks_nochange ();
end_temporary_allocation ();
if (! IS_SIGNATURE (rhs_type))
{
name = get_sigtable_name (sig_type, rhs_type);
decl = IDENTIFIER_GLOBAL_VALUE (name);
}
if (decl == NULL_TREE)
{
tree init = NULL_TREE;
if (SIGNATURE_HAS_OPAQUE_TYPEDECLS (sig_type)
&& SIGTABLE_HAS_BEEN_GENERATED (sig_type))
{
error ("signature with opaque type implemented by multiple classes");
return error_mark_node;
}
SIGTABLE_HAS_BEEN_GENERATED (sig_type) = 1;
init_expr = build_signature_table_constructor (sig_type, init_from);
if (init_expr == NULL_TREE || TREE_CODE (init_expr) != CONSTRUCTOR)
return init_expr;
if (name == NULL_TREE)
name = get_sigtable_name (sig_type, rhs_type);
{
tree context = current_function_decl;
current_function_decl = NULL_TREE;
decl = pushdecl_top_level (build_decl (VAR_DECL, name, sig_type));
current_function_decl = context;
}
SET_IDENTIFIER_GLOBAL_VALUE (name, decl);
store_init_value (decl, init_expr);
if (IS_SIGNATURE (rhs_type))
{
init = DECL_INITIAL (decl);
DECL_INITIAL (decl) = error_mark_node;
}
DECL_ALIGN (decl) = MAX (TYPE_ALIGN (double_type_node),
DECL_ALIGN (decl));
#if 0
TREE_READONLY (decl) = 1;
#endif
TREE_STATIC (decl) = 1;
TREE_USED (decl) = 1;
make_decl_rtl (decl, NULL, 1);
if (IS_SIGNATURE (rhs_type))
expand_static_init (decl, init);
}
pop_obstacks ();
return decl;
}
tree
build_signature_pointer_constructor (lhs, rhs)
tree lhs, rhs;
{
register struct obstack *ambient_obstack = current_obstack;
register struct obstack *ambient_saveable_obstack = saveable_obstack;
int initp = (TREE_CODE (lhs) == RECORD_TYPE);
tree lhstype = initp ? lhs : TREE_TYPE (lhs);
tree rhstype = TREE_TYPE (rhs);
tree sig_ty = SIGNATURE_TYPE (lhstype);
tree sig_tbl, sptr_expr, optr_expr;
tree result;
if (! ((TREE_CODE (rhstype) == POINTER_TYPE
&& TREE_CODE (TREE_TYPE (rhstype)) == RECORD_TYPE)
|| (TYPE_LANG_SPECIFIC (rhstype)
&& (IS_SIGNATURE_POINTER (rhstype)
|| IS_SIGNATURE_REFERENCE (rhstype)))))
{
error ("invalid assignment to signature pointer or reference");
return error_mark_node;
}
if (TYPE_SIZE (sig_ty) == NULL_TREE)
{
cp_error ("undefined signature `%T' used in signature %s declaration",
sig_ty,
IS_SIGNATURE_POINTER (lhstype) ? "pointer" : "reference");
return error_mark_node;
}
if (TREE_PERMANENT (sig_ty))
{
current_obstack = &permanent_obstack;
saveable_obstack = &permanent_obstack;
}
if (TYPE_LANG_SPECIFIC (rhstype)
&& (IS_SIGNATURE_POINTER (rhstype) || IS_SIGNATURE_REFERENCE (rhstype)))
{
if (SIGNATURE_TYPE (rhstype) == sig_ty)
{
optr_expr = build_optr_ref (rhs);
sptr_expr = build_sptr_ref (rhs);
}
else
{
tree rhs_sptr_ref = build_sptr_ref (rhs);
tree rhs_tbl = build1 (INDIRECT_REF, SIGNATURE_TYPE (rhstype),
rhs_sptr_ref);
sig_tbl = build_sigtable (sig_ty, SIGNATURE_TYPE (rhstype), rhs_tbl);
if (sig_tbl == error_mark_node)
return error_mark_node;
optr_expr = build_optr_ref (rhs);
if (sig_tbl == NULL_TREE)
sptr_expr = copy_node (null_pointer_node);
else if (sig_tbl == integer_zero_node)
sptr_expr = rhs_sptr_ref;
else
sptr_expr = build_unary_op (ADDR_EXPR, sig_tbl, 0);
TREE_TYPE (sptr_expr) = build_pointer_type (sig_ty);
}
}
else
{
sig_tbl = build_sigtable (sig_ty, TREE_TYPE (rhstype), rhs);
if (sig_tbl == error_mark_node)
return error_mark_node;
optr_expr = rhs;
if (sig_tbl == NULL_TREE)
{
sptr_expr = copy_node (null_pointer_node);
TREE_TYPE (sptr_expr) = build_pointer_type (sig_ty);
}
else
sptr_expr = build_unary_op (ADDR_EXPR, sig_tbl, 0);
}
if (initp)
{
result = tree_cons (NULL_TREE, optr_expr,
build_tree_list (NULL_TREE, sptr_expr));
result = build_nt (CONSTRUCTOR, NULL_TREE, result);
result = digest_init (lhstype, result, 0);
}
else
{
if (TREE_READONLY (lhs) || CP_TYPE_CONST_P (lhstype))
readonly_error (lhs, "assignment", 0);
optr_expr = build_modify_expr (build_optr_ref (lhs), NOP_EXPR,
optr_expr);
sptr_expr = build_modify_expr (build_sptr_ref (lhs), NOP_EXPR,
sptr_expr);
result = tree_cons (NULL_TREE, optr_expr,
tree_cons (NULL_TREE, sptr_expr,
build_tree_list (NULL_TREE, lhs)));
result = build_compound_expr (result);
}
current_obstack = ambient_obstack;
saveable_obstack = ambient_saveable_obstack;
return result;
}
static tree
save_this (instance)
tree instance;
{
tree decl;
if (TREE_CODE_CLASS (TREE_CODE (instance)) == 'd')
decl = instance;
else
{
decl = build_decl (VAR_DECL, NULL_TREE, TREE_TYPE (instance));
DECL_REGISTER (decl) = 1;
layout_decl (decl, 0);
expand_decl (decl);
}
return decl;
}
tree
build_signature_method_call (function, parms)
tree function, parms;
{
tree instance = TREE_VALUE (parms);
tree saved_instance = save_this (instance);
tree object_ptr = build_optr_ref (saved_instance);
tree new_object_ptr, new_parms;
tree signature_tbl_ptr = build_sptr_ref (saved_instance);
tree sig_field_name = DECL_NAME (DECL_MEMFUNC_POINTER_TO (function));
tree basetype = DECL_CONTEXT (function);
tree basetype_path = TYPE_BINFO (basetype);
tree tbl_entry = build_component_ref (build1 (INDIRECT_REF, basetype,
signature_tbl_ptr),
sig_field_name, basetype_path, 1);
tree tag, delta, pfn, vt_off, idx, vfn;
tree deflt_call = NULL_TREE, direct_call, virtual_call, result;
tbl_entry = save_expr (tbl_entry);
tag = build_component_ref (tbl_entry, tag_identifier, NULL_TREE, 1);
delta = build_component_ref (tbl_entry, delta_identifier, NULL_TREE, 1);
pfn = build_component_ref (tbl_entry, pfn_identifier, NULL_TREE, 1);
vt_off = build_component_ref (tbl_entry, vt_off_identifier, NULL_TREE, 1);
idx = build_component_ref (tbl_entry, index_identifier, NULL_TREE, 1);
TREE_TYPE (pfn) = build_pointer_type (TREE_TYPE (function));
if (IS_DEFAULT_IMPLEMENTATION (function))
{
pfn = save_expr (pfn);
deflt_call = build_function_call (pfn, parms);
}
new_object_ptr = build (PLUS_EXPR, build_pointer_type (basetype),
cp_convert (ptrdiff_type_node, object_ptr),
cp_convert (ptrdiff_type_node, delta));
parms = tree_cons (NULL_TREE,
cp_convert (build_pointer_type (basetype), object_ptr),
TREE_CHAIN (parms));
new_parms = tree_cons (NULL_TREE, new_object_ptr, TREE_CHAIN (parms));
{
tree old_this = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (TREE_TYPE (pfn))));
TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (TREE_TYPE (pfn))))
= build_qualified_type (build_pointer_type (basetype),
TYPE_QUALS (old_this));
direct_call = build_function_call (pfn, new_parms);
{
tree vfld, vtbl, aref;
vfld = build (PLUS_EXPR,
build_pointer_type (build_pointer_type (vtbl_type_node)),
cp_convert (ptrdiff_type_node, object_ptr),
cp_convert (ptrdiff_type_node, vt_off));
vtbl = build_indirect_ref (build_indirect_ref (vfld, NULL_PTR),
NULL_PTR);
aref = build_array_ref (vtbl, idx);
if (flag_vtable_thunks)
vfn = aref;
else
vfn = build_component_ref (aref, pfn_identifier, NULL_TREE, 0);
TREE_TYPE (vfn) = build_pointer_type (TREE_TYPE (function));
virtual_call = build_function_call (vfn, new_parms);
}
TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (TREE_TYPE (pfn)))) = old_this;
}
if (!direct_call || direct_call == error_mark_node
|| !virtual_call || virtual_call == error_mark_node
|| (IS_DEFAULT_IMPLEMENTATION (function)
&& (!deflt_call || deflt_call == error_mark_node)))
{
compiler_error ("cannot build call of signature member function `%s'",
fndecl_as_string (function, 1));
return error_mark_node;
}
if (IS_DEFAULT_IMPLEMENTATION (function))
{
tree test = build_binary_op_nodefault (LT_EXPR, tag, integer_zero_node,
LT_EXPR);
result = build_conditional_expr (tag,
build_conditional_expr (test,
deflt_call,
virtual_call),
direct_call);
}
else
result = build_conditional_expr (tag, virtual_call, direct_call);
if (instance != saved_instance)
result = build (COMPOUND_EXPR, TREE_TYPE (result),
build_modify_expr (saved_instance, NOP_EXPR, instance),
result);
return result;
}
tree
build_optr_ref (instance)
tree instance;
{
tree field = get_identifier (SIGNATURE_OPTR_NAME);
return build_component_ref (instance, field, NULL_TREE, 1);
}
static tree
build_sptr_ref (instance)
tree instance;
{
tree field = get_identifier (SIGNATURE_SPTR_NAME);
return build_component_ref (instance, field, NULL_TREE, 1);
}