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, 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.
*/


#include "config.h"
#include "c_types.h"
#include "ctype_ops.h"
#include "exp_ops.h"
#include "id_ops.h"
#include "hashid_ops.h"
#include "member_ops.h"
#include "nspace_ops.h"
#include "tok_ops.h"
#include "type_ops.h"
#include "error.h"
#include "catalog.h"
#include "access.h"
#include "basetype.h"
#include "class.h"
#include "constant.h"
#include "construct.h"
#include "convert.h"
#include "declare.h"
#include "derive.h"
#include "dump.h"
#include "exception.h"
#include "expression.h"
#include "file.h"
#include "function.h"
#include "hash.h"
#include "identifier.h"
#include "initialise.h"
#include "instance.h"
#include "namespace.h"
#include "option.h"
#include "parse.h"
#include "predict.h"
#include "redeclare.h"
#include "syntax.h"
#include "template.h"
#include "tok.h"
#include "token.h"


/*
    USAGE SUPPRESSION FLAG

    This flag may be set to true to indicate that the parser is in a sizeof
    expression or similar so that any usages should not be included.
*/

int suppress_usage = 0;


/*
    MARK AN IDENTIFIER AS BEING USED

    This routine marks the identifier id as having been used and checks
    any access controls.
*/

void
use_id(IDENTIFIER id, int suppress)
{
        DECL_SPEC ds = DEREF_dspec(id_storage(id));
        DECL_SPEC acc = (ds & dspec_access);
        if (acc)check_access(id, acc);

        /* Check usage */
        if (ds & dspec_main) {
                HASHID nm = DEREF_hashid(id_name(id));
                switch (TAG_hashid(nm)) {
                case hashid_name_tag:
                        /* Can't take the address of 'main' */
                        report(crt_loc, ERR_basic_start_main_addr(id));
                        break;
                case hashid_constr_tag:
                        /* Can't take the address of a constructor */
                        report(crt_loc, ERR_class_ctor_addr(id));
                        break;
                case hashid_destr_tag:
                        /* Can't take the address of a destructor */
                        report(crt_loc, ERR_class_dtor_addr(id));
                        break;
                }
        }

        /* Mark use */
        if (!(ds & dspec_used)) {
                if (!suppress) {
                        ds |= dspec_used;
                        COPY_dspec(id_storage(id), ds);
                }
                if (ds & (dspec_inherit | dspec_alias | dspec_extern)) {
                        /* Check for inheritance */
                        IDENTIFIER uid = DEREF_id(id_alias(id));
                        if (!EQ_id(uid, id)) {
                                ds = DEREF_dspec(id_storage(uid));
                                if (ds & dspec_used) {
                                        if (do_usage) {
                                                dump_use(id, &crt_loc, 1);
                                        }
                                        return;
                                }
                                if (!suppress) {
                                        ds |= dspec_used;
                                        COPY_dspec(id_storage(uid), ds);
                                }
                        }
                }
                if (!(ds & dspec_defn) && (ds & dspec_instance)) {
                        /* Define template instance */
                        if (!suppress) {
                                define_template(id, 0);
                        }
                }
        }
        if (do_usage) {
                dump_use(id, &crt_loc, 1);
        }
        return;
}


/*
    MARK A FUNCTION IDENTIFIER AS BEING USED

    This routine marks the function identifier id as having been called
    and checks any access controls.  In addition certain checks are applied
    to the first call of an identifier.  expl is true for explicit calls.
*/

void
use_func_id(IDENTIFIER id, int expl, int suppress)
{
        DECL_SPEC ds = DEREF_dspec(id_storage(id));
        DECL_SPEC acc = (ds & dspec_access);
        if (acc) {
                check_access(id, acc);
        }
        if (!(ds & dspec_called)) {
                /* Mark use */
                if (!suppress) {
                        if (!(ds & dspec_virtual)) {
                                ds |= dspec_used;
                        }
                        ds |= dspec_called;
                        COPY_dspec(id_storage(id), ds);
                }

                /* Check for inheritance */
                if (ds & (dspec_inherit | dspec_alias | dspec_extern)) {
                        IDENTIFIER uid = DEREF_id(id_alias(id));
                        if (!EQ_id(uid, id)) {
                                ds = DEREF_dspec(id_storage(uid));
                                if (ds & dspec_called) {
                                        if (do_usage) {
                                                dump_call(id, &crt_loc, expl);
                                        }
                                        return;
                                }
                                if (!suppress) {
                                        if (!(ds & dspec_virtual)) {
                                                ds |= dspec_used;
                                        }
                                        ds |= dspec_called;
                                        COPY_dspec(id_storage(uid), ds);
                                }
                        }
                }

                /* Check usage */
                if (ds & dspec_main) {
                        HASHID nm = DEREF_hashid(id_name(id));
                        if (IS_hashid_name(nm)) {
                                /* Can't call 'main' */
                                report(crt_loc, ERR_basic_start_main_call(id));
                        }
                }
                if (!(ds & dspec_defn) && !suppress) {
                        if (ds & dspec_implicit) {
                                /* Define implicitly declared function */
                                implicit_defn(id, DEFAULT_USR);
                                ds = DEREF_dspec(id_storage(id));
                        } else if (ds & dspec_instance) {
                                /* Define template function */
                                if (!(ds & dspec_virtual)) {
                                        define_template(id, 0);
                                        ds = DEREF_dspec(id_storage(id));
                                }
                        }
                        if ((ds & dspec_inline) && !(ds & dspec_defn)) {
                                /* An inline function called before it is
                                 * defined */
                                report(crt_loc,
                                       ERR_dcl_fct_spec_inline_call(id));
                        }
                }
        }
        if (do_usage) {
                dump_call(id, &crt_loc, expl);
        }
        return;
}


