/*
* Copyright (c) 2002, The Tendra Project <http://www.ten15.org/>
* 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 ("<local>");
HASHID nm = lookup_name (s, hash (s), 1, lex_identifier);
local_namespace_id = DEREF_id (hashid_id (nm));
global_namespace = make_global_nspace ("<global>", 50);
#if LANGUAGE_CPP
c_namespace = make_global_nspace ("<c>", 50);
#endif
token_namespace = make_global_nspace ("<token>", 50);
scope_namespace = make_global_nspace ("<scope>", 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;
}
syntax highlighted by Code2HTML, v. 0.9.1