/*
 * 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
 *    
 *    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/access.c,v 1.6 2004/08/14 15:15:35 bp Exp $
 */


#include "config.h"
#include "producer.h"
#include "c_types.h"
#include "ctype_ops.h"
#include "graph_ops.h"
#include "id_ops.h"
#include "nspace_ops.h"
#include "type_ops.h"
#include "error.h"
#include "catalog.h"
#include "access.h"
#include "chktype.h"
#include "class.h"
#include "derive.h"
#include "function.h"
#include "instance.h"
#include "namespace.h"
#include "predict.h"
#include "redeclare.h"
#include "token.h"


/*
 *    ACCESS CHECKING FLAGS
 *
 *    The flag do_access_checks may be set to false to suppress access
 *    checking.
 */

int do_access_checks = 1;


/*
 *    CURRENT CLASS MEMBER ACCESS
 *
 *    The variable crt_access is used to hold the current access specifier
 *    during a class definition.  prev_access is used to check for dubious
 *    base class access specifiers.
 */

DECL_SPEC crt_access = dspec_public;
DECL_SPEC prev_access = dspec_public;


/*
 *    FIND THE COMPOSITE OF TWO ACCESSES
 *
 *    This routine finds the composite of the access specifiers a and b,
 *    i.e. if Y is a base class of X of access a, and Z is a base class of
 *    Y of access b, then the result is the access of Z as a sub-class of X.
 *    Due to the fact that:
 *
 *	    dspec_public < dspec_protected < dspec_private
 *
 *    the composite is just the maximum of the access components of a and b.
 */

DECL_SPEC
join_access(DECL_SPEC a, DECL_SPEC b)
{
    DECL_SPEC p = (a & dspec_access);
    DECL_SPEC q = (b & dspec_access);
    if (p >= q) return (p);
    return (q);
}


/*
 *    ADJUST THE ACCESS FOR AN IDENTIFIER
 *
 *    This routine adjusts the access to the identifier id to the access
 *    level acc.  expl is true for explicit using and access declarations
 *    and false for simple redeclarations.
 */

void
adjust_access(IDENTIFIER id, DECL_SPEC acc, int expl)
{
    DECL_SPEC ds = DEREF_dspec (id_storage (id));
    DECL_SPEC pacc = (ds & dspec_access);
    if (pacc && acc != pacc) {
		if (!expl) {
			/* Access changed by redeclaration */
			PTR (LOCATION) loc = id_loc (id);
			report (crt_loc, ERR_class_access_spec_change (id, loc));
		}
		if (acc > pacc) {
			if (expl) {
				/* Access reduced by using declaration */
				PTR (LOCATION) loc = id_loc (id);
				report (crt_loc, ERR_dcl_nspace_udecl_acc (id, loc));
			}
			acc = pacc;
		}
		ds = ((ds & ~dspec_access) | acc);
		COPY_dspec (id_storage (id), ds);
    }
    if (IS_id_function_etc (id) && expl) {
		/* Deal with overloaded functions */
		id = DEREF_id (id_function_etc_over (id));
		if (!IS_NULL_id (id)) adjust_access (id, acc, expl);
    }
    return;
}


/*
 *    ACCESS DECLARATION FLAG
 *
 *    This flag is set to true by access_decl.
 */

int have_access_decl = 0;


/*
 *    MAKE AN ACCESS DECLARATION
 *
 *    This routine adjusts the access of the member id of the current class.
 *    This is equivalent to a using declaration for id.
 */

IDENTIFIER
access_decl(IDENTIFIER id)
{
    report (crt_loc, ERR_class_access_dcl_using (id));
    have_access_decl = 1;
    id = using_identifier (id);
    return (id);
}


/*
 *    MAKE A FRIENDLY FUNCTION
 *
 *    This routine makes the function id into a friend of the class cs.
 *    The effect of this is to add cs to id's chums list and id to cs's
 *    pals list.
 */