/*
    MARK A VIRTUAL FUNCTION AS BEING USED

    Note that use_func_id does not mark virtual functions as having been
    used because the actual function called can only be determined at
    run-time.  If subsequently it is found that the function called can
    be determined statically then this routine is called to mark that
    function as having been used.  It is also used in certain similar
    situations.
*/

void
reuse_id(IDENTIFIER id, int suppress)
{
        DECL_SPEC ds = DEREF_dspec(id_storage(id));
        if (!(ds & dspec_used) && !suppress) {
                ds |= dspec_used;
                COPY_dspec(id_storage(id), ds);
                if (!(ds & dspec_defn)) {
                        if (ds & dspec_implicit) {
                                /* Define implicitly declared function */
                                implicit_defn(id, DEFAULT_USR);
                        } else if (ds & dspec_instance) {
                                /* Instantiate template functions */
                                define_template(id, 0);
                        }
                }
        }
        return;
}


/*
    DEFINE AN IDENTIFIER ALIAS

    This routine is called if the function or variable id is defined
    (excluding tentative definitions).  It passes the definition on to
    any alias of id.
*/

void
define_id(IDENTIFIER id)
{
        IDENTIFIER lid = DEREF_id(id_alias(id));
        if (!EQ_id(lid, id)) {
                /* Check for previous definition */
                LOCATION loc;
                DECL_SPEC ds;
                DEREF_loc(id_loc(id), loc);
                ds = DEREF_dspec(id_storage(lid));
                if (ds & dspec_defn) {
                        EXP e = DEREF_exp(id_variable_etc_init(lid));
                        if (IS_NULL_exp(e) || !IS_exp_zero(e)) {
                                /* Exclude previous tentative definitions */
                                PTR(LOCATION)ploc = id_loc(lid);
                                ERROR err = ERR_basic_odr_def(lid, ploc);
                                if (ds & dspec_c) {
                                        /* Explain C linkage identifications */
                                        HASHID nm = DEREF_hashid(id_name(lid));
                                        ERROR err2 =
                                            ERR_dcl_link_redecl(nm, ploc);
                                        err = concat_error(err, err2);
                                }
                                report(loc, err);
                        }
                }

                /* Copy definition */
                if (IS_id_function_etc(id)) {
                        EXP e = DEREF_exp(id_function_etc_defn(id));
                        COPY_exp(id_function_etc_defn(lid), e);
                } else if (IS_id_variable_etc(id)) {
                        TYPE t = DEREF_type(id_variable_etc_type(id));
                        EXP e = DEREF_exp(id_variable_etc_init(id));
                        EXP d = DEREF_exp(id_variable_etc_term(id));
                        COPY_exp(id_variable_etc_init(lid), e);
                        COPY_exp(id_variable_etc_term(lid), d);
                        COPY_type(id_variable_etc_type(lid), t);
                }

                /* Record definition */
                ds |= dspec_defn;
                COPY_dspec(id_storage(lid), ds);
                DEREF_loc(id_loc(lid), loc);
        }
        return;
}


/*
    FIND THE END OF A LIST OF IDENTIFIER ALIASES

    In most cases the alias of an identifier is its underlying meaning,
    however for function parameters it is possible to have vast lists
    of identifiers arising from redeclarations all linked by their alias
    field.  This routine scans down such a list until it finds an identifier
    which is its own alias.
*/

IDENTIFIER
chase_alias(IDENTIFIER id)
{
        while (!IS_NULL_id(id)) {
                IDENTIFIER lid = DEREF_id(id_alias(id));
                if (EQ_id(lid, id)) {
                        break;
                }
                id = lid;
        }
        return (id);
}


/*
    CURRENT IDENTIFIER QUALIFIER

    This variable is set to describe how the current identifier is
    qualified.
*/

QUALIFIER crt_id_qualifier = qual_none;
int crt_templ_qualifier = 0;


