Subversion Repositories tendra.SVN

Rev

Rev 5 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * Copyright (c) 2002-2006 The TenDRA Project <http://www.tendra.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,
 *    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.
 * 3. Neither the name of The TenDRA Project nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific, prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``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 COPYRIGHT HOLDERS OR
 * CONTRIBUTORS 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.
 *
 * $Id$
 */
/*
                 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.
*/


#include "config.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;
}