void
friend_function(CLASS_TYPE cs, IDENTIFIER id, int expl)
{
    if (!IS_NULL_ctype (cs)) {
		IDENTIFIER cid;
		LIST (IDENTIFIER) pl;
		LIST (CLASS_TYPE) fr, fs;
		
		/* Make cs a pal of id */
		fr = DEREF_list (id_function_etc_chums (id));
		fs = fr;
		while (!IS_NULL_list (fs)) {
			CLASS_TYPE ct = DEREF_ctype (HEAD_list (fs));
			if (eq_ctype (ct, cs)) {
				/* id is already a friend of ns */
				if (expl) {
					ERROR err = ERR_class_friend_dup_func (id, cs);
					report (crt_loc, err);
				}
				return;
			}
			fs = TAIL_list (fs);
		}
		CONS_ctype (cs, fr, fr);
		COPY_list (id_function_etc_chums (id), fr);
		
		/* Make id a pal of cs */
		pl = DEREF_list (ctype_pals (cs));
		CONS_id (id, pl, pl);
		COPY_list (ctype_pals (cs), pl);
		
		/* Apply access checks immediately */
		cid = DEREF_id (ctype_name (cs));
		immediate_access (cid, id);
    }
    return;
}


/*
 *    MAKE A FRIENDLY CLASS
 *
 *    This routine makes the class cid into a friend of the class cs.  The
 *    effect of this is to add cs to cid's chums list and cid to cs's pals
 *    list.
 */

void
friend_class(CLASS_TYPE cs, IDENTIFIER cid, int expl)
{
    if (!IS_NULL_ctype (cs)) {
		ERROR err;
		TYPE t = DEREF_type (id_class_name_etc_defn (cid));
		while (IS_type_templ (t)) {
			t = DEREF_type (type_templ_defn (t));
		}
		if (IS_type_compound (t)) {
			CLASS_TYPE ct = DEREF_ctype (type_compound_defn (t));
			if (eq_ctype (cs, ct)) {
				/* Any class is a friend of itself */
				if (expl) {
					err = ERR_class_friend_dup_class (ct, cs);
					report (crt_loc, err);
				}
			} else {
				IDENTIFIER sid;
				LIST (IDENTIFIER) pl;
				
				/* Make cs a chum of ct */
				LIST (CLASS_TYPE) fr = DEREF_list (ctype_chums (ct));
				LIST (CLASS_TYPE) fs = fr;
				while (!IS_NULL_list (fs)) {
					CLASS_TYPE cr = DEREF_ctype (HEAD_list (fs));
					if (eq_ctype (cr, cs)) {
						/* ct is already a friend of cs */
						if (expl) {
							err = ERR_class_friend_dup_class (ct, cs);
							report (crt_loc, err);
						}
						return;
					}
					fs = TAIL_list (fs);
				}
				CONS_ctype (cs, fr, fr);
				COPY_list (ctype_chums (ct), fr);
				
				/* Make ct a pal of cs */
				pl = DEREF_list (ctype_pals (cs));
				CONS_id (cid, pl, pl);
				COPY_list (ctype_pals (cs), pl);
				
				/* Apply access checks immediately */
				sid = DEREF_id (ctype_name (cs));
				immediate_access (sid, cid);
			}
		}
    }
    return;
}


/*
 *    LISTS OF PENDING ACCESS CHECKS
 *
 *    Access control checking cannot be performed immediately because, for
 *    example, it is not known until the end of a declaration whether that
 *    declaration represents a friend function.  The list crt_access_list
 *    is used to store any pending access checks.
 */

ACCESS_LIST crt_access_list = {
    NULL_list (IDENTIFIER), NULL_list (GRAPH),
    NULL_list (int), 0, 0
};


/*
 *    FIND ACCESS LEVEL
 *
 *    This routine finds the access level for the members of the class
 *    namespace ns by the identifier pid (or a base class conversion from
 *    ns if base is true).  It returns the highest access level greater
 *    than acc which can be accessed.  It also returns a secondary access
 *    giving the access to base class members, plus markers for whether
 *    the access is via the actual class, a derived class or a friend.
 */