/*
    CHECK AN IDENTIFIER NAME

    This routine checks whether the identifier name id is suitable for use
    in the context given by loc.  The namespace qualifiers used in describing
    id will be given by crt_id_qualifier.  The routine returns true for
    legal identifiers.
*/

ERROR
check_id_name(IDENTIFIER id, int loc)
{
        /* Check on namespace component */
        ERROR err = NULL_err;
        QUALIFIER cq = crt_id_qualifier;
        switch (cq) {
        case qual_none: {
                /* No namespace specifier */
                if (in_template_decl && is_templ_alias(id)) {
                        /* Check for hiding of template parameters */
                        err = ERR_temp_local_hide(id);
                        if (!IS_NULL_err(err)) {
                                return (err);
                        }
                }
                if (crt_templ_qualifier) {
                        goto qualifier_lab;
                }
                break;
        }
        case qual_nested:
qualifier_lab:
                /* Nested namespace specifier */
                if (loc == CONTEXT_PARAMETER || loc == CONTEXT_TEMPL_PARAM) {
                        err = ERR_dcl_meaning_id(cq, id);
                        if (!IS_NULL_err(err)) {
                                return (err);
                        }
                }
                break;
        case qual_full:
        case qual_top:
                /* Namespace specifier beginning with '::' */
                err = ERR_dcl_meaning_full(cq, id);
                if (!IS_NULL_err(err)) {
                        return (err);
                }
                goto qualifier_lab;
        }

        /* Check on name component */
        if (loc != CONTEXT_FUNCTION && loc != CONTEXT_FUNC_MEMBER) {
                HASHID nm = DEREF_hashid(id_name(id));
                switch (TAG_hashid(nm)) {
                case hashid_constr_tag:
                        /* A constructor must be a member function */
                        err = ERR_class_mem_ctor(id);
                        break;
                case hashid_destr_tag:
                        /* A destructor must be a member function */
                        err = ERR_class_dtor_func(nm);
                        break;
                case hashid_conv_tag:
                case hashid_op_tag:
                        /* These must be functions */
                        err = ERR_dcl_meaning_id(cq, id);
                        break;
                }
        }
        return (err);
}


/*
    DECLARE AN IMPLICIT FUNCTION

    This routine declares a function named id in the current scope with
    declaration specifiers ds, return type ret (which may be the null type),
    parameter types p (terminated by the null type), and function kind ell.
    It is used to declare implicit functions.
*/

IDENTIFIER
declare_func(DECL_SPEC ds, IDENTIFIER id, TYPE ret, TYPE *p, int ell,
             LIST(TYPE) ex)
{
        /* Save certain information */
        TYPE t;
        CV_SPEC cv;
        DECL_SPEC acc = crt_access;
        int td = have_type_declaration;
        QUALIFIER cq = crt_id_qualifier;
        int tq = crt_templ_qualifier;
        crt_access = dspec_public;
        crt_id_qualifier = qual_none;
        crt_templ_qualifier = 0;
        have_type_declaration = TYPE_DECL_NONE;

        /* Declare parameters */
        decl_loc = crt_loc;
        begin_param(id);
        while (!IS_NULL_type(*p)) {
                HASHID nm = lookup_anon();
                IDENTIFIER pid = DEREF_id(hashid_id(nm));
                pid = make_param_decl(dspec_none, *p, pid, CONTEXT_PARAMETER);
                init_param(pid, NULL_exp);
                p++;
        }
        if (IS_NULL_type(ret)) {
                ret = type_inferred;
        }
        cv = func_linkage(cv_none);
        t = make_func_type(ret, ell, cv, ex);
        end_param();

        /* Declare the function */
        if (in_class_defn) {
#if LANGUAGE_CPP
                if (ds & dspec_friend) {
                        id = make_friend_decl(ds, t, id, 0, 1);
                } else {
                        id = make_func_mem_decl(ds, t, id, 0);
                }
#else
                id = make_member_decl(ds, t, id, 0);
#endif
        } else {
                id = make_func_decl(ds, t, id, 0);
        }
        if (do_dump) {
                dump_implicit = 1;
                dump_declare(id, &decl_loc, 0);
        }
        have_type_declaration = td;
        crt_templ_qualifier = tq;
        crt_id_qualifier = cq;
        crt_access = acc;
        return (id);
}


/*
    IMPLICITLY DECLARE AN IDENTIFIER

    This routine implicitly declares an identifier id, either as a local
    variable, if fn is false, or as a function.  fn will be 2 in the cases
    where C allows an implicit function declaration.  The routine returns
    the corresponding identifier expression.
*/

