1070 lines
34 KiB
C
1070 lines
34 KiB
C
/* Functions dealing with signatures and signature pointers/references.
|
|
Copyright (C) 1992, 93, 94, 95, 1996 Free Software Foundation, Inc.
|
|
Contributed by Gerald Baumgartner (gb@cs.purdue.edu)
|
|
|
|
This file is part of GNU CC.
|
|
|
|
GNU CC 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)
|
|
any later version.
|
|
|
|
GNU CC is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GNU CC; see the file COPYING. If not, write to
|
|
the Free Software Foundation, 59 Temple Place - Suite 330,
|
|
Boston, MA 02111-1307, USA. */
|
|
|
|
|
|
#include "config.h"
|
|
#include <stdio.h>
|
|
#include "obstack.h"
|
|
#include "tree.h"
|
|
#include "cp-tree.h"
|
|
#include "flags.h"
|
|
#include "assert.h"
|
|
|
|
extern struct obstack *current_obstack;
|
|
extern struct obstack permanent_obstack;
|
|
extern struct obstack *saveable_obstack;
|
|
|
|
extern void compiler_error ();
|
|
|
|
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, int));
|
|
static void build_signature_pointer_or_reference_decl
|
|
PROTO((tree, tree));
|
|
static tree build_signature_pointer_or_reference_type
|
|
PROTO((tree, int, 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));
|
|
|
|
/* Used to help generate globally unique names for signature tables. */
|
|
|
|
static int global_sigtable_name_counter;
|
|
|
|
/* Build an identifier for a signature pointer or reference, so we
|
|
can use it's name in function name mangling. */
|
|
|
|
static tree
|
|
build_signature_pointer_or_reference_name (to_type, constp, volatilep, refp)
|
|
tree to_type;
|
|
int constp, volatilep, refp;
|
|
{
|
|
char * sig_name = TYPE_NAME_STRING (to_type);
|
|
int name_len = TYPE_NAME_LENGTH (to_type) + constp + volatilep;
|
|
char * name;
|
|
|
|
if (refp)
|
|
{
|
|
name = (char *) alloca (name_len + sizeof (SIGNATURE_REFERENCE_NAME) +2);
|
|
sprintf (name, SIGNATURE_REFERENCE_NAME_FORMAT,
|
|
constp ? "C" : "", volatilep ? "V": "", sig_name);
|
|
}
|
|
else
|
|
{
|
|
name = (char *) alloca (name_len + sizeof (SIGNATURE_POINTER_NAME) + 2);
|
|
sprintf (name, SIGNATURE_POINTER_NAME_FORMAT,
|
|
constp ? "C" : "", volatilep ? "V": "", sig_name);
|
|
}
|
|
return get_identifier (name);
|
|
}
|
|
|
|
/* Build a DECL node for a signature pointer or reference, so we can
|
|
tell the debugger the structure of signature pointers/references.
|
|
This function is called at most eight times for a given signature,
|
|
once for each [const] [volatile] signature pointer/reference. */
|
|
|
|
static void
|
|
build_signature_pointer_or_reference_decl (type, name)
|
|
tree type, name;
|
|
{
|
|
tree decl;
|
|
|
|
/* We don't enter this declaration in any sort of symbol table. */
|
|
decl = build_decl (TYPE_DECL, name, type);
|
|
TYPE_NAME (type) = decl;
|
|
TREE_CHAIN (type) = decl;
|
|
}
|
|
|
|
/* Construct, lay out and return the type of pointers or references
|
|
to signature TO_TYPE. If such a type has already been constructed,
|
|
reuse it. If CONSTP or VOLATILEP is specified, make the `optr' const
|
|
or volatile, respectively. If we are constructing a const/volatile
|
|
type variant and the main type variant doesn't exist yet, it is built
|
|
as well. If REFP is 1, we construct a signature reference, otherwise
|
|
a signature pointer is constructed.
|
|
|
|
This function is a subroutine of `build_signature_pointer_type' and
|
|
`build_signature_reference_type'. */
|
|
|
|
static tree
|
|
build_signature_pointer_or_reference_type (to_type, constp, volatilep, refp)
|
|
tree to_type;
|
|
int constp, volatilep, 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 we don't have the main variant yet, construct it. */
|
|
if (m == NULL_TREE
|
|
&& (constp || volatilep))
|
|
m = build_signature_pointer_or_reference_type (to_type, 0, 0, refp);
|
|
|
|
/* Treat any nonzero argument as 1. */
|
|
constp = !!constp;
|
|
volatilep = !!volatilep;
|
|
refp = !!refp;
|
|
|
|
/* If not generating auxiliary info, search the chain of variants to see
|
|
if there is already one there just like the one we need to have. If so,
|
|
use that existing one.
|
|
|
|
We don't do this in the case where we are generating aux info because
|
|
in that case we want each typedef names to get it's own distinct type
|
|
node, even if the type of this new typedef is the same as some other
|
|
(existing) type. */
|
|
|
|
if (m && !flag_gen_aux_info)
|
|
for (t = m; t; t = TYPE_NEXT_VARIANT (t))
|
|
if (constp == TYPE_READONLY (TREE_TYPE (TREE_TYPE (TYPE_FIELDS (t))))
|
|
&& volatilep == TYPE_VOLATILE (TREE_TYPE (TREE_TYPE (TYPE_FIELDS (t)))))
|
|
return t;
|
|
|
|
/* We need a new one. If TO_TYPE is permanent, make this permanent too. */
|
|
if (TREE_PERMANENT (to_type))
|
|
{
|
|
current_obstack = &permanent_obstack;
|
|
saveable_obstack = &permanent_obstack;
|
|
}
|
|
|
|
/* A signature pointer or reference to a signature `s' looks like this:
|
|
|
|
struct {
|
|
void * optr;
|
|
const s * sptr;
|
|
};
|
|
|
|
A `const' signature pointer/reference is a
|
|
|
|
struct {
|
|
const void * optr;
|
|
const s * sptr;
|
|
};
|
|
|
|
Similarly, for `volatile' and `const volatile'. */
|
|
|
|
t = make_lang_type (RECORD_TYPE);
|
|
{
|
|
tree obj_type = build_type_variant (void_type_node, constp, volatilep);
|
|
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)
|
|
/* We can share the `sptr' field among type variants. */
|
|
sptr = TREE_CHAIN (TYPE_FIELDS (m));
|
|
else
|
|
{
|
|
tree sig_tbl_type = cp_build_type_variant (to_type, 1, 0);
|
|
|
|
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;
|
|
/* Allow signature pointers/references to be grabbed 2 words at a time.
|
|
For this to work on a Sparc, we need 8-byte alignment. */
|
|
TYPE_ALIGN (t) = MAX (TYPE_ALIGN (double_type_node),
|
|
TYPE_ALIGN (optr_type));
|
|
|
|
/* A signature pointer/reference type isn't a `real' class type. */
|
|
IS_AGGR_TYPE (t) = 0;
|
|
}
|
|
|
|
{
|
|
tree name = build_signature_pointer_or_reference_name (to_type, constp,
|
|
volatilep, refp);
|
|
|
|
/* Build a DECL node for this type, so the debugger has access to it. */
|
|
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)
|
|
{
|
|
/* Add this type to the chain of variants of TYPE.
|
|
Every type has to be its own TYPE_MAIN_VARIANT. */
|
|
TYPE_NEXT_VARIANT (t) = TYPE_NEXT_VARIANT (m);
|
|
TYPE_NEXT_VARIANT (m) = t;
|
|
}
|
|
else if (refp)
|
|
/* Record this type as the reference to TO_TYPE. */
|
|
SIGNATURE_REFERENCE_TO (to_type) = t;
|
|
else
|
|
/* Record this type as the pointer to TO_TYPE. */
|
|
SIGNATURE_POINTER_TO (to_type) = t;
|
|
|
|
/* Lay out the type. This function has many callers that are concerned
|
|
with expression-construction, and this simplifies them all.
|
|
Also, it guarantees the TYPE_SIZE is permanent if the type is. */
|
|
layout_type (t);
|
|
|
|
current_obstack = ambient_obstack;
|
|
saveable_obstack = ambient_saveable_obstack;
|
|
|
|
/* Output debug information for this type. */
|
|
rest_of_type_compilation (t, 1);
|
|
|
|
return t;
|
|
}
|
|
|
|
/* Construct, lay out and return the type of pointers to signature TO_TYPE. */
|
|
|
|
tree
|
|
build_signature_pointer_type (to_type, constp, volatilep)
|
|
tree to_type;
|
|
int constp, volatilep;
|
|
{
|
|
return
|
|
build_signature_pointer_or_reference_type (to_type, constp, volatilep, 0);
|
|
}
|
|
|
|
/* Construct, lay out and return the type of pointers to signature TO_TYPE. */
|
|
|
|
tree
|
|
build_signature_reference_type (to_type, constp, volatilep)
|
|
tree to_type;
|
|
int constp, volatilep;
|
|
{
|
|
return
|
|
build_signature_pointer_or_reference_type (to_type, constp, volatilep, 1);
|
|
}
|
|
|
|
/* Return the name of the signature table (as an IDENTIFIER_NODE)
|
|
for the given signature type SIG_TYPE and rhs type RHS_TYPE. */
|
|
|
|
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);
|
|
char *sig_ptr = IDENTIFIER_POINTER (sig_type_id);
|
|
char *rhs_ptr = IDENTIFIER_POINTER (rhs_type_id);
|
|
int i, j;
|
|
|
|
for (i = 0; sig_ptr[i] == OPERATOR_TYPENAME_FORMAT[i]; i++)
|
|
/* do nothing */;
|
|
while (sig_ptr[i] >= '0' && sig_ptr[i] <= '9')
|
|
i += 1;
|
|
|
|
for (j = 0; rhs_ptr[j] == OPERATOR_TYPENAME_FORMAT[j]; j++)
|
|
/* do nothing */;
|
|
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);
|
|
}
|
|
|
|
/* Build a field decl that points to a signature member function. */
|
|
|
|
static tree
|
|
build_member_function_pointer (member)
|
|
tree member;
|
|
{
|
|
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);
|
|
|
|
/* @@ Do we really want to xref signature table fields? */
|
|
GNU_xref_ref (current_function_decl, name);
|
|
|
|
entry = build_lang_field_decl (FIELD_DECL, get_identifier (name),
|
|
TYPE_MAIN_VARIANT (sigtable_entry_type));
|
|
TREE_CONSTANT (entry) = 1;
|
|
TREE_READONLY (entry) = 1;
|
|
|
|
/* @@ Do we really want to xref signature table fields? */
|
|
GNU_xref_decl (current_function_decl, entry);
|
|
|
|
return entry;
|
|
}
|
|
|
|
/* For each FUNCTION_DECL in a signature we construct a member function
|
|
pointer of the appropriate type. We also need two flags to test
|
|
whether the member function pointer points to a virtual function or
|
|
to a default implementation. Those flags will be the two lower order
|
|
bits of the member function pointer (or the two higher order bits,
|
|
based on the configuration).
|
|
|
|
The new FIELD_DECLs are appended at the end of the last (and only)
|
|
sublist of `list_of_fieldlists.'
|
|
|
|
As a side effect, each member function in the signature gets the
|
|
`decl.ignored' bit turned on, so we don't output debug info for it. */
|
|
|
|
void
|
|
append_signature_fields (list_of_fieldlists)
|
|
tree list_of_fieldlists;
|
|
{
|
|
tree l, x;
|
|
tree last_x = NULL_TREE;
|
|
tree mfptr;
|
|
tree last_mfptr;
|
|
tree mfptr_list = NULL_TREE;
|
|
|
|
/* For signatures it should actually be only a list with one element. */
|
|
for (l = list_of_fieldlists; l; l = TREE_CHAIN (l))
|
|
{
|
|
for (x = TREE_VALUE (l); 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;
|
|
}
|
|
}
|
|
last_x = x;
|
|
}
|
|
}
|
|
|
|
/* Append the lists. */
|
|
if (last_x && mfptr_list)
|
|
{
|
|
TREE_CHAIN (last_x) = mfptr_list;
|
|
TREE_CHAIN (last_mfptr) = NULL_TREE;
|
|
}
|
|
}
|
|
|
|
/* Compare the types of a signature member function and a class member
|
|
function. Returns 1 if the types are in the C++ `<=' relationship.
|
|
|
|
If we have a signature pointer/reference as argument or return type
|
|
we don't want to do a recursive conformance check. The conformance
|
|
check only succeeds if both LHS and RHS refer to the same signature
|
|
pointer. Otherwise we need to keep information about parameter types
|
|
around at run time to initialize the signature table correctly. */
|
|
|
|
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);
|
|
|
|
/* The return types have to be the same. */
|
|
if (! comptypes (sig_return_type, class_return_type, 1))
|
|
return 0;
|
|
|
|
/* Compare the first argument `this.' */
|
|
{
|
|
/* Get the type of what the `optr' is pointing to. */
|
|
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) /* Is `this' a sig ptr? */
|
|
class_this = TREE_TYPE (TREE_TYPE (TYPE_FIELDS (class_this)));
|
|
else
|
|
class_this = TREE_TYPE (class_this);
|
|
|
|
/* If a signature method's `this' is const or volatile, so has to be
|
|
the corresponding class method's `this.' */
|
|
if ((TYPE_READONLY (sig_this) && ! TYPE_READONLY (class_this))
|
|
|| (TYPE_VOLATILE (sig_this) && ! TYPE_VOLATILE (class_this)))
|
|
return 0;
|
|
}
|
|
|
|
sig_arg_types = TREE_CHAIN (sig_arg_types);
|
|
class_arg_types = TREE_CHAIN (class_arg_types);
|
|
|
|
/* The number of arguments and the argument types have to be the same. */
|
|
return compparms (sig_arg_types, class_arg_types, 3);
|
|
}
|
|
|
|
/* Undo casts of opaque type variables to the RHS types. */
|
|
|
|
static void
|
|
undo_casts (sig_ty)
|
|
tree sig_ty;
|
|
{
|
|
tree field = TYPE_FIELDS (sig_ty);
|
|
|
|
/* Since all the FIELD_DECLs for the signature table entries are at the end
|
|
of the chain (see `append_signature_fields'), we can do it this way. */
|
|
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);
|
|
}
|
|
|
|
/* Do the type checking necessary to see whether the `rhs' conforms to
|
|
the lhs's `sig_ty'. Depending on the type of `rhs' return a NULL_TREE,
|
|
an integer_zero_node, a constructor, or an expression offsetting the
|
|
`rhs' signature table. */
|
|
|
|
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;
|
|
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)
|
|
{
|
|
/* We've got an opaque type here. */
|
|
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);
|
|
|
|
/* Cast `sig_field' to be of type `oty_type'. This will be
|
|
undone in `undo_casts' by walking over all the TYPE_DECLs. */
|
|
TREE_TYPE (sig_field_type) = TREE_TYPE (oty_type);
|
|
}
|
|
/* If we don't have an opaque type, we can ignore the `typedef'. */
|
|
continue;
|
|
}
|
|
|
|
/* Find the signature method corresponding to `sig_field'. */
|
|
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
|
|
{
|
|
/* We use the signature's default implementation. */
|
|
rhs_method = sig_method;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Find the class method of the correct type. */
|
|
|
|
basetypes = TREE_PURPOSE (baselink);
|
|
if (TREE_CODE (basetypes) == TREE_LIST)
|
|
basetypes = TREE_VALUE (basetypes);
|
|
|
|
rhs_method = TREE_VALUE (baselink);
|
|
for (; rhs_method; rhs_method = TREE_CHAIN (rhs_method))
|
|
if (sig_mname == DECL_NAME (rhs_method)
|
|
&& ! DECL_STATIC_FUNCTION_P (rhs_method)
|
|
&& match_method_types (sig_mtype, TREE_TYPE (rhs_method)))
|
|
break;
|
|
|
|
if (rhs_method == NULL_TREE
|
|
|| (compute_access (basetypes, rhs_method)
|
|
!= access_public_node))
|
|
{
|
|
error ("class `%s' does not contain a method conforming to `%s'",
|
|
TYPE_NAME_STRING (rhstype),
|
|
fndecl_as_string (sig_method, 1));
|
|
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, vt_off;
|
|
tree tag_decl, vb_off_decl, delta_decl, index_decl;
|
|
tree pfn_decl, vt_off_decl;
|
|
|
|
if (rhs_method == sig_method)
|
|
{
|
|
/* default implementation */
|
|
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; /* we can't offset the rhs sig table */
|
|
}
|
|
else if (DECL_VINDEX (rhs_method))
|
|
{
|
|
/* virtual member function */
|
|
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
|
|
{
|
|
/* non-virtual member function */
|
|
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;
|
|
}
|
|
|
|
/* Since digest_init doesn't handle initializing selected fields
|
|
of a struct (i.e., anonymous union), we build the constructor
|
|
by hand, without calling digest_init. */
|
|
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;
|
|
}
|
|
|
|
/* Chain those function address expressions together. */
|
|
if (result)
|
|
result = tree_cons (NULL_TREE, tbl_entry, result);
|
|
else
|
|
result = build_tree_list (NULL_TREE, tbl_entry);
|
|
}
|
|
|
|
if (result == NULL_TREE)
|
|
{
|
|
/* The signature was empty, we don't need a signature table. */
|
|
undo_casts (sig_ty);
|
|
return NULL_TREE;
|
|
}
|
|
|
|
if (offset_p)
|
|
{
|
|
if (first_rhs_field == TYPE_FIELDS (rhstype))
|
|
{
|
|
/* The sptr field on the lhs can be copied from the rhs. */
|
|
undo_casts (sig_ty);
|
|
return integer_zero_node;
|
|
}
|
|
else
|
|
{
|
|
/* The sptr field on the lhs will point into the rhs sigtable. */
|
|
undo_casts (sig_ty);
|
|
return build_component_ref (rhs, DECL_NAME (first_rhs_field),
|
|
NULL_TREE, 0);
|
|
}
|
|
}
|
|
|
|
/* We need to construct a new signature table. */
|
|
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;
|
|
}
|
|
|
|
/* Build a signature table declaration and initialize it or return an
|
|
existing one if we built one already. If we don't get a constructor
|
|
as initialization expression, we don't need a new signature table
|
|
variable and just hand back the init expression.
|
|
|
|
The declaration processing is done by hand instead of using `cp_finish_decl'
|
|
so that we can make signature pointers global variables instead of
|
|
static ones. */
|
|
|
|
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;
|
|
|
|
/* We allow only one signature table to be generated for signatures
|
|
with opaque types. Otherwise we create a loophole in the type
|
|
system since we could cast data from one classes implementation
|
|
of the opaque type to that of another class. */
|
|
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;
|
|
|
|
/* Make the signature table global, not just static in whichever
|
|
function a signature pointer/ref is used for the first time. */
|
|
current_function_decl = NULL_TREE;
|
|
decl = pushdecl_top_level (build_decl (VAR_DECL, name, sig_type));
|
|
current_function_decl = context;
|
|
}
|
|
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
|
|
/* GDB-4.7 doesn't find the initialization value of a signature table
|
|
when it is constant. */
|
|
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;
|
|
}
|
|
|
|
/* Create a constructor or modify expression if the LHS of an assignment
|
|
is a signature pointer or a signature reference. If LHS is a record
|
|
type node, we build a constructor, otherwise a compound expression. */
|
|
|
|
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 SIG_TY is permanent, make the signature table constructor and
|
|
the signature pointer/reference constructor permanent too. */
|
|
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)
|
|
{
|
|
/* LHS and RHS are signature pointers/refs of the same signature. */
|
|
optr_expr = build_optr_ref (rhs);
|
|
sptr_expr = build_sptr_ref (rhs);
|
|
}
|
|
else
|
|
{
|
|
/* We need to create a new signature table and copy
|
|
elements from the rhs signature table. */
|
|
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)
|
|
/* The signature was empty. The signature pointer is
|
|
pretty useless, but the user has been warned. */
|
|
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)
|
|
/* The signature was empty. The signature pointer is
|
|
pretty useless, but the user has been warned. */
|
|
{
|
|
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);
|
|
TREE_HAS_CONSTRUCTOR (result) = 1;
|
|
result = digest_init (lhstype, result, 0);
|
|
}
|
|
else
|
|
{
|
|
if (TREE_READONLY (lhs) || TYPE_READONLY (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;
|
|
}
|
|
|
|
/* Build a temporary variable declaration for the instance of a signature
|
|
member function call if it isn't a declaration node already. Simply
|
|
using a SAVE_EXPR doesn't work since we need `this' in both branches
|
|
of a conditional expression. */
|
|
|
|
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;
|
|
}
|
|
|
|
/* Build a signature member function call. Looks up the signature table
|
|
entry corresponding to FUNCTION. Depending on the value of the CODE
|
|
field, either call the function in PFN directly, or use OFFSET to
|
|
index the object's virtual function table. */
|
|
|
|
tree
|
|
build_signature_method_call (function, parms)
|
|
tree function, parms;
|
|
{
|
|
tree instance = TREE_VALUE (parms);
|
|
tree saved_instance = save_this (instance); /* Create temp for `this'. */
|
|
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));
|
|
|
|
{
|
|
/* Cast the signature method to have `this' of a normal pointer type. */
|
|
tree old_this = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (TREE_TYPE (pfn))));
|
|
|
|
TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (TREE_TYPE (pfn))))
|
|
= build_type_variant (build_pointer_type (basetype),
|
|
TYPE_READONLY (old_this),
|
|
TYPE_VOLATILE (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);
|
|
}
|
|
|
|
/* Undo the cast, make `this' a signature pointer again. */
|
|
TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (TREE_TYPE (pfn)))) = old_this;
|
|
}
|
|
|
|
/* Once the function was found, there should be no reason why we
|
|
couldn't build the member function pointer call. */
|
|
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 we created a temporary variable for `this', initialize it first. */
|
|
if (instance != saved_instance)
|
|
result = build (COMPOUND_EXPR, TREE_TYPE (result),
|
|
build_modify_expr (saved_instance, NOP_EXPR, instance),
|
|
result);
|
|
|
|
return result;
|
|
}
|
|
|
|
/* Create a COMPONENT_REF expression for referencing the OPTR field
|
|
of a signature pointer or reference. */
|
|
|
|
tree
|
|
build_optr_ref (instance)
|
|
tree instance;
|
|
{
|
|
tree field = get_identifier (SIGNATURE_OPTR_NAME);
|
|
|
|
return build_component_ref (instance, field, NULL_TREE, 1);
|
|
}
|
|
|
|
/* Create a COMPONENT_REF expression for referencing the SPTR field
|
|
of a signature pointer or reference. */
|
|
|
|
static tree
|
|
build_sptr_ref (instance)
|
|
tree instance;
|
|
{
|
|
tree field = get_identifier (SIGNATURE_SPTR_NAME);
|
|
|
|
return build_component_ref (instance, field, NULL_TREE, 1);
|
|
}
|