| /* 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 error (); |
| extern void sorry (); |
| extern void compiler_error (); |
| extern void make_decl_rtl PROTO((tree, char *, int)); |
| |
| static tree build_sptr_ref PROTO((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); |
| } |