EXP
implicit_id_exp(IDENTIFIER id, int fn)
{
        EXP e;
        ERROR err;
        LOCATION loc;
        DECL_SPEC ds;
        IDENTIFIER pid;
        DECL_SPEC cl = crt_linkage;
        QUALIFIER cq = crt_id_qualifier;
        int tq = crt_templ_qualifier;
        NAMESPACE cns = crt_namespace;
        HASHID nm = DEREF_hashid(id_name(id));
        NAMESPACE ns = DEREF_nspace(id_parent(id));

        /* Check for implicit function declarations */
        if (fn == 2 && option(OPT_func_impl) == OPTION_DISALLOW) {
                fn = 1;
        }

        /* Allow for template dependent declarations */
        if (in_template_decl && is_templ_nspace(cns)) {
                int opt = OPT_templ_undecl;
                OPTION sev = option(opt);
                if (sev != OPTION_ALLOW) {
                        /* Implicit declarations for dependent identifiers */
                        if (dependent_id(id)) {
                                opt = OPT_none;
                                sev = OPTION_ALLOW;
                        }
                }
                if (sev != OPTION_DISALLOW) {
                        if (sev != OPTION_ALLOW && fn != 2) {
                                if (cq == qual_none) {
                                        err = ERR_lookup_unqual_undef(nm);
                                } else {
                                        err = ERR_lookup_qual_undef(nm, ns);
                                }
                                err = set_severity(err, opt, 0);
                                report(crt_loc, err);
                        }
                        MAKE_exp_undeclared(type_templ_param, id, cq, e);
                        return (e);
                }
                if (cq == qual_none) {
                        ns = NULL_nspace;
                }
        }

        /* Check for previous declaration */
        if (IS_NULL_nspace(ns)) {
                OPTION sev = option(OPT_decl_unify);
                if (sev != OPTION_OFF) {
                        LIST(IDENTIFIER)p;
                        NAMESPACE pns = nonblock_namespace;
                        p = DEREF_list(nspace_named_etc_extra(pns));
                        pid = unify_extern(id, NULL_type, pns, p);
                        if (!IS_NULL_id(pid)) {
                                if (sev != OPTION_ON) {
                                        err = ERR_lookup_unqual_vis(pid);
                                        err = set_severity(err, OPT_decl_unify,
                                                           0);
                                        report(crt_loc, err);
                                }
                                e = make_id_exp(pid);
                                return (e);
                        }
                }
                ns = cns;
        }

        /* Rescan identifier */
        pid = search_id(ns, nm, 0, 0);
        if (!IS_NULL_id(pid)) {
                switch (TAG_id(pid)) {
                case id_variable_tag:
                case id_function_tag: {
                        e = make_id_exp(pid);
                        return (e);
                }
                }
        }

        /* Find namespace for declaration */
        crt_linkage = dspec_c;
        crt_id_qualifier = qual_none;
        crt_templ_qualifier = 0;
        push_namespace(ns);
        bad_crt_loc++;
        loc = crt_loc;
        if (crt_state_depth == 0) {
                /* Find identifier location */
                IDENTIFIER uid = underlying_id(id);
                DEREF_loc(id_loc(uid), crt_loc);
        }

        if (fn) {
                /* Implicit functions declarations */
                int ell;
                TYPE ret = type_sint;
                TYPE pars = NULL_type;
#if LANGUAGE_CPP
                /* Type is 'int ( ... )' in C++ */
                ell = FUNC_ELLIPSIS;
                if (fn == 1) {
                        ret = type_error;
                }
#else
                /* Type is 'int ()' in C */
                ell = FUNC_NO_PARAMS;
#endif
                ds = (dspec_extern | dspec_ignore);
                id = declare_func(ds, id, ret, &pars, ell, empty_type_set);
        } else {
                /* Other implicit declarations */
                decl_loc = crt_loc;
                id = make_object_decl(dspec_none, type_error, id, 0);
                IGNORE init_object(id, NULL_exp);
        }

        /* Report error */
        crt_linkage = cl;
        crt_templ_qualifier = tq;
        crt_id_qualifier = cq;
        if (fn == 2) {
                pid = DEREF_id(id_alias(id));
                ds = DEREF_dspec(id_storage(pid));
                if (ds & dspec_temp) {
                        /* Implicit declaration already reported */
                        err = NULL_err;
                } else {
                        err = ERR_expr_call_undecl(id);
                        if (!IS_NULL_err(err)) {
                                ds |= dspec_temp;
                                COPY_dspec(id_storage(pid), ds);
                        }
                }
        } else if (cq == qual_none) {
                err = ERR_lookup_unqual_undef(nm);
        } else {
                err = ERR_lookup_qual_undef(nm, ns);
        }
        report(crt_loc, err);

        /* Construct the result */
        crt_loc = loc;
        bad_crt_loc--;
        IGNORE pop_namespace();
        e = make_id_exp(id);
        return (e);
}


/*
    LIST OF MEANINGS

    This list is built up by list_ambiguous to give all the meanings of
    its ambiguous identifier which match its type argument.
*/

static LIST(IDENTIFIER) ambig_meanings = NULL_list(IDENTIFIER);