static DECL_SPEC
find_access(IDENTIFIER *pid, NAMESPACE ns, DECL_SPEC acc, int base)
{
    NAMESPACE cns;
    CLASS_TYPE ct;
    TYPE t = NULL_type;
    IDENTIFIER id = *pid;
	
    /* Find the namespace corresponding to id */
    DECL_SPEC ok = (dspec_public | dspec_public2);
    if (IS_NULL_id (id)) return (ok);
    if (IS_id_class_name (id)) {
		cns = find_namespace (id);
    } else {
		cns = DEREF_nspace (id_parent (id));
    }
    if (IS_NULL_nspace (cns)) return (ok);
	
    /* Map block identifiers to the corresponding function */
    if (IS_nspace_block (cns)) {
		id = DEREF_id (nspace_name (cns));
		cns = DEREF_nspace (id_parent (id));
		*pid = id;
    }
	
    /* Allow for equal namespaces */
    if (base) {
		ok = (dspec_private | dspec_public2);
    } else {
		ok = (dspec_private | dspec_protected2);
    }
    if (EQ_nspace (cns, ns)) {
		ok |= dspec_defn;
		return (ok);
    }
    ct = namespace_class (ns);
    ct = expand_ctype (ct, 2, &t);
    complete_class (ct, 1);
	
    /* Check access for classes */
    if (IS_nspace_ctype (cns)) {
		LIST (CLASS_TYPE) fr;
		CLASS_TYPE cs = namespace_class (cns);
		if (eq_ctype (cs, ct)) {
			/* Same class */
			ok |= dspec_defn;
			return (ok);
		}
		fr = DEREF_list (ctype_chums (cs));
		while (!IS_NULL_list (fr)) {
			CLASS_TYPE cr = DEREF_ctype (HEAD_list (fr));
			if (eq_ctype (cr, ct)) {
				/* Friend class */
				ok |= (dspec_defn | dspec_friend);
				return (ok);
			}
			fr = TAIL_list (fr);
		}
    }
	
    /* Check for friend functions */
    if (IS_id_function_etc (id)) {
		LIST (CLASS_TYPE) fr;
		fr = DEREF_list (id_function_etc_chums (id));
		while (!IS_NULL_list (fr)) {
			CLASS_TYPE cr = DEREF_ctype (HEAD_list (fr));
			if (eq_ctype (cr, ct)) {
				/* Friend function */
				ok |= (dspec_defn | dspec_friend);
				return (ok);
			}
			fr = TAIL_list (fr);
		}
    }
	
    /* End here for private members */
    if (acc == dspec_private || base) {
		ok = (dspec_public | dspec_public2);
		return (ok);
    }
	
    /* Check access for derived classes */
    ok = (dspec_protected | dspec_protected2);
    if (IS_nspace_ctype (cns)) {
		LIST (CLASS_TYPE) fr;
		CLASS_TYPE cs = namespace_class (cns);
		GRAPH gr = find_base_class (cs, ct, 0);
		if (!IS_NULL_graph (gr)) {
			/* Derived class */
			ok |= dspec_inherit;
			return (ok);
		}
		fr = DEREF_list (ctype_chums (cs));
		while (!IS_NULL_list (fr)) {
			CLASS_TYPE cr = DEREF_ctype (HEAD_list (fr));
			gr = find_base_class (cr, ct, 0);
			if (!IS_NULL_graph (gr)) {
				/* Friend class of derived class */
				ok |= (dspec_inherit | dspec_friend);
				return (ok);
			}
			fr = TAIL_list (fr);
		}
    }
	
    /* Check for derived friend functions */
    if (IS_id_function_etc (id)) {
		LIST (CLASS_TYPE) fr;
		fr = DEREF_list (id_function_etc_chums (id));
		while (!IS_NULL_list (fr)) {
			CLASS_TYPE cr = DEREF_ctype (HEAD_list (fr));
			GRAPH gr = find_base_class (cr, ct, 0);
			if (!IS_NULL_graph (gr)) {
				/* Friend function of derived class */
				ok |= (dspec_inherit | dspec_friend);
				return (ok);
			}
			fr = TAIL_list (fr);
		}
    }
	
    /* Default access */
    ok = (dspec_public | dspec_public2);
    return (ok);
}


/*
 *    COMPARE ACCESS LEVELS
 *
 *    This routine checks whether the access level ok returned by find_access
 *    is sufficient to access a member of type tag with declaration specifiers
 *    ds.  It returns true if the access is an error.
 */

