/* * Copyright (c) 2002, The Tendra Project * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * * Crown Copyright (c) 1997, 1998 * * This TenDRA(r) Computer Program is subject to Copyright * owned by the United Kingdom Secretary of State for Defence * acting through the Defence Evaluation and Research Agency * (DERA). It is made available to Recipients with a * royalty-free licence for its use, reproduction, transfer * to other parties and amendment for any purpose not excluding * product development provided that any such use et cetera * shall be deemed to be acceptance of the following conditions:- * * (1) Its Recipients shall ensure that this Notice is * reproduced upon any copies or amended versions of it; * * (2) Any amended version of it shall be clearly marked to * show both the nature of and the organisation responsible * for the relevant amendment or amendments; * * (3) Its onward transfer from a recipient to another * party shall be deemed to be that party's acceptance of * these conditions; * * (4) DERA gives no warranty or assurance as to its * quality or suitability for any purpose and DERA accepts * no liability whatsoever in relation to any use to which * it may be put. * * $TenDRA: tendra/src/producers/common/construct/namespace.c,v 1.9 2005/08/04 20:22:16 stefanf Exp $ */ #include "config.h" #include "producer.h" #include "c_types.h" #include "ctype_ops.h" #include "graph_ops.h" #include "hashid_ops.h" #include "id_ops.h" #include "member_ops.h" #include "nspace_ops.h" #include "type_ops.h" #include "error.h" #include "catalog.h" #include "option.h" #include "access.h" #include "class.h" #include "declare.h" #include "derive.h" #include "dump.h" #include "hash.h" #include "identifier.h" #include "instance.h" #include "label.h" #include "namespace.h" #include "parse.h" #include "predict.h" #include "print.h" #include "redeclare.h" #include "syntax.h" #include "tokdef.h" #include "ustring.h" #include "variable.h" /* * CURRENT NAMESPACE LISTS * * These variables give the current namespace (the one which is currently * being defined) and the stack of all enclosing namespaces. Another stack * of namespaces, which will be a superset of the first, gives the list * of locations to be searched during name look-up. */ NAMESPACE crt_namespace = NULL_nspace; NAMESPACE last_namespace = NULL_nspace; NAMESPACE qual_namespace = NULL_nspace; STACK (NAMESPACE) namespace_stack = NULL_stack (NAMESPACE); STACK (NAMESPACE) crt_nspace_stack = NULL_stack (NAMESPACE); STACK (NAMESPACE) local_nspace_stack = NULL_stack (NAMESPACE); /* * STANDARD NAMESPACES * * These variables give the various standard namespaces, including the * global namespace and the external token namespace. In addition a * dynamic record of the smallest named namespace enclosing the current * namespace and the smallest named or block namespace (or, if the * proto_scope option is true, function prototype namespace) enclosing * the current namespace are maintained in nonblock_namespace and * nonclass_namespace respectively. */ NAMESPACE global_namespace = NULL_nspace; NAMESPACE token_namespace = NULL_nspace; NAMESPACE c_namespace = NULL_nspace; NAMESPACE nonblock_namespace = NULL_nspace; NAMESPACE nonclass_namespace = NULL_nspace; static NAMESPACE scope_namespace = NULL_nspace; /* * STANDARD NAMESPACE IDENTIFIERS * * The identifier local_namespace_id is used as the name for all unnamed * namespaces defined within the current translation unit. */ static IDENTIFIER local_namespace_id = NULL_id; /* * NAME LOOK-UP CACHE FLAG * * This flag is set to indicate that name look-up should be cached * whenever possible. It is switched off temporarily in field selectors * as an optimisation. */ int cache_lookup = 1; int old_cache_lookup = 1; /* * CREATE A STANDARD NAMESPACE * * This routine creates a global namespace of size sz with a dummy name * given by s. */ NAMESPACE make_global_nspace(const char *s, int sz) { IDENTIFIER id; string u = ustrlit (s); NAMESPACE ns = NULL_nspace; DECL_SPEC ds = (dspec_defn | dspec_extern); HASHID nm = lookup_name (u, hash (u), 1, lex_identifier); MAKE_id_nspace_name (nm, ds, NULL_nspace, crt_loc, ns, id); ns = make_namespace (id, nspace_global_tag, sz); COPY_nspace (id_nspace_name_defn (id), ns); return (ns); } /* * INITIALISE STANDARD NAMESPACES * * This routine initialises the standard namespaces above. */ void init_namespace() { string s = ustrlit (""); HASHID nm = lookup_name (s, hash (s), 1, lex_identifier); local_namespace_id = DEREF_id (hashid_id (nm)); global_namespace = make_global_nspace ("", 50); #if LANGUAGE_CPP c_namespace = make_global_nspace ("", 50); #endif token_namespace = make_global_nspace ("", 50); scope_namespace = make_global_nspace ("", 50); last_namespace = global_namespace; old_cache_lookup = cache_lookup; return; } /* * DOES ONE NAMESPACE CONTAIN ANOTHER? * * This routine checks whether the namespace ns contains the definition * of the namespace pns. */ int is_subnspace(NAMESPACE ns, NAMESPACE pns) { if (EQ_nspace (ns, pns)) return (1); while (!IS_NULL_nspace (pns)) { pns = DEREF_nspace (nspace_parent (pns)); if (EQ_nspace (ns, pns)) return (1); } return (0); } /* * FIND THE JOIN OF TWO NAMESPACES * * This routine finds the join of the namespaces ns and nt, that is to * say the smallest namespace which contains both ns and nt. A using * directive for nt in ns causes the members of nt to be treated as * members of the join namespace for the purposes of unqualified name * look-up. */ static NAMESPACE join_namespace(NAMESPACE ns, NAMESPACE nt) { while (!EQ_nspace (ns, nt)) { if (IS_NULL_nspace (ns)) return (NULL_nspace); if (IS_NULL_nspace (nt)) return (NULL_nspace); if (is_subnspace (ns, nt)) return (ns); if (is_subnspace (nt, ns)) return (nt); ns = DEREF_nspace (nspace_parent (ns)); nt = DEREF_nspace (nspace_parent (nt)); } return (ns); } /* * UNCACHE A NAMESPACE * * This routine clears the cached name look-ups for all the names * accessible from the namespace ns. */ void uncache_namespace(NAMESPACE ns, int add) { if (!IS_NULL_nspace (ns) && cache_lookup) { MEMBER mem; LIST (NAMESPACE) uns; /* Clear cache fields for all members */ if (IS_nspace_named_etc (ns)) { mem = DEREF_member (nspace_named_etc_first (ns)); } else { mem = DEREF_member (nspace_last (ns)); } while (!IS_NULL_member (mem)) { IDENTIFIER id = DEREF_id (member_id (mem)); if (!IS_NULL_id (id)) { HASHID nm = DEREF_hashid (id_name (id)); if (!add) id = NULL_id; COPY_id (hashid_cache (nm), id); } mem = DEREF_member (member_next (mem)); } /* Recursively clear all used namespaces */ uns = DEREF_list (nspace_use (ns)); if (!IS_NULL_list (uns)) { LIST (NAMESPACE) lns = uns; COPY_list (nspace_use (ns), NULL_list (NAMESPACE)); while (!IS_NULL_list (lns)) { NAMESPACE pns = DEREF_nspace (HEAD_list (lns)); uncache_namespace (pns, 0); lns = TAIL_list (lns); } COPY_list (nspace_use (ns), uns); } } return; } /* * ADD A NAMESPACE TO THE LOOK-UP LIST * * This routine adds the namespace ns to the look-up namespace list. Note * that this is automatically called by push_namespace. */ void add_namespace(NAMESPACE ns) { PUSH_nspace (ns, namespace_stack); uncache_namespace (ns, 1); return; } /* * REMOVE A NAMESPACE FROM THE LOOK-UP LIST * * This routine removes the top namespace from the look-up namespace list. * Note that this is automatically called by pop_namespace. */ void remove_namespace() { NAMESPACE ns; POP_nspace (ns, namespace_stack); uncache_namespace (ns, 0); return; } /* * PUSH A NAMESPACE * * This routine pushes the namespace ns onto the namespace stack. */ void store_namespace(NAMESPACE ns) { NAMESPACE cns = crt_namespace; switch (TAG_nspace (ns)) { case nspace_named_tag : case nspace_unnamed_tag : case nspace_global_tag : { /* Record named namespaces */ nonblock_namespace = ns; nonclass_namespace = ns; break; } case nspace_param_tag : { /* Deal with function prototype scopes */ if (option (OPT_proto_scope)) { nonclass_namespace = ns; } break; } case nspace_block_tag : { /* A block is a non-class namespace */ nonclass_namespace = ns; break; } } COPY_nspace (nspace_parent (ns), cns); PUSH_nspace (cns, crt_nspace_stack); crt_namespace = ns; return; } /* * POP A NAMESPACE * * This routine removes a namespace from the namespace stack. */ NAMESPACE restore_namespace() { NAMESPACE ns = crt_namespace; int fb = EQ_nspace (ns, nonblock_namespace); int fc = EQ_nspace (ns, nonclass_namespace); int fa = (fb || fc); if (fa) { /* Check for enclosing namespaces */ LIST (NAMESPACE) lns = LIST_stack (crt_nspace_stack); while (fa && !IS_NULL_list (lns)) { NAMESPACE pns = DEREF_nspace (HEAD_list (lns)); switch (TAG_nspace (pns)) { case nspace_named_tag : case nspace_unnamed_tag : case nspace_global_tag : { if (fb) { /* Non-block namespace found */ nonblock_namespace = pns; fa = fc; fb = 0; } goto nonclass_namespace_lab; } case nspace_param_tag : { /* Deal with function prototype scopes */ if (!option (OPT_proto_scope)) break; goto nonclass_namespace_lab; } case nspace_block_tag : nonclass_namespace_lab : { if (fc) { /* Non-class namespace found */ nonclass_namespace = pns; fa = fb; fc = 0; } break; } } lns = TAIL_list (lns); } } POP_nspace (crt_namespace, crt_nspace_stack); return (ns); } /* * SET CURRENT NAMESPACE * * This routine makes the namespace ns into the current namespace, pushing * the previous namespace onto the stack. */ void push_namespace(NAMESPACE ns) { store_namespace (ns); add_namespace (ns); return; } /* * RESTORE PREVIOUS NAMESPACE * * This routine restores the current namespace to its previous value by * popping it from the stack. It returns the removed namespace. */ NAMESPACE pop_namespace() { NAMESPACE ns = restore_namespace (); remove_namespace (); return (ns); } /* * RECALCULATE NAMESPACES * * This routine forces the recalculation of nonblock_namespace and * nonclass_namespace by pushing and immediately popping the global * namespace. */ void update_namespace() { NAMESPACE ns = global_namespace; if (!IS_NULL_nspace (ns)) { store_namespace (ns); IGNORE restore_namespace (); } return; } /* * CREATE A NAMESPACE * * This routine creates a namespace named id of type tag. If tag * indicates a small namespace then sz will be zero. Otherwise sz gives * the size of hash table to be created. */ NAMESPACE make_namespace(IDENTIFIER id, unsigned tag, int sz) { NAMESPACE ns; NAMESPACE pns; if (!IS_NULL_id (id)) { pns = DEREF_nspace (id_parent (id)); } else { pns = crt_namespace; } if (sz == 0) { /* Small namespace */ MAKE_nspace_block_etc (tag, id, pns, ns); } else { /* Large namespace */ PTR (MEMBER) ptr; SIZE (MEMBER) psz; unsigned long i, n = (unsigned long) sz; /* Allocate namespace hash table */ psz = SCALE (SIZE_member, n); ptr = MAKE_ptr (psz); MAKE_nspace_named_etc (tag, id, pns, n, ptr, ns); /* Initialise hash table entries */ for (i = 0 ; i < n ; i++) { COPY_member (ptr, NULL_member); ptr = STEP_ptr (ptr, SIZE_member); } } return (ns); } /* * USE A NAMESPACE * * This routine creates a using directive for the namespace ns in * the namespace cns. nt gives the join of cns and ns. The routine * returns zero to indicate that ns has already been used from cns. */ int use_namespace(NAMESPACE ns, NAMESPACE cns, NAMESPACE nt) { if (!EQ_nspace (cns, ns)) { LIST (NAMESPACE) p = DEREF_list (nspace_use (cns)); LIST (NAMESPACE) r = DEREF_list (nspace_join (cns)); LIST (NAMESPACE) q = p; while (!IS_NULL_list (q)) { NAMESPACE qns = DEREF_nspace (HEAD_list (q)); if (EQ_nspace (qns, ns)) return (0); q = TAIL_list (q); } CONS_nspace (nt, r, r); COPY_list (nspace_join (cns), r); CONS_nspace (ns, p, p); COPY_list (nspace_use (cns), p); return (1); } return (0); } /* * LIST OF JOIN NAMESPACES * * During unqualified name look-up any using-directives are treated as * injecting the names from the used namespace into the join on the * used and the using namespaces. This is implemented by injecting a * dummy using-directive into the join namespace. This list is used * to keep track of all the join namespaces which have using-directives * injected in this way. */ static LIST (NAMESPACE) join_nspaces = NULL_list (NAMESPACE); /* * CLEAR LIST OF JOIN NAMESPACES * * This routine removes any dummy using-directives from the list of join * namespaces above. */ static void clear_join_nspaces() { LIST (NAMESPACE) lns = join_nspaces; while (!IS_NULL_list (lns)) { NAMESPACE ns; NAMESPACE dns; LIST (NAMESPACE) p; DESTROY_CONS_nspace (destroy, ns, lns, lns); p = DEREF_list (nspace_use (ns)); if (!IS_NULL_list (p)) { DESTROY_CONS_nspace (destroy, dns, p, p); UNUSED (dns); COPY_list (nspace_use (ns), p); } p = DEREF_list (nspace_join (ns)); if (!IS_NULL_list (p)) { DESTROY_CONS_nspace (destroy, dns, p, p); UNUSED (dns); COPY_list (nspace_join (ns), p); } } join_nspaces = NULL_list (NAMESPACE); return; } /* * BEGIN A NAMESPACE DEFINITION * * This routine begins the definition of a namespace named id (or an * anonymous namespace if anon is true). Note that there may be multiple * definitions of a namespace, the effect of the later definitions being * to add elements to the existing namespace. Only original namespace * names, and not namespace aliases, can be used in these namespace * extensions. */ void begin_namespace(IDENTIFIER id, int anon) { HASHID nm; MEMBER mem; IDENTIFIER old_id; NAMESPACE ns = NULL_nspace; NAMESPACE cns = crt_namespace; unsigned tag = nspace_named_tag; /* Find name for anonymous namespaces */ if (anon) { id = local_namespace_id; tag = nspace_unnamed_tag; } nm = DEREF_hashid (id_name (id)); /* Can only occur in namespace scope */ if (in_function_defn || in_class_defn) { report (crt_loc, ERR_dcl_nspace_scope ()); } /* Look up namespace name */ mem = search_member (cns, nm, 1); old_id = DEREF_id (member_id (mem)); if (!IS_NULL_id (old_id)) { /* Check for redeclarations */ old_id = redecl_inherit (old_id, qual_none, in_class_defn, 2); switch (TAG_id (old_id)) { case id_nspace_name_tag : { /* Previously defined namespace */ ns = DEREF_nspace (id_nspace_name_defn (old_id)); break; } case id_nspace_alias_tag : { /* Previously defined namespace alias */ IDENTIFIER ns_id; ns = DEREF_nspace (id_nspace_alias_defn (old_id)); ns_id = DEREF_id (nspace_name (ns)); if (!IS_id_nspace_name (ns_id)) { /* Alias for class namespace */ ns = NULL_nspace; goto default_lab; } report (crt_loc, ERR_dcl_nspace_def_orig (ns_id, old_id)); break; } default : default_lab : { /* Other previously defined identifier */ PTR (LOCATION) loc = id_loc (old_id); report (crt_loc, ERR_basic_odr_decl (old_id, loc)); break; } } } /* Construct the namespace if necessary */ if (IS_NULL_nspace (ns)) { DECL_SPEC ds = (dspec_defn | dspec_extern); MAKE_id_nspace_name (nm, ds, cns, decl_loc, ns, id); ns = make_namespace (id, tag, 50); COPY_nspace (id_nspace_name_defn (id), ns); set_member (mem, id); if (anon) { /* Anonymous namespaces are implicitly used */ IGNORE use_namespace (ns, cns, cns); if (do_dump) dump_using (ns, cns, &decl_loc); } } /* Adjust the current namespace */ if (do_dump) dump_declare (id, &decl_loc, 1); push_namespace (ns); cns = DEREF_nspace (id_parent (id)); COPY_nspace (nspace_parent (ns), cns); return; } /* * END A NAMESPACE DEFINITION * * This routine ends the definition of the current namespace. */ void end_namespace(int anon) { NAMESPACE ns; clear_decl_blocks (); ns = pop_namespace (); if (do_dump) { IDENTIFIER id = DEREF_id (nspace_name (ns)); dump_undefine (id, &crt_loc, 0); } UNUSED (anon); return; } /* * PROCESS A TARGET DEPENDENT DECLARATION SEQUENCE * * This routine is called in the processing of target dependent * preprocessing directives within declaration sequences. The directive * involved is given by dir, while c gives the associated condition for * '#if' and '#elif'. */ void target_decl(int dir, EXP c) { if (!IS_NULL_exp (c)) { report (crt_loc, ERR_cpp_cond_if_ti_decl (dir)); } return; } /* * BEGIN A DECLARATION BLOCK * * A declaration block is a technique for partitioning the external * declarations into subsets. These subsets have no significance within * the language, but are useful for indicating, for example, those * identifiers which form part of a certain API. This routine starts * a declaration block named id. */ void begin_decl_block(IDENTIFIER id) { NAMESPACE cns = nonblock_namespace; NAMESPACE sns = scope_namespace; HASHID nm = DEREF_hashid (id_name (id)); MEMBER mem = search_member (sns, nm, 1); id = DEREF_id (member_id (mem)); if (IS_NULL_id (id)) { /* Create dummy namespace identifier */ NAMESPACE bns = NULL_nspace; DECL_SPEC ds = (dspec_defn | dspec_extern); MAKE_id_nspace_name (nm, ds, cns, crt_loc, bns, id); bns = make_namespace (id, nspace_block_tag, 0); COPY_nspace (id_nspace_name_defn (id), bns); COPY_id (member_id (mem), id); } if (do_dump) { /* Output scope to dump file */ NAMESPACE bns = DEREF_nspace (id_nspace_name_defn (id)); dump_begin_scope (id, bns, cns, &crt_loc); } if (!IS_NULL_nspace (cns)) { /* Add to current namespace */ STACK (IDENTIFIER) pids = DEREF_stack (nspace_set (cns)); PUSH_id (id, pids); COPY_stack (nspace_set (cns), pids); } return; } /* * END A DECLARATION BLOCK * * This routine ends a declaration block named id or the current * declaration block if id is the null identifier. If there is no * current declaration block and force is true then an error is * raised. The routine returns true if a declaration block is ended. */ int end_decl_block(IDENTIFIER id, int force) { int res = 0; NAMESPACE cns = nonblock_namespace; if (!IS_NULL_nspace (cns)) { /* Add to current namespace */ STACK (IDENTIFIER) pids = DEREF_stack (nspace_set (cns)); if (IS_NULL_stack (pids)) { if (force) { /* Stack shouldn't be empty */ report (crt_loc, ERR_pragma_dblock_end ()); } } else { IDENTIFIER pid; POP_id (pid, pids); COPY_stack (nspace_set (cns), pids); if (!IS_NULL_id (id)) { /* Check block name if given */ HASHID nm = DEREF_hashid (id_name (id)); HASHID pnm = DEREF_hashid (id_name (pid)); if (!EQ_hashid (nm, pnm)) { report (crt_loc, ERR_pragma_dblock_name (pnm)); } } if (do_dump) { /* Output scope to dump file */ NAMESPACE bns = DEREF_nspace (id_nspace_name_defn (pid)); dump_end_scope (pid, bns, &crt_loc); } res = 1; } } return (res); } /* * END ALL DECLARATION BLOCKS * * This routine ends all the declaration blocks associated with the * current namespace. This is called at the end of a namespace definition * and at the end of the input file. */ void clear_decl_blocks() { while (end_decl_block (NULL_id, 0)); return; } /* * FIND A NAMESPACE * * This routine finds the namespace corresponding to the identifier id, * returning the null namespace if id does not represent a class or a * namespace. */ NAMESPACE find_namespace(IDENTIFIER id) { NAMESPACE ns = NULL_nspace; if (!IS_NULL_id (id)) { switch (TAG_id (id)) { case id_nspace_name_tag : case id_nspace_alias_tag : { /* Explicitly named namespaces */ ns = DEREF_nspace (id_nspace_name_etc_defn (id)); break; } case id_class_name_tag : case id_class_alias_tag : { /* Class namespaces */ TYPE t = DEREF_type (id_class_name_etc_defn (id)); if (IS_type_compound (t)) { CLASS_TYPE ct = DEREF_ctype (type_compound_defn (t)); complete_class (ct, 1); ns = DEREF_nspace (ctype_member (ct)); } else { CLASS_TYPE ct = find_class (id); if (!IS_NULL_ctype (ct)) { complete_class (ct, 1); ns = DEREF_nspace (ctype_member (ct)); } } break; } } } return (ns); } /* * LOOK UP A NAMESPACE NAME * * This routine looks up the identifier id as a namespace name in a * namespace alias or namespace directive. */ NAMESPACE find_nspace_id(IDENTIFIER id) { NAMESPACE ns; if (IS_id_nspace_name_etc (id)) { ns = DEREF_nspace (id_nspace_name_etc_defn (id)); } else { if (crt_templ_qualifier == 0) { HASHID nm = DEREF_hashid (id_name (id)); if (crt_id_qualifier == qual_none) { IDENTIFIER nid = find_type_id (nm, 2); if (!IS_NULL_id (nid)) id = nid; } else { NAMESPACE pns = DEREF_nspace (id_parent (id)); IDENTIFIER nid = find_qual_id (pns, nm, 0, 2); if (!IS_NULL_id (nid)) id = nid; } } ns = find_namespace (id); if (IS_NULL_nspace (ns)) { /* Invalid namespace identifier */ report (crt_loc, ERR_dcl_nspace_undef (id)); return (NULL_nspace); } } use_id (id, 0); return (ns); } /* * CONSTRUCT A NAMESPACE ALIAS * * This routine sets up id as an alias for the namespace ns. A namespace * alias may be consistently redefined any number of times. */ void alias_namespace(IDENTIFIER id, NAMESPACE ns) { MEMBER mem; IDENTIFIER old_id; NAMESPACE cns = crt_namespace; HASHID nm = DEREF_hashid (id_name (id)); DECL_SPEC ds = (dspec_defn | dspec_extern); if (IS_NULL_nspace (ns)) { /* Invalid namespace identifier */ begin_namespace (id, 0); end_namespace (0); return; } if (IS_nspace_ctype (ns)) { /* Can't have a class namespace */ report (crt_loc, ERR_dcl_nspace_alias_class (ns)); } /* Look up namespace alias name */ mem = search_member (cns, nm, 1); old_id = DEREF_id (member_id (mem)); /* Check for redeclarations */ if (!IS_NULL_id (old_id)) { old_id = redecl_inherit (old_id, qual_none, in_class_defn, 2); switch (TAG_id (old_id)) { case id_nspace_name_tag : { /* Previously defined namespace name */ ERROR err; NAMESPACE old_ns; PTR (LOCATION) loc = id_loc (old_id); old_ns = DEREF_nspace (id_nspace_name_defn (old_id)); report (crt_loc, ERR_dcl_nspace_alias_bad (old_id, loc)); if (EQ_nspace (ns, old_ns)) { /* No further action if consistent */ return; } /* Hide namespace name with namespace alias */ err = ERR_dcl_nspace_alias_redef (old_id, loc); report (crt_loc, err); break; } case id_nspace_alias_tag : { /* Previously defined namespace alias */ NAMESPACE old_ns; old_ns = DEREF_nspace (id_nspace_alias_defn (old_id)); if (!EQ_nspace (ns, old_ns)) { /* Inconsistent alias redefinition */ ERROR err; PTR (LOCATION) loc = id_loc (old_id); err = ERR_dcl_nspace_alias_redef (old_id, loc); report (crt_loc, err); COPY_nspace (id_nspace_alias_defn (old_id), ns); } return; } default : { /* Other previously defined identifier */ PTR (LOCATION) loc = id_loc (old_id); report (crt_loc, ERR_basic_odr_decl (old_id, loc)); break; } } } /* Set up the namespace alias */ MAKE_id_nspace_alias (nm, ds, cns, decl_loc, ns, id); set_member (mem, id); return; } /* * PROCESS A USING NAMESPACE DIRECTIVE * * This routine processes a using namespace directive for the namespace ns. */ void using_namespace(NAMESPACE ns) { if (!IS_NULL_nspace (ns)) { if (IS_nspace_ctype (ns)) { /* Namespace designates a class */ report (crt_loc, ERR_dcl_nspace_udir_class (ns)); } else { NAMESPACE cns = crt_namespace; NAMESPACE jns = join_namespace (ns, cns); if (IS_NULL_nspace (jns)) jns = global_namespace; if (!use_namespace (ns, cns, jns)) { /* Namespace already used */ report (crt_loc, ERR_dcl_nspace_udir_dup (ns)); } uncache_namespace (ns, 0); if (do_dump) dump_using (ns, cns, &crt_loc); } } return; } /* * ADD A NESTED NAMESPACE * * This routine adds the namespace ns to the look-up stack. If ns is * a nested namespace then the enclosing namespaces are also added. * It returns true if the stack is changed. */ int add_nested_nspace(NAMESPACE ns) { int res = 0; if (!IS_NULL_nspace (ns) && !EQ_nspace (ns, crt_namespace)) { switch (TAG_nspace (ns)) { case nspace_named_tag : case nspace_unnamed_tag : case nspace_ctype_tag : { IDENTIFIER id = DEREF_id (nspace_name (ns)); if (!IS_NULL_id (id)) { NAMESPACE pns = DEREF_nspace (id_parent (id)); IGNORE add_nested_nspace (pns); } add_namespace (ns); res = 1; break; } } } return (res); } /* * REMOVE A NESTED NAMESPACE * * This routine removes the namespace ns from the look-up stack. If * ns is a namespace class then the enclosing namespaces are also * removed. It returns true if the stack is changed. */ int remove_nested_nspace(NAMESPACE ns) { int res = 0; if (!IS_NULL_nspace (ns) && !EQ_nspace (ns, crt_namespace)) { switch (TAG_nspace (ns)) { case nspace_named_tag : case nspace_unnamed_tag : case nspace_ctype_tag : { IDENTIFIER id; remove_namespace (); id = DEREF_id (nspace_name (ns)); if (!IS_NULL_id (id)) { NAMESPACE pns = DEREF_nspace (id_parent (id)); IGNORE remove_nested_nspace (pns); } res = 1; break; } } } return (res); } /* * BEGIN THE LOOK-UP SCOPE FOR A DECLARATOR * * This routine is called immediately after the declarator id given with * identifier qualifiers idtype and qns to set the name look-up * appropriately. Thus for example, in 'int C::a = b ;', b is looked up * in the scope of C. */ void begin_declarator(IDENTIFIER id, QUALIFIER idtype, NAMESPACE qns, int scan) { NAMESPACE ns = NULL_nspace; if (idtype != qual_none) { ns = DEREF_nspace (id_parent (id)); if (!IS_NULL_nspace (ns) && !IS_nspace_ctype (ns)) { if (!IS_NULL_nspace (qns) && !EQ_nspace (qns, ns)) { /* Should have an immediate member */ report (crt_loc, ERR_lookup_qual_decl (id, qns)); ns = qns; } } if (add_nested_nspace (ns) && scan) { /* Rescan identifier if stack has changed */ RESCAN_LEXER; } } PUSH_nspace (ns, local_nspace_stack); if (crt_state_depth == 0) { id = underlying_id (id); DEREF_loc (id_loc (id), decl_loc); } else { decl_loc = crt_loc; } return; } /* * END THE LOOK-UP SCOPE FOR A DECLARATOR * * This routine is called at the end of the initialiser or definition * of the declarator id to reset the name look-up. */ void end_declarator(IDENTIFIER id, int scan) { NAMESPACE ns; POP_nspace (ns, local_nspace_stack); if (IS_NULL_nspace (ns)) { if (!IS_NULL_id (id)) { HASHID nm = DEREF_hashid (id_name (id)); IDENTIFIER cid = DEREF_id (hashid_cache (nm)); if (!EQ_id (id, cid)) { /* Look-up may have changed */ COPY_id (hashid_cache (nm), NULL_id); if (scan) { /* Rescan identifier if look-up has changed */ RESCAN_LEXER; } } } } else { if (remove_nested_nspace (ns) && scan) { /* Rescan identifier if stack has changed */ RESCAN_LEXER; } } return; } /* * SET A NAMESPACE MEMBER * * This routine sets the identifier id to be a namespace member as * indicated by mem. It also sets the look-up cache to id if appropriate. */ void set_member(MEMBER mem, IDENTIFIER id) { if (!IS_NULL_member (mem)) { HASHID nm; IDENTIFIER cid; LIST (NAMESPACE) lns; /* Check any previous definition */ IDENTIFIER pid = DEREF_id (member_id (mem)); if (!IS_NULL_id (pid)) { if (IS_id_token (pid)) { IGNORE unify_id (pid, id, 1); } else if (IS_id_token (id)) { if (unify_id (id, pid, 0)) return; } } /* Check look-up cache */ cid = NULL_id; nm = DEREF_hashid (id_name (id)); lns = LIST_stack (namespace_stack); if (!IS_NULL_list (lns)) { /* Check for current namespace */ NAMESPACE ns = DEREF_nspace (id_parent (id)); NAMESPACE pns = DEREF_nspace (HEAD_list (lns)); if (EQ_nspace (ns, pns)) { /* Check for unambiguous look-up */ lns = DEREF_list (nspace_use (ns)); if (IS_NULL_list (lns)) cid = id; } } COPY_id (hashid_cache (nm), cid); /* Set member identifier */ COPY_id (member_id (mem), id); } return; } /* * SET A NAMESPACE TYPE MEMBER * * This routine sets the type identifier id to be a namespace member * as indicated by mem. Note that in C this just sets the alt field * of mem, whereas in C++ it may also set the id field. */ void set_type_member(MEMBER mem, IDENTIFIER id) { if (!IS_NULL_member (mem)) { #if LANGUAGE_CPP IDENTIFIER mid = DEREF_id (member_id (mem)); if (IS_NULL_id (mid)) { /* No object of the same name */ set_member (mem, id); } else { /* Hidden by object of the same name */ HASHID nm = DEREF_hashid (id_name (id)); DECL_SPEC ds = DEREF_dspec (id_storage (mid)); if ((ds & dspec_inherit) && !(ds & dspec_alias)) { ds = DEREF_dspec (id_storage (id)); if (!(ds & dspec_inherit)) { /* An uninherited type overrides an inherited object */ COPY_id (member_id (mem), id); } } COPY_id (hashid_cache (nm), NULL_id); } #endif COPY_id (member_alt (mem), id); } return; } /* * CLEAR A NAMESPACE MEMBER * * This routine clears all meanings of the member nm of the namespace ns. */ void clear_member(NAMESPACE ns, HASHID nm) { MEMBER mem = search_member (ns, nm, 0); if (!IS_NULL_member (mem)) { COPY_id (member_id (mem), NULL_id); COPY_id (member_alt (mem), NULL_id); } COPY_id (hashid_cache (nm), NULL_id); return; } /* * FIND A MEMBER OF A NAMESPACE * * This routine searches the namespace ns for a member named nm. This * is returned if found. Otherwise if create is true then an empty * member is created and returned. Otherwise the null member is returned. */ MEMBER search_member(NAMESPACE ns, HASHID nm, int create) { MEMBER mem; if (IS_NULL_nspace (ns)) { /* Null namespace */ if (create) { MAKE_member_small (NULL_member, mem); } else { mem = NULL_member; } } else if (IS_nspace_block_etc (ns)) { /* Small namespaces */ MEMBER last = DEREF_member (nspace_last (ns)); /* Search through members */ mem = last; while (!IS_NULL_member (mem)) { IDENTIFIER mid = DEREF_id (member_id (mem)); if (!IS_NULL_id (mid)) { HASHID mnm = DEREF_hashid (id_name (mid)); if (EQ_hashid (nm, mnm)) return (mem); } #if LANGUAGE_C /* ... continues */ else { /* Need to also check tag namespace in C */ mid = DEREF_id (member_alt (mem)); if (!IS_NULL_id (mid)) { HASHID mnm = DEREF_hashid (id_name (mid)); if (EQ_hashid (nm, mnm)) return (mem); } } #endif mem = DEREF_member (member_next (mem)); } /* Create new member if necessary */ if (create) { MAKE_member_small (last, mem); COPY_member (nspace_last (ns), mem); } } else { /* Large namespaces */ PTR (MEMBER) ptr = DEREF_ptr (nspace_named_etc_table (ns)); unsigned long sz = DEREF_ulong (nspace_named_etc_size (ns)); unsigned long h = DEREF_ulong (hashid_hash (nm)); SIZE (MEMBER) psz = SCALE (SIZE_member, (h % sz)); /* Search through members */ ptr = STEP_ptr (ptr, psz); mem = DEREF_member (ptr); while (!IS_NULL_member (mem)) { IDENTIFIER mid = DEREF_id (member_id (mem)); if (!IS_NULL_id (mid)) { HASHID mnm = DEREF_hashid (id_name (mid)); if (EQ_hashid (nm, mnm)) return (mem); } #if LANGUAGE_C /* ... continues */ else { /* Need to also check tag namespace in C */ mid = DEREF_id (member_alt (mem)); if (!IS_NULL_id (mid)) { HASHID mnm = DEREF_hashid (id_name (mid)); if (EQ_hashid (nm, mnm)) return (mem); } } #endif mem = DEREF_member (member_large_tnext (mem)); } /* Create new member if necessary */ if (create) { MEMBER last; mem = DEREF_member (ptr); MAKE_member_large (NULL_member, mem, mem); COPY_member (ptr, mem); last = DEREF_member (nspace_last (ns)); if (IS_NULL_member (last)) { COPY_member (nspace_named_etc_first (ns), mem); } else { COPY_member (member_next (last), mem); } COPY_member (nspace_last (ns), mem); } } return (mem); } /* * UPDATE A NAMESPACE MEMBER * * This routine copies the member mem of the namespace ns to the end of * the list of all members. In block namespaces this is to force the * member to be re-examined in make_decl_stmt. In class namespaces this * is to preserve the order of the data members. */ MEMBER update_member(NAMESPACE ns, MEMBER mem) { IDENTIFIER id = DEREF_id (member_id (mem)); IDENTIFIER alt = DEREF_id (member_alt (mem)); COPY_id (member_id (mem), NULL_id); COPY_id (member_alt (mem), NULL_id); if (IS_member_small (mem)) { /* Create new small member */ MEMBER last = DEREF_member (nspace_last (ns)); MAKE_member_small (last, mem); COPY_member (nspace_last (ns), mem); } else { /* Create new large member */ if (!IS_NULL_id (id)) { HASHID nm = DEREF_hashid (id_name (id)); mem = search_member (ns, nm, 1); } else if (!IS_NULL_id (alt)) { HASHID nm = DEREF_hashid (id_name (alt)); mem = search_member (ns, nm, 1); } } COPY_id (member_id (mem), id); COPY_id (member_alt (mem), alt); return (mem); } /* * FIND A TYPE IDENTIFIER * * This routine checks whether the identifier id is a type name (if bit 0 * of type is true) or a namespace name (if bit 1 of type is true). In * C only struct, union and enum tags are allowed. */ static IDENTIFIER select_type_id(IDENTIFIER id, int type) { if (!IS_NULL_id (id)) { switch (TAG_id (id)) { case id_class_name_tag : case id_enum_name_tag : { if (type & 1) return (id); break; } #if LANGUAGE_CPP case id_class_alias_tag : case id_enum_alias_tag : case id_type_alias_tag : { if (type & 1) return (id); break; } case id_nspace_name_tag : case id_nspace_alias_tag : { if (type & 2) return (id); break; } #endif } } return (NULL_id); } /* * FIND A TYPE MEMBER * * This routine returns the type or namespace name associated with the * member mem, or the null identifier if this does not exist. The * type argument is as above. In C++ it is necessary to check both * the id and the alt fields of mem, whereas in C only the alt field * needs to be examined. */ IDENTIFIER type_member(MEMBER mem, int type) { IDENTIFIER id = NULL_id; if (!IS_NULL_member (mem)) { #if LANGUAGE_CPP id = DEREF_id (member_id (mem)); id = select_type_id (id, type); if (IS_NULL_id (id)) { id = DEREF_id (member_alt (mem)); id = select_type_id (id, type); } #else id = DEREF_id (member_alt (mem)); id = select_type_id (id, type); #endif } return (id); } /* * IS AN IDENTIFIER IN A LIST? * * This routine checks whether the identifier id is in the list of * ambiguous meanings given by pid and pids. Functions are excluded * because of the complications introduced by using-declarations and * overloading. Overload resolution will eliminate duplicate entries * in this case. */ static int already_found_id(IDENTIFIER id, IDENTIFIER pid, LIST (IDENTIFIER) pids) { if (IS_id_function_etc (id)) { /* Exclude functions */ return (0); } if (!IS_NULL_id (pid)) { if (IS_id_ambig (pid)) { /* Check ambiguous identifiers */ LIST (IDENTIFIER) qids = DEREF_list (id_ambig_ids (pid)); if (already_found_id (id, NULL_id, qids)) return (1); } else { /* Check simple identifiers */ if (!IS_id_function_etc (pid)) { id = DEREF_id (id_alias (id)); pid = DEREF_id (id_alias (pid)); if (EQ_id (id, pid)) return (1); } } } if (!IS_NULL_list (pids)) { /* Check identifier lists */ IDENTIFIER qid = DEREF_id (HEAD_list (pids)); pids = TAIL_list (pids); return (already_found_id (id, qid, pids)); } return (0); } /* * LOOK UP AN IDENTIFIER IN A NAMESPACE * * This routine looks up the identifier nm in the namespace ns. */ IDENTIFIER search_id(NAMESPACE ns, HASHID nm, int create, int type) { IDENTIFIER id; MEMBER mem = search_member (ns, nm, create); if (!IS_NULL_member (mem)) { if (type) { id = type_member (mem, type); } else { id = DEREF_id (member_id (mem)); } } else { id = NULL_id; } return (id); } /* * SEARCH A NAMESPACE FOR AN IDENTIFIER * * This routine searches the namespace ns and used namespaces for an * identifier named nm, which is returned if found. Otherwise if create * is true then a dummy identifier is created and returned. Otherwise * the null identifier is returned. qual is true for qualified look-ups. * If type is nonzero then only type and namespace names are considered. * rns gives the original value of ns for use when recursively searching * used namespaces. */ static IDENTIFIER search_nspace(NAMESPACE ns, HASHID nm, NAMESPACE rns, int qual, int create, int type) { IDENTIFIER id; LIST (NAMESPACE) uns; /* Allow for class namespaces */ if (IS_nspace_ctype (ns)) { id = search_field (ns, nm, create, type); return (id); } /* Search main namespace */ id = search_id (ns, nm, create, type); if (!IS_NULL_id (id) && qual) { /* Return found identifier */ return (id); } /* Search used namespaces */ uns = DEREF_list (nspace_use (ns)); if (!IS_NULL_list (uns)) { LIST (NAMESPACE) vns = DEREF_list (nspace_join (ns)); LIST (NAMESPACE) uns_orig = uns; LIST (NAMESPACE) vns_orig = vns; LIST (IDENTIFIER) ambig = NULL_list (IDENTIFIER); COPY_list (nspace_use (ns), NULL_list (NAMESPACE)); COPY_list (nspace_join (ns), NULL_list (NAMESPACE)); while (!IS_NULL_list (uns)) { NAMESPACE pns = DEREF_nspace (HEAD_list (uns)); NAMESPACE jns = DEREF_nspace (HEAD_list (vns)); if (qual || is_subnspace (rns, jns)) { /* Look-up identifier in used namespace */ IDENTIFIER pid; pid = search_nspace (pns, nm, rns, qual, 0, type); if (!IS_NULL_id (pid)) { /* Add found identifier to list */ if (IS_NULL_id (id)) { id = pid; } else if (!already_found_id (pid, id, ambig)) { CONS_id (pid, ambig, ambig); } } } else { /* Postpone look-up until join namespace */ if (use_namespace (pns, jns, jns)) { CONS_nspace (jns, join_nspaces, join_nspaces); } } vns = TAIL_list (vns); uns = TAIL_list (uns); } if (!IS_NULL_list (ambig)) { /* Ambiguous resolution */ DECL_SPEC ds; CONS_id (id, ambig, ambig); ds = find_ambig_dspec (ambig); MAKE_id_ambig (nm, ds, rns, crt_loc, ambig, 1, id); } COPY_list (nspace_use (ns), uns_orig); COPY_list (nspace_join (ns), vns_orig); } /* Create dummy identifier if necessary */ if (IS_NULL_id (id) && create) { MAKE_id_undef (nm, dspec_none, ns, crt_loc, id); } return (id); } /* * EXTERNAL NAME LOOK-UP * * This routine searches all the namespaces in the current namespace stack * which are contained within pns for the identifier pns. If type is * nonzero then only type and namespace names are considered. */ IDENTIFIER find_extern_id(HASHID nm, NAMESPACE pns, int type) { IDENTIFIER id = NULL_id; LIST (NAMESPACE) lns = LIST_stack (namespace_stack); while (!IS_NULL_list (lns)) { NAMESPACE ns = DEREF_nspace (HEAD_list (lns)); if (!IS_NULL_nspace (ns)) { id = search_nspace (ns, nm, ns, 0, 0, type); if (!IS_NULL_id (id)) break; if (EQ_nspace (ns, pns)) break; } lns = TAIL_list (lns); } if (!IS_NULL_list (join_nspaces)) clear_join_nspaces (); return (id); } /* * UNQUALIFIED NAME LOOK-UP * * This routine (aka who the feck is fred?) looks up the name nm in the * current scope, returning the corresponding identifier. Note that * there is always a meaning for nm even if it is the underlying dummy * identifier. */ IDENTIFIER find_id(HASHID nm) { IDENTIFIER id; if (cache_lookup) { id = DEREF_id (hashid_cache (nm)); if (IS_NULL_id (id)) { id = find_extern_id (nm, NULL_nspace, 0); if (IS_NULL_id (id)) { /* Use underlying meaning if not found */ id = DEREF_id (hashid_id (nm)); } COPY_id (hashid_cache (nm), id); } } else { id = find_extern_id (nm, NULL_nspace, 0); if (IS_NULL_id (id)) { /* Use underlying meaning if not found */ id = DEREF_id (hashid_id (nm)); } } return (id); } /* * QUALIFIED NAME LOOK-UP * * This routine (aka who the feck is fred::bloggs?) looks up the name nm * in the namespace ns. Only type names are considered when type is * nonzero. When ns is the null namespace this reduces to an unqualified * name look-up. */ IDENTIFIER find_qual_id(NAMESPACE ns, HASHID nm, int create, int type) { IDENTIFIER id; if (IS_NULL_nspace (ns)) { /* Unqualified name look-up */ if (type == 0) { id = find_id (nm); } else { id = find_type_id (nm, type); } } else { /* Qualified name look-up */ id = search_nspace (ns, nm, ns, 1, create, type); } return (id); } /* * SIMPLE TYPE NAME LOOK-UP * * This routine looks up the name nm as a type in the current scope, * returning the corresponding identifier. If there is no type named * nm then the null identifier is returned. */ IDENTIFIER find_type_id(HASHID nm, int type) { IDENTIFIER id; if (cache_lookup) { /* Check whether cached value is a type */ id = DEREF_id (hashid_cache (nm)); id = select_type_id (id, type); if (!IS_NULL_id (id)) return (id); } id = find_extern_id (nm, NULL_nspace, type); return (id); } /* * OPERATOR FUNCTION NAME LOOK-UP * * This routine is identical to find_id except that it ignores all class * namespaces. This is used when looking up operator functions in C++ * and is the default look-up rule in C. */ IDENTIFIER find_op_id(HASHID nm) { IDENTIFIER id; LIST (NAMESPACE) lns; int cache = cache_lookup; if (cache) { /* Check cached look-up */ id = DEREF_id (hashid_cache (nm)); if (!IS_NULL_id (id)) { NAMESPACE ns = DEREF_nspace (id_parent (id)); if (IS_NULL_nspace (ns) || !IS_nspace_ctype (ns)) { return (id); } cache = 0; } } /* Scan through namespace stack */ lns = LIST_stack (namespace_stack); while (!IS_NULL_list (lns)) { NAMESPACE ns = DEREF_nspace (HEAD_list (lns)); if (!IS_NULL_nspace (ns) && !IS_nspace_ctype (ns)) { id = search_nspace (ns, nm, ns, 0, 0, 0); if (!IS_NULL_id (id)) { if (!IS_NULL_list (join_nspaces)) clear_join_nspaces (); if (cache) COPY_id (hashid_cache (nm), id); return (id); } } else { cache = 0; } lns = TAIL_list (lns); } if (!IS_NULL_list (join_nspaces)) clear_join_nspaces (); id = DEREF_id (hashid_id (nm)); if (cache) COPY_id (hashid_cache (nm), id); return (id); } /* * FINAL NAME LOOK-UP CHECK * * This routine gives a final check in the name look-up. In most cases * id will already be a valid identifier for the namespace ns - the couple * of exceptions - undeclared members and non-simple identifier names * are handled by this routine. */ IDENTIFIER check_id(NAMESPACE ns, IDENTIFIER id, int templ) { if (!IS_NULL_id (id)) { unsigned tag = TAG_id (id); if (tag == id_dummy_tag) { /* Re-scan dummy identifiers */ HASHID nm = DEREF_hashid (id_name (id)); id = find_qual_id (ns, nm, 1, 0); tag = TAG_id (id); } if (tag == id_token_tag && crt_id_qualifier != qual_none) { /* Can't qualify token names */ report (crt_loc, ERR_token_qual (id)); } UNUSED (templ); } return (id); } /* * REMOVE AN ELEMENT OF A SET OF OVERLOADED FUNCTIONS * * This routine removes the function id from the set of overloaded * functions fid. */ static IDENTIFIER remove_func(IDENTIFIER fid, IDENTIFIER id) { if (!IS_NULL_id (fid)) { IDENTIFIER pid = DEREF_id (id_function_etc_over (fid)); if (EQ_id (fid, id)) { fid = pid; COPY_id (id_function_etc_over (id), NULL_id); } else { pid = remove_func (pid, id); COPY_id (id_function_etc_over (fid), pid); } } return (fid); } /* * REMOVE AN IDENTIFIER FROM A NAMESPACE * * This routine removes the identifier id its parent namespace. */ void remove_id(IDENTIFIER id) { HASHID nm = DEREF_hashid (id_name (id)); NAMESPACE ns = DEREF_nspace (id_parent (id)); MEMBER mem = search_member (ns, nm, 0); if (!IS_NULL_member (mem)) { IDENTIFIER mid = DEREF_id (member_id (mem)); IDENTIFIER tid = DEREF_id (member_alt (mem)); if (IS_id_function_etc (id)) { mid = remove_func (mid, id); } else { if (EQ_id (id, mid)) mid = NULL_id; if (EQ_id (id, tid)) tid = NULL_id; } if (IS_NULL_id (mid)) mid = tid; COPY_id (member_id (mem), mid); COPY_id (member_alt (mem), tid); COPY_id (hashid_cache (nm), NULL_id); IGNORE check_identifier (id, ns, NULL_exp, ANON_NONE, 1); } return; } /* * DOES AN IDENTIFIER HAVE AN EXTERNAL LINKAGE NAME? * * This routine checks whether the identifier id has an external linkage * name. It cannot be a member of a block, an unnamed class or an * anonymous namespace. */ int has_linkage(IDENTIFIER id) { while (!IS_NULL_id (id)) { HASHID nm = DEREF_hashid (id_name (id)); NAMESPACE ns = DEREF_nspace (id_parent (id)); DECL_SPEC ds = DEREF_dspec (id_storage (id)); if (IS_hashid_anon (nm)) return (0); if (!(ds & dspec_extern)) return (0); if ((ds & dspec_c) && !anon_c_linkage) return (1); if (IS_NULL_nspace (ns)) return (0); switch (TAG_nspace (ns)) { case nspace_named_tag : break; case nspace_ctype_tag : break; case nspace_global_tag : return (1); default : return (0); } id = DEREF_id (nspace_name (ns)); } return (0); } /* * CHECK HIDING OF LOCAL VARIABLES * * This routine is used to report on variables, parameters and functions * hidden by id. Note that only the first instance is reported, and that * the hiding of one declaration by a subsequent incompatible redeclaration * is excluded. */ void check_hiding(IDENTIFIER id) { if (crt_id_qualifier == qual_none) { /* Check through all look-up namespaces */ HASHID nm = DEREF_hashid (id_name (id)); LIST (NAMESPACE) lns = LIST_stack (namespace_stack); while (!IS_NULL_list (lns)) { NAMESPACE ns = DEREF_nspace (HEAD_list (lns)); if (!IS_NULL_nspace (ns)) { IDENTIFIER mid = search_nspace (ns, nm, ns, 0, 0, 0); if (!IS_NULL_id (mid) && !EQ_id (mid, id)) { ERROR err = NULL_err; switch (TAG_id (mid)) { case id_variable_tag : case id_parameter_tag : case id_function_tag : { /* Report hiding of these objects */ PTR (LOCATION) mloc = id_loc (mid); err = ERR_basic_scope_hide (nm, mloc); break; } case id_stat_member_tag : case id_mem_func_tag : case id_stat_mem_func_tag : case id_member_tag : { /* Report hiding of members */ err = ERR_basic_scope_hide_mem (nm, mid); break; } } if (!IS_NULL_err (err)) { /* Print error */ LOCATION loc; DEREF_loc (id_loc (id), loc); report (crt_loc, err); } break; } } lns = TAIL_list (lns); } if (!IS_NULL_list (join_nspaces)) clear_join_nspaces (); } return; }