/*
    LIST MEANINGS OF AN AMBIGUOUS IDENTIFIER

    This routine adds all meanings of the ambiguous identifier id to the
    error list err.
*/

static unsigned
list_ambiguous(IDENTIFIER id, unsigned n, ERROR *err, int type, int rec)
{
        if (!IS_NULL_id(id)) {
                switch (TAG_id(id)) {
                case id_ambig_tag: {
                        /* Ambiguous identifiers */
                        LIST(IDENTIFIER) p = DEREF_list(id_ambig_ids(id));
                        rec = DEREF_int(id_ambig_over(id));
                        while (!IS_NULL_list(p)) {
                                id = DEREF_id(HEAD_list(p));
                                n = list_ambiguous(id, n, err, type, rec);
                                p = TAIL_list(p);
                        }
                        break;
                }
                case id_function_tag:
                case id_mem_func_tag:
                case id_stat_mem_func_tag: {
                        /* Functions */
                        if (rec) {
                                IDENTIFIER over;
                                over = DEREF_id(id_function_etc_over(id));
                                n = list_ambiguous(over, n, err, type, rec);
                        }
                        goto default_lab;
                }
                case id_class_name_tag:
                case id_class_alias_tag:
                case id_enum_name_tag:
                case id_enum_alias_tag:
                case id_type_alias_tag: {
                        /* Types */
                        type = 0;
                        goto default_lab;
                }
                default:
default_lab: {
                     /* Other identifiers */
                     PTR(LOCATION) loc = id_loc(id);
                     n++;
                     add_error(err, ERR_fail_list_item(n, id, loc));
                     break;
             }
                }
                if (type == 0) {
                        /* Add to list of meanings */
                        CONS_id(id, ambig_meanings, ambig_meanings);
                }
        }
        return (n);
}


/*
    REPORT AN AMBIGUOUS IDENTIFIER

    This routine reports the identifier id as being ambiguous.  Overloaded
    functions count as a single identifier unless rec is true.  It returns
    one of the possible meanings.  Only types are considered if type is
    true.  If there is exactly one possible meaning or force is true the
    first meaning is returned.  Otherwise the null identifier is returned.
*/

IDENTIFIER
report_ambiguous(IDENTIFIER id, int type, int rec, int force)
{
        ERROR err, err2;
        LIST(IDENTIFIER) p;
        NAMESPACE ns = DEREF_nspace(id_parent(id));

        /* List all meanings */
        if (!IS_NULL_nspace(ns) && IS_nspace_ctype(ns)) {
                err = ERR_lookup_ambig_mem(id);
        } else {
                err = ERR_lookup_ambig_id(id);
        }
        err2 = ERR_lookup_ambig_list();
        if (!IS_NULL_err(err2)) {
                unsigned n = 0;
                err = concat_error(err, err2);
                n = list_ambiguous(id, n, &err, type, rec);
                err = concat_error(err, ERR_fail_list_end(n));
        }
        report(crt_loc, err);

        /* Check for resolved meaning */
        id = NULL_id;
        p = ambig_meanings;
        if (!IS_NULL_list(p)) {
                if (IS_NULL_list(TAIL_list(p)) || force) {
                        id = DEREF_id(HEAD_list(p));
                }
                DESTROY_list(p, SIZE_id);
                ambig_meanings = NULL_list(IDENTIFIER);
        }
        return (id);
}


/*
    FIND THE DECLARATION SPECIFIER FOR AN AMBIGUOUS IDENTIFIER

    This routine finds the declaration specifier for an ambiguous identifier
    given by the list of cases pids.  This consists of various properties
    which all the cases share.
*/

DECL_SPEC
find_ambig_dspec(LIST(IDENTIFIER) pids)
{
        DECL_SPEC ds = (dspec_implicit | dspec_template);
        while (!IS_NULL_list(pids)) {
                IDENTIFIER pid = DEREF_id(HEAD_list(pids));
                if (!IS_NULL_id(pid)) {
                        DECL_SPEC pds = DEREF_dspec(id_storage(pid));
                        ds &= pds;
                }
                pids = TAIL_list(pids);
        }
        return (ds);
}


/*
    CREATE AN IDENTIFIER EXPRESSION

    This routine creates an expression corresponding to the identifier id.
    Note that static member functions are identifier expressions if they
    are not overloaded, but member expression otherwise.  They are sorted
    out properly during overload resolution (see resolve_cast).
*/