static int
compare_access(DECL_SPEC ok, DECL_SPEC ds, unsigned tag, int mem)
{
    int ret = 0;
    DECL_SPEC acc = (ds & dspec_access);
    DECL_SPEC level = (ok & dspec_access);
    if (level < acc) {
		/* Straightforward case */
		ret = 1;
    } else {
		/* Deal with inheritance */
		if (ds & dspec_inherit) {
			DECL_SPEC acc2 = (ds & dspec_access2);
			level = (ok & dspec_access2);
			if (level < acc2) ret = 1;
		}
    }
    if (!ret && !mem) {
		if (tag == id_member_tag || tag == id_mem_func_tag) {
			/* Check for access through derived class */
			if (ok & dspec_inherit) ret = 1;
		}
    }
    return (ret);
}


/*
 *    CAN AN INHERITED MEMBER BE ACCESSED?
 *
 *    This routine checks whether the inherited class member pid can be
 *    accessed by the identifier id.  If so it returns true.  The direct
 *    cases, such as a member of a derived class accessing a member of a
 *    base class are handled by the main part of do_member_access, this
 *    routine deals with the indirect cases such as friends of classes
 *    intermediate between the base class and the derived class.
 */

static int
inherit_access(IDENTIFIER id, IDENTIFIER pid, int mem)
{
    /* Find inheritance base class */
    unsigned tag = TAG_id (pid);
    IDENTIFIER bid = DEREF_id (id_alias (pid));
    DECL_SPEC acc = DEREF_dspec (id_storage (bid));
    NAMESPACE pns = DEREF_nspace (id_parent (pid));
    NAMESPACE bns = DEREF_nspace (id_parent (bid));
    CLASS_TYPE ct = namespace_class (pns);
    CLASS_TYPE cs = namespace_class (bns);
    GRAPH gr = find_base_class (ct, cs, 0);
	
    /* Scan through all base classes of ct containing bid */
    while (!IS_NULL_graph (gr)) {
		GRAPH gs = gr;
		while (!IS_NULL_graph (gs)) {
			cs = DEREF_ctype (graph_head (gs));
			if (!eq_ctype (cs, ct)) {
				DECL_SPEC ds, ok;
				ds = DEREF_dspec (graph_access (gr));
				ds = join_access (acc, ds);
				bns = DEREF_nspace (ctype_member (cs));
				ok = find_access (&id, bns, ds, 2);
				if (ok & dspec_defn) {
					/* id is a member or friend of cs */
					if (!compare_access (ok, ds, tag, mem)) {
						/* Can access bid */
						return (1);
					}
				}
			}
			gs = DEREF_graph (graph_up (gs));
		}
		gr = DEREF_graph (graph_equal (gr));
    }
    return (0);
}


/*
 *    CHECK A MEMBER ACCESS
 *
 *    This routine checks the access of the class member pid by the
 *    identifier id.  It prints an error and returns true if the access
 *    is illegal.
 */

static int
do_member_access(IDENTIFIER id, IDENTIFIER pid, int mem)
{
    int ret = 0;
    NAMESPACE pns = DEREF_nspace (id_parent (pid));
    DECL_SPEC ds = DEREF_dspec (id_storage (pid));
    DECL_SPEC acc = (ds & dspec_access);
    DECL_SPEC ok = find_access (&id, pns, acc, 0);
    if (compare_access (ok, ds, TAG_id (pid), mem)) {
		if ((ds & dspec_inherit) && inherit_access (id, pid, mem)) {
			/* Can access through inheritance */
			/* EMPTY */
		} else {
			/* Report access error */
			ERROR err;
			if (IS_NULL_id (id)) {
				err = ERR_class_access_spec_none (pid, acc);
			} else {
				err = ERR_class_access_spec_id (pid, acc, id);
			}
			report (crt_loc, err);
			ret = 1;
		}
    }
    return (ret);
}


/*
 *    CHECK A BASE CLASS ACCESS
 *
 *    This routine checks the access of the base class gr by the
 *    identifier id.  It prints an error and returns true if the access
 *    is illegal.
 */

static int
do_base_access(IDENTIFIER id, GRAPH gr)
{
    int ret = 0;
    GRAPH gt = DEREF_graph (graph_top (gr));
    CLASS_TYPE ct = DEREF_ctype (graph_head (gt));
    NAMESPACE pns = DEREF_nspace (ctype_member (ct));
    DECL_SPEC ds = DEREF_dspec (graph_access (gr));
    DECL_SPEC acc = (ds & dspec_access);
    DECL_SPEC ok = find_access (&id, pns, acc, 1);
    if (compare_access (ok, ds, null_tag, 0)) {
		/* Report access error */
		ERROR err;
		CLASS_TYPE cs = DEREF_ctype (graph_head (gr));
		if (IS_NULL_id (id)) {
			err = ERR_class_access_base_none (cs, ct, acc);
		} else {
			err = ERR_class_access_base_id (cs, ct, acc, id);
		}
		err = concat_error (err, ERR_conv_ptr_access ());
		report (crt_loc, err);
		ret = 1;
    }
    return (ret);
}


