/* Name mangling for the 3.0 C++ ABI. Copyright (C) 2000, 2001, 2002, 2003 Free Software Foundation, Inc. Written by Alex Samuel This file is part of GCC. GCC 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. GCC 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 GCC; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* This file implements mangling of C++ names according to the IA64 C++ ABI specification. A mangled name encodes a function or variable's name, scope, type, and/or template arguments into a text identifier. This identifier is used as the function's or variable's linkage name, to preserve compatibility between C++'s language features (templates, scoping, and overloading) and C linkers. Additionally, g++ uses mangled names internally. To support this, mangling of types is allowed, even though the mangled name of a type should not appear by itself as an exported name. Ditto for uninstantiated templates. The primary entry point for this module is mangle_decl, which returns an identifier containing the mangled name for a decl. Additional entry points are provided to build mangled names of particular constructs when the appropriate decl for that construct is not available. These are: mangle_typeinfo_for_type: typeinfo data mangle_typeinfo_string_for_type: typeinfo type name mangle_vtbl_for_type: virtual table data mangle_vtt_for_type: VTT data mangle_ctor_vtbl_for_type: `C-in-B' constructor virtual table data mangle_thunk: thunk function or entry */ #include "config.h" #include "system.h" #include "coretypes.h" #include "tm.h" #include "tree.h" #include "cp-tree.h" #include "real.h" #include "obstack.h" #include "toplev.h" #include "varray.h" /* Debugging support. */ /* Define DEBUG_MANGLE to enable very verbose trace messages. */ #ifndef DEBUG_MANGLE #define DEBUG_MANGLE 0 #endif /* Macros for tracing the write_* functions. */ #if DEBUG_MANGLE # define MANGLE_TRACE(FN, INPUT) \ fprintf (stderr, " %-24s: %-24s\n", (FN), (INPUT)) # define MANGLE_TRACE_TREE(FN, NODE) \ fprintf (stderr, " %-24s: %-24s (%p)\n", \ (FN), tree_code_name[TREE_CODE (NODE)], (void *) (NODE)) #else # define MANGLE_TRACE(FN, INPUT) # define MANGLE_TRACE_TREE(FN, NODE) #endif /* Nonzero if NODE is a class template-id. We can't rely on CLASSTYPE_USE_TEMPLATE here because of tricky bugs in the parser that hard to distinguish A from A, where A is the type as instantiated outside of the template, and A is the type used without parameters inside the template. */ #define CLASSTYPE_TEMPLATE_ID_P(NODE) \ (TYPE_LANG_SPECIFIC (NODE) != NULL \ && (TREE_CODE (NODE) == BOUND_TEMPLATE_TEMPLATE_PARM \ || (CLASSTYPE_TEMPLATE_INFO (NODE) != NULL \ && (PRIMARY_TEMPLATE_P (CLASSTYPE_TI_TEMPLATE (NODE)))))) /* Things we only need one of. This module is not reentrant. */ static struct globals { /* The name in which we're building the mangled name. */ struct obstack name_obstack; /* An array of the current substitution candidates, in the order we've seen them. */ varray_type substitutions; /* The entity that is being mangled. */ tree entity; /* We are mangling an internal symbol. It is important to keep those involving template parmeters distinct by distinguishing their level and, for non-type parms, their type. */ bool internal_mangling_p; /* True if the mangling will be different in a future version of the ABI. */ bool need_abi_warning; } G; /* Indices into subst_identifiers. These are identifiers used in special substitution rules. */ typedef enum { SUBID_ALLOCATOR, SUBID_BASIC_STRING, SUBID_CHAR_TRAITS, SUBID_BASIC_ISTREAM, SUBID_BASIC_OSTREAM, SUBID_BASIC_IOSTREAM, SUBID_MAX } substitution_identifier_index_t; /* For quick substitution checks, look up these common identifiers once only. */ static GTY(()) tree subst_identifiers[SUBID_MAX]; /* Single-letter codes for builtin integer types, defined in . These are indexed by integer_type_kind values. */ static const char integer_type_codes[itk_none] = { 'c', /* itk_char */ 'a', /* itk_signed_char */ 'h', /* itk_unsigned_char */ 's', /* itk_short */ 't', /* itk_unsigned_short */ 'i', /* itk_int */ 'j', /* itk_unsigned_int */ 'l', /* itk_long */ 'm', /* itk_unsigned_long */ 'x', /* itk_long_long */ 'y' /* itk_unsigned_long_long */ }; static int decl_is_template_id PARAMS ((tree, tree*)); /* Functions for handling substitutions. */ static inline tree canonicalize_for_substitution PARAMS ((tree)); static void add_substitution PARAMS ((tree)); static inline int is_std_substitution PARAMS ((tree, substitution_identifier_index_t)); static inline int is_std_substitution_char PARAMS ((tree, substitution_identifier_index_t)); static int find_substitution PARAMS ((tree)); static void mangle_call_offset PARAMS ((tree, tree)); /* Functions for emitting mangled representations of things. */ static void write_mangled_name PARAMS ((tree)); static void write_encoding PARAMS ((tree)); static void write_name PARAMS ((tree, int)); static void write_unscoped_name PARAMS ((tree)); static void write_unscoped_template_name PARAMS ((tree)); static void write_nested_name PARAMS ((tree)); static void write_prefix PARAMS ((tree)); static void write_template_prefix PARAMS ((tree)); static void write_unqualified_name PARAMS ((tree)); static void write_conversion_operator_name (tree); static void write_source_name PARAMS ((tree)); static int hwint_to_ascii PARAMS ((unsigned HOST_WIDE_INT, unsigned int, char *, unsigned)); static void write_number PARAMS ((unsigned HOST_WIDE_INT, int, unsigned int)); static void write_integer_cst PARAMS ((tree)); static void write_identifier PARAMS ((const char *)); static void write_special_name_constructor PARAMS ((tree)); static void write_special_name_destructor PARAMS ((tree)); static void write_type PARAMS ((tree)); static int write_CV_qualifiers_for_type PARAMS ((tree)); static void write_builtin_type PARAMS ((tree)); static void write_function_type PARAMS ((tree)); static void write_bare_function_type PARAMS ((tree, int, tree)); static void write_method_parms PARAMS ((tree, int, tree)); static void write_class_enum_type PARAMS ((tree)); static void write_template_args PARAMS ((tree)); static void write_expression PARAMS ((tree)); static void write_template_arg_literal PARAMS ((tree)); static void write_template_arg PARAMS ((tree)); static void write_template_template_arg PARAMS ((tree)); static void write_array_type PARAMS ((tree)); static void write_pointer_to_member_type PARAMS ((tree)); static void write_template_param PARAMS ((tree)); static void write_template_template_param PARAMS ((tree)); static void write_substitution PARAMS ((int)); static int discriminator_for_local_entity PARAMS ((tree)); static int discriminator_for_string_literal PARAMS ((tree, tree)); static void write_discriminator PARAMS ((int)); static void write_local_name PARAMS ((tree, tree, tree)); static void dump_substitution_candidates PARAMS ((void)); static const char *mangle_decl_string PARAMS ((tree)); /* Control functions. */ static inline void start_mangling (tree); static inline const char *finish_mangling (bool); static tree mangle_special_for_type PARAMS ((tree, const char *)); /* Foreign language functions. */ static void write_java_integer_type_codes PARAMS ((tree)); /* Append a single character to the end of the mangled representation. */ #define write_char(CHAR) \ obstack_1grow (&G.name_obstack, (CHAR)) /* Append a sized buffer to the end of the mangled representation. */ #define write_chars(CHAR, LEN) \ obstack_grow (&G.name_obstack, (CHAR), (LEN)) /* Append a NUL-terminated string to the end of the mangled representation. */ #define write_string(STRING) \ obstack_grow (&G.name_obstack, (STRING), strlen (STRING)) /* Nonzero if NODE1 and NODE2 are both TREE_LIST nodes and have the same purpose (context, which may be a type) and value (template decl). See write_template_prefix for more information on what this is used for. */ #define NESTED_TEMPLATE_MATCH(NODE1, NODE2) \ (TREE_CODE (NODE1) == TREE_LIST \ && TREE_CODE (NODE2) == TREE_LIST \ && ((TYPE_P (TREE_PURPOSE (NODE1)) \ && same_type_p (TREE_PURPOSE (NODE1), TREE_PURPOSE (NODE2)))\ || TREE_PURPOSE (NODE1) == TREE_PURPOSE (NODE2)) \ && TREE_VALUE (NODE1) == TREE_VALUE (NODE2)) /* Write out an unsigned quantity in base 10. */ #define write_unsigned_number(NUMBER) \ write_number ((NUMBER), /*unsigned_p=*/1, 10) /* If DECL is a template instance, return nonzero and, if TEMPLATE_INFO is non-NULL, set *TEMPLATE_INFO to its template info. Otherwise return zero. */ static int decl_is_template_id (decl, template_info) tree decl; tree* template_info; { if (TREE_CODE (decl) == TYPE_DECL) { /* TYPE_DECLs are handled specially. Look at its type to decide if this is a template instantiation. */ tree type = TREE_TYPE (decl); if (CLASS_TYPE_P (type) && CLASSTYPE_TEMPLATE_ID_P (type)) { if (template_info != NULL) /* For a templated TYPE_DECL, the template info is hanging off the type. */ *template_info = TYPE_TEMPLATE_INFO (type); return 1; } } else { /* Check if this is a primary template. */ if (DECL_LANG_SPECIFIC (decl) != NULL && DECL_USE_TEMPLATE (decl) && PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (decl)) && TREE_CODE (decl) != TEMPLATE_DECL) { if (template_info != NULL) /* For most templated decls, the template info is hanging off the decl. */ *template_info = DECL_TEMPLATE_INFO (decl); return 1; } } /* It's not a template id. */ return 0; } /* Produce debugging output of current substitution candidates. */ static void dump_substitution_candidates () { unsigned i; fprintf (stderr, " ++ substitutions "); for (i = 0; i < VARRAY_ACTIVE_SIZE (G.substitutions); ++i) { tree el = VARRAY_TREE (G.substitutions, i); const char *name = "???"; if (i > 0) fprintf (stderr, " "); if (DECL_P (el)) name = IDENTIFIER_POINTER (DECL_NAME (el)); else if (TREE_CODE (el) == TREE_LIST) name = IDENTIFIER_POINTER (DECL_NAME (TREE_VALUE (el))); else if (TYPE_NAME (el)) name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (el))); fprintf (stderr, " S%d_ = ", i - 1); if (TYPE_P (el) && (CP_TYPE_RESTRICT_P (el) || CP_TYPE_VOLATILE_P (el) || CP_TYPE_CONST_P (el))) fprintf (stderr, "CV-"); fprintf (stderr, "%s (%s at %p)\n", name, tree_code_name[TREE_CODE (el)], (void *) el); } } /* Both decls and types can be substitution candidates, but sometimes they refer to the same thing. For instance, a TYPE_DECL and RECORD_TYPE for the same class refer to the same thing, and should be treated accordinginly in substitutions. This function returns a canonicalized tree node representing NODE that is used when adding and substitution candidates and finding matches. */ static inline tree canonicalize_for_substitution (node) tree node; { /* For a TYPE_DECL, use the type instead. */ if (TREE_CODE (node) == TYPE_DECL) node = TREE_TYPE (node); if (TYPE_P (node)) node = canonical_type_variant (node); return node; } /* Add NODE as a substitution candidate. NODE must not already be on the list of candidates. */ static void add_substitution (node) tree node; { tree c; if (DEBUG_MANGLE) fprintf (stderr, " ++ add_substitution (%s at %10p)\n", tree_code_name[TREE_CODE (node)], (void *) node); /* Get the canonicalized substitution candidate for NODE. */ c = canonicalize_for_substitution (node); if (DEBUG_MANGLE && c != node) fprintf (stderr, " ++ using candidate (%s at %10p)\n", tree_code_name[TREE_CODE (node)], (void *) node); node = c; #if ENABLE_CHECKING /* Make sure NODE isn't already a candidate. */ { int i; for (i = VARRAY_ACTIVE_SIZE (G.substitutions); --i >= 0; ) { tree candidate = VARRAY_TREE (G.substitutions, i); if ((DECL_P (node) && node == candidate) || (TYPE_P (node) && TYPE_P (candidate) && same_type_p (node, candidate))) abort (); } } #endif /* ENABLE_CHECKING */ /* Put the decl onto the varray of substitution candidates. */ VARRAY_PUSH_TREE (G.substitutions, node); if (DEBUG_MANGLE) dump_substitution_candidates (); } /* Helper function for find_substitution. Returns nonzero if NODE, which may be a decl or a CLASS_TYPE, is a template-id with template name of substitution_index[INDEX] in the ::std namespace. */ static inline int is_std_substitution (node, index) tree node; substitution_identifier_index_t index; { tree type = NULL; tree decl = NULL; if (DECL_P (node)) { type = TREE_TYPE (node); decl = node; } else if (CLASS_TYPE_P (node)) { type = node; decl = TYPE_NAME (node); } else /* These are not the droids you're looking for. */ return 0; return (DECL_NAMESPACE_STD_P (CP_DECL_CONTEXT (decl)) && TYPE_LANG_SPECIFIC (type) && TYPE_TEMPLATE_INFO (type) && (DECL_NAME (TYPE_TI_TEMPLATE (type)) == subst_identifiers[index])); } /* Helper function for find_substitution. Returns nonzero if NODE, which may be a decl or a CLASS_TYPE, is the template-id ::std::identifier, where identifier is substitution_index[INDEX]. */ static inline int is_std_substitution_char (node, index) tree node; substitution_identifier_index_t index; { tree args; /* Check NODE's name is ::std::identifier. */ if (!is_std_substitution (node, index)) return 0; /* Figure out its template args. */ if (DECL_P (node)) args = DECL_TI_ARGS (node); else if (CLASS_TYPE_P (node)) args = CLASSTYPE_TI_ARGS (node); else /* Oops, not a template. */ return 0; /* NODE's template arg list should be . */ return TREE_VEC_LENGTH (args) == 1 && TREE_VEC_ELT (args, 0) == char_type_node; } /* Check whether a substitution should be used to represent NODE in the mangling. First, check standard special-case substitutions. ::= St # ::std ::= Sa # ::std::allocator ::= Sb # ::std::basic_string ::= Ss # ::std::basic_string, ::std::allocator > ::= Si # ::std::basic_istream > ::= So # ::std::basic_ostream > ::= Sd # ::std::basic_iostream > Then examine the stack of currently available substitution candidates for entities appearing earlier in the same mangling If a substitution is found, write its mangled representation and return nonzero. If none is found, just return zero. */ static int find_substitution (node) tree node; { int i; int size = VARRAY_ACTIVE_SIZE (G.substitutions); tree decl; tree type; if (DEBUG_MANGLE) fprintf (stderr, " ++ find_substitution (%s at %p)\n", tree_code_name[TREE_CODE (node)], (void *) node); /* Obtain the canonicalized substitution representation for NODE. This is what we'll compare against. */ node = canonicalize_for_substitution (node); /* Check for builtin substitutions. */ decl = TYPE_P (node) ? TYPE_NAME (node) : node; type = TYPE_P (node) ? node : TREE_TYPE (node); /* Check for std::allocator. */ if (decl && is_std_substitution (decl, SUBID_ALLOCATOR) && !CLASSTYPE_USE_TEMPLATE (TREE_TYPE (decl))) { write_string ("Sa"); return 1; } /* Check for std::basic_string. */ if (decl && is_std_substitution (decl, SUBID_BASIC_STRING)) { if (TYPE_P (node)) { /* If this is a type (i.e. a fully-qualified template-id), check for std::basic_string , std::allocator > . */ if (cp_type_quals (type) == TYPE_UNQUALIFIED && CLASSTYPE_USE_TEMPLATE (type)) { tree args = CLASSTYPE_TI_ARGS (type); if (TREE_VEC_LENGTH (args) == 3 && same_type_p (TREE_VEC_ELT (args, 0), char_type_node) && is_std_substitution_char (TREE_VEC_ELT (args, 1), SUBID_CHAR_TRAITS) && is_std_substitution_char (TREE_VEC_ELT (args, 2), SUBID_ALLOCATOR)) { write_string ("Ss"); return 1; } } } else /* Substitute for the template name only if this isn't a type. */ { write_string ("Sb"); return 1; } } /* Check for basic_{i,o,io}stream. */ if (TYPE_P (node) && cp_type_quals (type) == TYPE_UNQUALIFIED && CLASS_TYPE_P (type) && CLASSTYPE_USE_TEMPLATE (type) && CLASSTYPE_TEMPLATE_INFO (type) != NULL) { /* First, check for the template args > . */ tree args = CLASSTYPE_TI_ARGS (type); if (TREE_VEC_LENGTH (args) == 2 && same_type_p (TREE_VEC_ELT (args, 0), char_type_node) && is_std_substitution_char (TREE_VEC_ELT (args, 1), SUBID_CHAR_TRAITS)) { /* Got them. Is this basic_istream? */ tree name = DECL_NAME (CLASSTYPE_TI_TEMPLATE (type)); if (name == subst_identifiers[SUBID_BASIC_ISTREAM]) { write_string ("Si"); return 1; } /* Or basic_ostream? */ else if (name == subst_identifiers[SUBID_BASIC_OSTREAM]) { write_string ("So"); return 1; } /* Or basic_iostream? */ else if (name == subst_identifiers[SUBID_BASIC_IOSTREAM]) { write_string ("Sd"); return 1; } } } /* Check for namespace std. */ if (decl && DECL_NAMESPACE_STD_P (decl)) { write_string ("St"); return 1; } /* Now check the list of available substitutions for this mangling operation. */ for (i = 0; i < size; ++i) { tree candidate = VARRAY_TREE (G.substitutions, i); /* NODE is a matched to a candidate if it's the same decl node or if it's the same type. */ if (decl == candidate || (TYPE_P (candidate) && type && TYPE_P (type) && same_type_p (type, candidate)) || NESTED_TEMPLATE_MATCH (node, candidate)) { write_substitution (i); return 1; } } /* No substitution found. */ return 0; } /* ::= _Z */ static inline void write_mangled_name (decl) tree decl; { MANGLE_TRACE_TREE ("mangled-name", decl); if (DECL_LANG_SPECIFIC (decl) && DECL_EXTERN_C_FUNCTION_P (decl) && ! DECL_OVERLOADED_OPERATOR_P (decl)) /* The standard notes: "The of an extern "C" function is treated like global-scope data, i.e. as its without a type." We cannot write overloaded operators that way though, because it contains characters invalid in assembler. */ write_source_name (DECL_NAME (decl)); else /* C++ name; needs to be mangled. */ { write_string ("_Z"); write_encoding (decl); } } /* ::= ::= */ static void write_encoding (decl) tree decl; { MANGLE_TRACE_TREE ("encoding", decl); if (DECL_LANG_SPECIFIC (decl) && DECL_EXTERN_C_FUNCTION_P (decl)) { /* For overloaded operators write just the mangled name without arguments. */ if (DECL_OVERLOADED_OPERATOR_P (decl)) write_name (decl, /*ignore_local_scope=*/0); else write_source_name (DECL_NAME (decl)); return; } write_name (decl, /*ignore_local_scope=*/0); if (TREE_CODE (decl) == FUNCTION_DECL) { tree fn_type; if (decl_is_template_id (decl, NULL)) fn_type = get_mostly_instantiated_function_type (decl); else fn_type = TREE_TYPE (decl); write_bare_function_type (fn_type, (!DECL_CONSTRUCTOR_P (decl) && !DECL_DESTRUCTOR_P (decl) && !DECL_CONV_FN_P (decl) && decl_is_template_id (decl, NULL)), decl); } } /* ::= ::= ::= ::= If IGNORE_LOCAL_SCOPE is nonzero, this production of is called from , which mangles the enclosing scope elsewhere and then uses this function to mangle just the part underneath the function scope. So don't use the production, to avoid an infinite recursion. */ static void write_name (decl, ignore_local_scope) tree decl; int ignore_local_scope; { tree context; MANGLE_TRACE_TREE ("name", decl); if (TREE_CODE (decl) == TYPE_DECL) { /* In case this is a typedef, fish out the corresponding TYPE_DECL for the main variant. */ decl = TYPE_NAME (TYPE_MAIN_VARIANT (TREE_TYPE (decl))); context = TYPE_CONTEXT (TYPE_MAIN_VARIANT (TREE_TYPE (decl))); } else context = (DECL_CONTEXT (decl) == NULL) ? NULL : CP_DECL_CONTEXT (decl); /* A decl in :: or ::std scope is treated specially. The former is mangled using or , the latter with a special substitution. Also, a name that is directly in a local function scope is also mangled with rather than a full . */ if (context == NULL || context == global_namespace || DECL_NAMESPACE_STD_P (context) || (ignore_local_scope && TREE_CODE (context) == FUNCTION_DECL)) { tree template_info; /* Is this a template instance? */ if (decl_is_template_id (decl, &template_info)) { /* Yes: use . */ write_unscoped_template_name (TI_TEMPLATE (template_info)); write_template_args (TI_ARGS (template_info)); } else /* Everything else gets an . */ write_unscoped_name (decl); } else { /* Handle local names, unless we asked not to (that is, invoked under , to handle only the part of the name under the local scope). */ if (!ignore_local_scope) { /* Scan up the list of scope context, looking for a function. If we find one, this entity is in local function scope. local_entity tracks context one scope level down, so it will contain the element that's directly in that function's scope, either decl or one of its enclosing scopes. */ tree local_entity = decl; while (context != NULL && context != global_namespace) { /* Make sure we're always dealing with decls. */ if (context != NULL && TYPE_P (context)) context = TYPE_NAME (context); /* Is this a function? */ if (TREE_CODE (context) == FUNCTION_DECL) { /* Yes, we have local scope. Use the production for the innermost function scope. */ write_local_name (context, local_entity, decl); return; } /* Up one scope level. */ local_entity = context; context = CP_DECL_CONTEXT (context); } /* No local scope found? Fall through to . */ } /* Other decls get a to encode their scope. */ write_nested_name (decl); } } /* ::= ::= St # ::std:: */ static void write_unscoped_name (decl) tree decl; { tree context = CP_DECL_CONTEXT (decl); MANGLE_TRACE_TREE ("unscoped-name", decl); /* Is DECL in ::std? */ if (DECL_NAMESPACE_STD_P (context)) { write_string ("St"); write_unqualified_name (decl); } /* If not, it should be either in the global namespace, or directly in a local function scope. */ else if (context == global_namespace || context == NULL || TREE_CODE (context) == FUNCTION_DECL) write_unqualified_name (decl); else abort (); } /* ::= ::= */ static void write_unscoped_template_name (decl) tree decl; { MANGLE_TRACE_TREE ("unscoped-template-name", decl); if (find_substitution (decl)) return; write_unscoped_name (decl); add_substitution (decl); } /* Write the nested name, including CV-qualifiers, of DECL. ::= N [] E ::= N [] E ::= [r] [V] [K] */ static void write_nested_name (decl) tree decl; { tree template_info; MANGLE_TRACE_TREE ("nested-name", decl); write_char ('N'); /* Write CV-qualifiers, if this is a member function. */ if (TREE_CODE (decl) == FUNCTION_DECL && DECL_NONSTATIC_MEMBER_FUNCTION_P (decl)) { if (DECL_VOLATILE_MEMFUNC_P (decl)) write_char ('V'); if (DECL_CONST_MEMFUNC_P (decl)) write_char ('K'); } /* Is this a template instance? */ if (decl_is_template_id (decl, &template_info)) { /* Yes, use . */ write_template_prefix (decl); write_template_args (TI_ARGS (template_info)); } else { /* No, just use */ write_prefix (DECL_CONTEXT (decl)); write_unqualified_name (decl); } write_char ('E'); } /* ::= ::= ::= ::= # empty ::= */ static void write_prefix (node) tree node; { tree decl; /* Non-NULL if NODE represents a template-id. */ tree template_info = NULL; MANGLE_TRACE_TREE ("prefix", node); if (node == NULL || node == global_namespace) return; if (find_substitution (node)) return; if (DECL_P (node)) { /* If this is a function decl, that means we've hit function scope, so this prefix must be for a local name. In this case, we're under the production, which encodes the enclosing function scope elsewhere. So don't continue here. */ if (TREE_CODE (node) == FUNCTION_DECL) return; decl = node; decl_is_template_id (decl, &template_info); } else { /* Node is a type. */ decl = TYPE_NAME (node); if (CLASSTYPE_TEMPLATE_ID_P (node)) template_info = TYPE_TEMPLATE_INFO (node); } /* In G++ 3.2, the name of the template parameter was used. */ if (TREE_CODE (node) == TEMPLATE_TYPE_PARM && !abi_version_at_least (2)) G.need_abi_warning = true; if (TREE_CODE (node) == TEMPLATE_TYPE_PARM && abi_version_at_least (2)) write_template_param (node); else if (template_info != NULL) /* Templated. */ { write_template_prefix (decl); write_template_args (TI_ARGS (template_info)); } else /* Not templated. */ { write_prefix (CP_DECL_CONTEXT (decl)); write_unqualified_name (decl); } add_substitution (node); } /* ::=