EXP
make_id_exp(IDENTIFIER id)
{
        EXP e;
        unsigned tag = TAG_id(id);
        QUALIFIER cq = crt_id_qualifier;
        switch (tag) {
        case id_variable_tag:
        case id_parameter_tag: {
                /* Variables and parameters */
                TYPE t;
                DECL_SPEC ds;
                use_id(id, 0);
                ds = DEREF_dspec(id_storage(id));
                if (ds & dspec_auto) {
                        /* Check use of automatic variable */
                        NAMESPACE ns = crt_namespace;
                        switch (TAG_nspace(ns)) {
                        case nspace_block_tag:
                        case nspace_dummy_tag: {
                                if (really_in_function_defn > 1) {
                                        /* Used in nested function */
                                        IDENTIFIER fid;
                                        ns = DEREF_nspace(id_parent(id));
                                        fid = DEREF_id(nspace_name(ns));
                                        if (!EQ_id(fid, crt_func_id)) {
                                                ERROR err =
                                                    ERR_class_local_auto(id);
                                                report(crt_loc, err);
                                        }
                                }
                                break;
                        }
                        case nspace_param_tag:
                        case nspace_templ_tag: {
                                /* Used in default argument */
                                if (in_default_arg) {
                                        ERROR err =
                                            ERR_dcl_fct_default_param(id);
                                        report(crt_loc, err);
                                }
                                break;
                        }
                        default: {
                                /* Used in local class */
                                ERROR err = ERR_class_local_auto(id);
                                report(crt_loc, err);
                                break;
                        }
                        }
                }
                t = DEREF_type(id_variable_etc_type(id));
                if ((ds & dspec_extern) && cv_extern) {
                        CV_SPEC cv = DEREF_cv(type_qual(t));
                        t = qualify_type(t,(cv | cv_extern), 0);
                        used_extern_volatile = 1;
                }
                MAKE_exp_identifier(t, id, cq, e);
                break;
        }
        case id_stat_member_tag: {
                /* Static data members */
                TYPE t;
                DECL_SPEC ds;
                use_id(id, suppress_usage);
                ds = DEREF_dspec(id_storage(id));
                if (ds & dspec_inherit) {
                        id = DEREF_id(id_alias(id));
                }
                t = DEREF_type(id_stat_member_type(id));
                if ((ds & dspec_extern) && cv_extern) {
                        CV_SPEC cv = DEREF_cv(type_qual(t));
                        t = qualify_type(t,(cv | cv_extern), 0);
                        used_extern_volatile = 1;
                }
                MAKE_exp_identifier(t, id, cq, e);
                break;
        }
        case id_function_tag: {
                /* Normal functions */
                TYPE t = DEREF_type(id_function_type(id));
                MAKE_exp_identifier(t, id, cq, e);
                break;
        }
        case id_stat_mem_func_tag: {
                /* Static member functions */
                TYPE t = DEREF_type(id_stat_mem_func_type(id));
                IDENTIFIER over = DEREF_id(id_stat_mem_func_over(id));
                if (IS_NULL_id(over)) {
                        MAKE_exp_identifier(t, id, cq, e);
                } else {
                        MAKE_exp_member(t, id, cq, e);
                }
                break;
        }
        case id_mem_func_tag: {
                /* Non-static member functions */
                TYPE t = DEREF_type(id_mem_func_type(id));
                MAKE_exp_member(t, id, cq, e);
                break;
        }
        case id_member_tag: {
                /* Non-static members */
                TYPE t = DEREF_type(id_member_type(id));
                MAKE_exp_member(t, id, cq, e);
                break;
        }
        case id_enumerator_tag: {
                /* Enumerators */
                NAT n;
                TYPE t;
                use_id(id, 0);
                e = DEREF_exp(id_enumerator_value(id));
                DECONS_exp_int_lit(t, n, tag, e);
                MAKE_exp_int_lit(t, n, tag, e);
                break;
        }
        case id_token_tag: {
                /* Tokens */
                TOKEN tok = DEREF_tok(id_token_sort(id));
                switch (TAG_tok(tok)) {
                case tok_exp_tag:
                case tok_nat_tag:
                case tok_snat_tag:
                case tok_stmt_tag:
                        /* Expression tokens */
                        use_id(id, 0);
                        id = DEREF_id(id_token_alt(id));
                        e = apply_exp_token(id, NULL_list(TOKEN), 0);
                        break;
                default:
                        /* Other tokens */
                        goto default_lab;
                }
                break;
        }
        case id_class_name_tag:
        case id_enum_name_tag:
        case id_class_alias_tag:
        case id_enum_alias_tag:
        case id_type_alias_tag: {
                /* Type names */
                TYPE t = DEREF_type(id_class_name_etc_defn(id));
                report(crt_loc, ERR_expr_prim_type(id));
                MAKE_exp_value(t, e);
                break;
        }
        case id_ambig_tag: {
                /* Ambiguous identifiers */
                MAKE_exp_ambiguous(type_error, id, cq, e);
                break;
        }
        default:
default_lab:
                /* Undefined identifiers */
                if (cq == qual_none && in_template_decl) {
                        /* Create a dummy identifier */
                        HASHID nm = DEREF_hashid(id_name(id));
                        NAMESPACE ns = nonblock_namespace;
                        MAKE_id_undef(nm, dspec_none, ns, crt_loc, id);
                }
                MAKE_exp_undeclared(type_error, id, cq, e);
                break;
        }
        return (e);
}