/*
 *    CLEAR A LIST OF PENDING IDENTIFIER ACCESS CHECKS
 *
 *    This routine clears the list of pending access checks given by p in
 *    the context given by the identifier id.  It returns true if any
 *    results in an error.
 */

static int
clear_id_access(IDENTIFIER id, LIST (IDENTIFIER) p, LIST (int) r)
{
    int ret = 0;
    if (!IS_NULL_list (p)) {
		int mem = DEREF_int (HEAD_list (r));
		IDENTIFIER pid = DEREF_id (HEAD_list (p));
		ret = clear_id_access (id, TAIL_list (p), TAIL_list (r));
		if (!IS_NULL_id (pid) && do_member_access (id, pid, mem)) {
			COPY_id (HEAD_list (p), NULL_id);
			ret = 1;
		}
    }
    return (ret);
}


/*
 *    CLEAR A LIST OF PENDING BASE CLASS ACCESS CHECKS
 *
 *    This routine clears the list of pending base class access checks given
 *    by p in the context given by the identifier id.  It returns true if
 *    any results in an error.
 */

static int
clear_base_access(IDENTIFIER id, LIST (GRAPH) p)
{
    int ret = 0;
    if (!IS_NULL_list (p)) {
		GRAPH gr = DEREF_graph (HEAD_list (p));
		ret = clear_base_access (id, TAIL_list (p));
		if (!IS_NULL_graph (gr) && do_base_access (id, gr)) {
			COPY_graph (HEAD_list (p), NULL_graph);
			ret = 1;
		}
    }
    return (ret);
}


/*
 *    CLEAR A LIST OF PENDING ACCESS CHECKS
 *
 *    This routine clears the list of pending access checks given by acc in
 *    the context given by the identifier id.  It returns true if any
 *    results in an error.
 */

int
clear_access(IDENTIFIER id, ACCESS_LIST *acc)
{
    int ret = 0;
    if (acc->pending) {
		LIST (IDENTIFIER) p = acc->ids;
		LIST (GRAPH) q = acc->bases;
		LIST (int) r = acc->info;
		if (!IS_NULL_list (p) && clear_id_access (id, p, r)) ret = 1;
		if (!IS_NULL_list (q) && clear_base_access (id, q)) ret = 1;
    }
    return (ret);
}


/*
 *    CLEAR THE LIST OF CURRENT ACCESSES
 *
 *    This routine clears all outstanding accesses in the scope given by id.
 *    It returns true if any results in an error.
 */

int
report_access(IDENTIFIER id)
{
    ACCESS_LIST *acc = &crt_access_list;
    int ret = clear_access (id, acc);
    free_access (acc);
    return (ret);
}


/*
 *    FREE AN ACCESS LIST
 *
 *    This routine frees the list of accesses given by acc.
 */

void
free_access(ACCESS_LIST *acc)
{
    LIST (IDENTIFIER) p = acc->ids;
    LIST (GRAPH) q = acc->bases;
    LIST (int) r = acc->info;
    if (!IS_NULL_list (p)) {
		DESTROY_list (p, SIZE_id);
		acc->ids = NULL_list (IDENTIFIER);
    }
    if (!IS_NULL_list (q)) {
		DESTROY_list (q, SIZE_graph);
		acc->bases = NULL_list (GRAPH);
    }
    if (!IS_NULL_list (r)) {
		DESTROY_list (r, SIZE_int);
		acc->info = NULL_list (int);
    }
    acc->pending = 0;
    acc->inherit = 0;
    return;
}


/*
 *    SAVE AN ACCESS LIST
 *
 *    This routine saves the current access list into acc and clears the
 *    list.
 */