/*
    FIND THE THIS PARAMETER OF A FUNCTION

    This routine returns the 'this' parameter of the member function fn.
    If use is true then the parameter is marked as used.
*/

IDENTIFIER
this_param(IDENTIFIER fn, int use)
{
        NAMESPACE ns;
        IDENTIFIER pid;
        HASHID nm = KEYWORD(lex_this_Hname);
        TYPE t = DEREF_type(id_mem_func_type(fn));
        while (IS_type_templ(t)) {
                t = DEREF_type(type_templ_defn(t));
        }
        ns = DEREF_nspace(type_func_pars(t));
        pid = search_id(ns, nm, 0, 0);
        if (!IS_NULL_id(pid) && use) {
                /* Mark parameter as used */
                DECL_SPEC ds = DEREF_dspec(id_storage(pid));
                ds |= dspec_used;
                COPY_dspec(id_storage(pid), ds);
        }
        return (pid);
}


/*
    CREATE A THIS EXPRESSION

    This routine deals with the 'this' expression.  This is only in scope
    in the definition of a non-static member function.   Note that the
    current value of 'this' is given by the address of the dummy parameter
    with name given by lex_this_Hname.
*/

EXP
make_this_exp(void)
{
        EXP e;
        TYPE t;
        IDENTIFIER fn = crt_func_id;
        if (in_function_defn && IS_id_mem_func(fn)) {
                /* The current value of 'this' is returned */
                IDENTIFIER pid = this_param(fn, 1);
                if (in_default_arg) {
                        /* Can't use 'this' in default argument */
                        ERROR err = ERR_dcl_fct_default_param(pid);
                        report(crt_loc, err);
                }
                e = DEREF_exp(id_parameter_init(pid));
                t = DEREF_type(exp_type(e));
                MAKE_exp_copy(t, e, e);
        } else {
                /* Must be inside a non-static member function */
                report(crt_loc, ERR_expr_prim_this());
                e = make_error_exp(0);
        }
        return (e);
}


/*
    CREATE A THIS EXPRESSION

    This routine creates an expression corresponding to the 'this' parameter
    of a member function.  The parent class namespace is assigned to pns.
    The null expression is returned if we are outside of a member function.
*/

EXP
make_this_ref(NAMESPACE *pns)
{
        IDENTIFIER fn = crt_func_id;
        if (in_function_defn && IS_id_mem_func(fn)) {
                EXP e;
                IDENTIFIER pid = this_param(fn, 1);
                TYPE t = DEREF_type(id_parameter_type(pid));
                MAKE_exp_identifier(t, pid, qual_none, e);
                *pns = DEREF_nspace(id_parent(fn));
                return (e);
        }
        return (NULL_exp);
}


/*
    DECLARE A THIS PARAMETER

    This routine declares the dummy extra parameter for the non-static
    member function given by the identifier fn.  The type of this parameter
    is the first element of the corresponding mtypes list (see
    member_func_type).  An expression giving the address of the parameter
    is stored in its default argument position.  This gives the value of
    the 'this' expression.
*/

EXP
make_this_decl(IDENTIFIER fn)
{
        /* Look up identifier */
        EXP e;
        TYPE t;
        IDENTIFIER pid;
        LIST(TYPE) mtypes;
        NAMESPACE ns = crt_namespace;
        HASHID nm = KEYWORD(lex_this_Hname);
        MEMBER mem = search_member(ns, nm, 1);
        IDENTIFIER qid = DEREF_id(member_id(mem));
        DECL_SPEC ds = (dspec_auto | dspec_defn | dspec_implicit);

        /* Construct the type of the parameter */
        TYPE f = DEREF_type(id_mem_func_type(fn));
        while (IS_type_templ(f)) {
                f = DEREF_type(type_templ_defn(f));
        }
        mtypes = DEREF_list(type_func_mtypes(f));
        t = DEREF_type(HEAD_list(mtypes));

        /* Declare parameter */
        MAKE_id_parameter(nm, ds, ns, crt_loc, t, pid);
        if (!IS_NULL_id(qid)) {
                qid = DEREF_id(id_alias(qid));
                COPY_id(id_alias(pid), qid);
        }
        COPY_id(hashid_cache(nm), pid);
        COPY_id(member_id(mem), pid);

        /* Construct the 'this' expression */
        t = DEREF_type(type_ref_sub(t));
        t = rvalue_type(t);
        if (option(OPT_this_lvalue) == OPTION_ALLOW) {
                MAKE_type_ptr(cv_lvalue, t, t);
                MAKE_exp_identifier(t, pid, qual_none, e);
        } else {
                MAKE_type_ptr(cv_none, t, t);
                MAKE_exp_identifier(t, pid, qual_none, e);
                MAKE_exp_contents(t, e, e);
        }
        COPY_exp(id_parameter_init(pid), e);
        return (e);
}