void
save_access(ACCESS_LIST *acc)
{
    ACCESS_LIST *crt = &crt_access_list;
    acc->ids = crt->ids;
    acc->bases = crt->bases;
    acc->info = crt->info;
    acc->pending = crt->pending;
    acc->inherit = crt->inherit;
    crt->ids = NULL_list (IDENTIFIER);
    crt->bases = NULL_list (GRAPH);
    crt->info = NULL_list (int);
    crt->pending = 0;
    crt->inherit = 0;
    return;
}


/*
 *    RESTORE AN ACCESS LIST
 *
 *    This routine clears the current access list in the scope given by
 *    id and resets the current access list the values stored in acc.
 */

int
restore_access(IDENTIFIER id, ACCESS_LIST *acc)
{
    int ret = report_access (id);
    ACCESS_LIST *crt = &crt_access_list;
    crt->ids = acc->ids;
    crt->bases = acc->bases;
    crt->info = acc->info;
    crt->pending = acc->pending;
    crt->inherit = acc->inherit;
    return (ret);
}


/*
 *    CHECK THE ACCESS TO AN IDENTIFIER
 *
 *    This routine adds the identifier id with access acc to the list of
 *    pending access checks.  acc will always be dspec_public, dspec_protected
 *    or dspec_private.
 */

void
check_access(IDENTIFIER id, DECL_SPEC acc)
{
    if (acc == dspec_public) return;
    if (do_access_checks) {
		NAMESPACE ns = DEREF_nspace (id_parent (id));
		if (IS_nspace_ctype (ns)) {
			ACCESS_LIST *crt = &crt_access_list;
			if (in_function_defn && !in_declaration) {
				/* Calculate access immediately */
				IGNORE do_member_access (crt_func_id, id, crt->inherit);
			} else {
				/* Add to pending list */
				CONS_id (id, crt->ids, crt->ids);
				CONS_int (crt->inherit, crt->info, crt->info);
				crt->pending = 1;
			}
		} else {
			DECL_SPEC ds = DEREF_dspec (id_storage (id));
			if (ds & dspec_auto) {
				/* Used to mark for-init variables */
				report (crt_loc, ERR_stmt_for_init (id));
				ds &= ~dspec_access;
				COPY_dspec (id_storage (id), ds);
			}
		}
    }
    return;
}


/*
 *    CHECK THE ACCESS TO A BASE CLASS
 *
 *    This routine adds the base class graph gr to the list of pending
 *    base access checks.
 */

void
check_base_access(GRAPH gr)
{
    DECL_SPEC ds = DEREF_dspec (graph_access (gr));
    DECL_SPEC acc = (ds & dspec_access);
    DECL_SPEC acc2 = (ds & dspec_access2);
    if (acc == dspec_public) return;
	
    if (do_access_checks) {
		/* Find best access to a virtual base */
		GRAPH gs = DEREF_graph (graph_equal (gr));
		while (!IS_NULL_graph (gs)) {
			DECL_SPEC pds = DEREF_dspec (graph_access (gs));
			DECL_SPEC pacc = (pds & dspec_access);
			DECL_SPEC pacc2 = (pds & dspec_access2);
			if (pacc == dspec_public) return;
			if (pacc < acc || (pacc == acc && pacc2 < acc2)) {
				acc = pacc;
				acc2 = pacc2;
				gr = gs;
			}
			gs = DEREF_graph (graph_equal (gs));
		}
		
		/* Check access control */
		if (in_function_defn && !in_declaration) {
			/* Calculate access immediately */
			IGNORE do_base_access (crt_func_id, gr);
		} else {
			/* Add to pending list */
			ACCESS_LIST *crt = &crt_access_list;
			CONS_graph (gr, crt->bases, crt->bases);
			crt->pending = 1;
		}
    }
    return;
}


/*
 *    IMMEDIATELY CHECK THE ACCESS TO AN IDENTIFIER
 *
 *    This routine applies an immediate access check to the identifier id
 *    by cid.
 */

void
immediate_access(IDENTIFIER cid, IDENTIFIER id)
{
    DECL_SPEC acc = DEREF_dspec (id_storage (id));
    acc &= dspec_access;
    if (acc == dspec_none || acc == dspec_public) return;
    if (do_access_checks) {
		NAMESPACE ns = DEREF_nspace (id_parent (id));
		if (IS_nspace_ctype (ns)) {
			int mem = crt_access_list.inherit;
			IGNORE do_member_access (cid, id, mem);
		}
    }
    return;
}