/*
    CHECK FOR THIS EXPRESSIONS

    This routine checks whether the expression e is a 'this' expression.
*/

int
is_this_exp(EXP e)
{
        if (!IS_NULL_exp(e)) {
                unsigned tag = TAG_exp(e);
                if (tag == exp_indir_tag) {
                        e = DEREF_exp(exp_indir_ptr(e));
                        tag = TAG_exp(e);
                }
                if (tag == exp_copy_tag) {
                        e = DEREF_exp(exp_copy_arg(e));
                        tag = TAG_exp(e);
                }
                if (tag == exp_contents_tag) {
                        e = DEREF_exp(exp_contents_ptr(e));
                        tag = TAG_exp(e);
                }
                if (tag == exp_copy_tag) {
                        e = DEREF_exp(exp_copy_arg(e));
                        tag = TAG_exp(e);
                }
                if (tag == exp_identifier_tag) {
                        IDENTIFIER id = DEREF_id(exp_identifier_id(e));
                        HASHID nm = DEREF_hashid(id_name(id));
                        if (EQ_KEYWORD(nm, lex_this_Hname)) {
                                return (1);
                        }
                }
        }
        return (0);
}


/*
    FIND THE ELLIPSIS PARAMETER OF A FUNCTION

    This routine returns the ellipsis parameter of the function fn.
*/

IDENTIFIER
ellipsis_param(IDENTIFIER fn)
{
        int ell;
        TYPE t = DEREF_type(id_function_etc_type(fn));
        while (IS_type_templ(t)) {
                t = DEREF_type(type_templ_defn(t));
        }
        ell = DEREF_int(type_func_ellipsis(t));
        if (ell & FUNC_ELLIPSIS) {
                HASHID nm = KEYWORD(lex_ellipsis_Hexp);
                NAMESPACE ns = DEREF_nspace(type_func_pars(t));
                IDENTIFIER pid = search_id(ns, nm, 0, 0);
                return (pid);
        }
        return (NULL_id);
}


/*
    FIND ELLIPSIS TYPE

    This routine returns the type of the dummy ellipsis parameter.  If
    force is true then an error is reported if this type has not been
    declared.
*/

static TYPE
find_ellipsis_type(int force)
{
        TYPE t = type_ellipsis;
        if (IS_NULL_type(t)) {
                /* Look up token if not already defined */
                IDENTIFIER tid = get_special(TOK_va_t, 1);
                if (!IS_NULL_id(tid) && IS_id_token(tid)) {
                        tid = DEREF_id(id_token_alt(tid));
                        if (IS_id_class_name_etc(tid)) {
                                t = DEREF_type(id_class_name_etc_defn(tid));
                                type_ellipsis = t;
                        }
                }
                if (IS_NULL_type(t)) {
                        if (force) {
                                /* Report if ellipsis type is undefined */
                                HASHID nm = KEYWORD(lex_ellipsis_Hexp);
                                report(crt_loc,
                                       ERR_lib_builtin(NULL_string, nm));
                        }
                        t = type_error;
                }
        }
        return (t);
}


/*
    DECLARE AN ELLIPSIS PARAMETER

    This routine declares the dummy extra parameter representing the
    ellipsis component of the function id.
*/

void
make_ellipsis_decl(void)
{
        IDENTIFIER pid;
        NAMESPACE ns = crt_namespace;
        TYPE t = find_ellipsis_type(0);
        HASHID nm = KEYWORD(lex_ellipsis_Hexp);
        MEMBER mem = search_member(ns, nm, 1);
        IDENTIFIER qid = DEREF_id(member_id(mem));
        DECL_SPEC ds = (dspec_auto | dspec_defn | dspec_implicit);
        MAKE_id_parameter(nm, ds, ns, crt_loc, t, pid);
        if (!IS_NULL_id(qid)) {
                qid = DEREF_id(id_alias(qid));
                COPY_id(id_alias(pid), qid);
        }
        COPY_id(hashid_cache(nm), pid);
        COPY_id(member_id(mem), pid);
        return;
}


/*
    CREATE AN ELLIPSIS EXPRESSION

    This routine creates an expression corresponding to the ellipsis
    expression '...'.  This can only be used in a function definition
    which has an ellipsis type, in which case it is an rvalue of type
    type_ellipsis.
*/

EXP
make_ellipsis_exp(void)
{
        EXP e;
        if (in_function_defn) {
                IDENTIFIER pid = ellipsis_param(crt_func_id);
                if (!IS_NULL_id(pid)) {
                        /* Form result */
                        TYPE t = find_ellipsis_type(1);
                        report(crt_loc, ERR_expr_call_ell_exp());
                        MAKE_exp_identifier(t, pid, qual_none, e);
                        return (e);
                }
        }
        report(crt_loc, ERR_expr_call_ell_func());
        e = make_error_exp(0);
        return (e);
}