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 "etype_ops.h"
#include "exp_ops.h"
#include "hashid_ops.h"
#include "id_ops.h"
#include "inst_ops.h"
#include "itype_ops.h"
#include "member_ops.h"
#include "nat_ops.h"
#include "nspace_ops.h"
#include "off_ops.h"
#include "tok_ops.h"
#include "type_ops.h"
#include "error.h"
#include "catalog.h"
#include "option.h"
#include "basetype.h"
#include "check.h"
#include "chktype.h"
#include "class.h"
#include "compile.h"
#include "convert.h"
#include "copy.h"
#include "declare.h"
#include "derive.h"
#include "dump.h"
#include "expression.h"
#include "hash.h"
#include "identifier.h"
#include "initialise.h"
#include "instance.h"
#include "macro.h"
#include "namespace.h"
#include "option.h"
#include "overload.h"
#include "parse.h"
#include "predict.h"
#include "statement.h"
#include "syntax.h"
#include "template.h"
#include "tokdef.h"
#include "token.h"
#include "ustring.h"


/*
    TEMPLATE ARGUMENT HACK FLAG

    This flag can be set to true to indicate that the template argument
    hack, namely mapping '>>' to '> >' at the end of a set of template
    arguments, should be applied.
*/

static int apply_rshift_hack = 0;


/*
    SKIP TEMPLATE ARGUMENTS OR PARAMETERS

    This routine skips a set of template arguments or parameters.  It
    returns the sequence of preprocessing tokens enclosed between the
    initial '<' and the matching closing '>'.
*/

static PPTOKEN *
skip_template(IDENTIFIER id)
{
        PPTOKEN *q;
        LOCATION loc;
        int templ = 0;
        int angles = 1;
        int brackets = 0;
        int t = crt_lex_token;
        PPTOKEN *p = crt_token;
        loc = crt_loc;
        do {
                switch (t) {
                case lex_less:
                        /* Open angle brackets */
                        if (!brackets && templ) {
                                angles++;
                        }
                        templ = 0;
                        break;
                case lex_greater:
                        /* Close angle brackets */
                        if (!brackets) {
                                angles--;
                        }
                        templ = 0;
                        break;
                case lex_rshift:
                        /* Map '>>' to '> >' */
                        if (!brackets && apply_rshift_hack) {
                                PPTOKEN *r = new_pptok();
                                r->tok = lex_greater;
                                r->next = crt_token->next;
                                crt_token->tok = lex_greater;
                                crt_token->next = r;
                                angles--;
                                report(crt_loc, ERR_temp_names_hack());
                        }
                        templ = 0;
                        break;
                case lex_open_Hround:
                case lex_open_Hbrace_H1:
                case lex_open_Hbrace_H2:
                case lex_open_Hsquare_H1:
                case lex_open_Hsquare_H2:
                        /* Open brackets */
                        brackets++;
                        templ = 0;
                        break;
                case lex_close_Hround:
                case lex_close_Hbrace_H1:
                case lex_close_Hbrace_H2:
                case lex_close_Hsquare_H1:
                case lex_close_Hsquare_H2:
                        /* Close brackets */
                        if (brackets) {
                                brackets--;
                        }
                        templ = 0;
                        break;
                case lex_identifier:
                case lex_template:
                case lex_const_Hcast:
                case lex_static_Hcast:
                case lex_dynamic_Hcast:
                case lex_reinterpret_Hcast:
                        /* These may be followed by '<' */
                        /* NOT YET IMPLEMENTED - but are they? */
                        templ = 1;
                        break;
                case lex_eof:
                        /* End of file */
                        if (IS_NULL_id(id)) {
                                report(loc, ERR_temp_param_eof());
                        } else {
                                report(loc, ERR_temp_names_eof(id));
                        }
                        angles = 0;
                        break;
                default:
                        /* Other tokens */
                        templ = 0;
                        break;
                }
                q = crt_token;
                t = expand_preproc(EXPAND_AHEAD);
        } while (angles);
        q->tok = lex_close_Htemplate;
        snip_tokens(p, q);
        return (p);
}


/*
    SKIP A SET OF TEMPLATE ARGUMENTS

    This routine skips a set of arguments for the template id.  It is
    entered with the current token equal to the template name preceeding
    the initial '<' if started is false, and equal to the initial '<'
    otherwise.  After skipping the current token is either still the
    template name or the token following the template arguments, depending
    on the value of started.
*/

PPTOKEN *
skip_template_args(IDENTIFIER id, int started)
{
        PPTOKEN *q;
        PPTOKEN *p = crt_token;
        int t = crt_lex_token;
        if (started) {
                /* Patch in dummy preprocessing token */
                q = patch_tokens(1);
                q->tok = t;
                t = lex_ignore_token;
                p->tok = t;
        }
        IGNORE expand_preproc(EXPAND_AHEAD);
        crt_lex_token = lex_open_Htemplate;
        crt_token->tok = lex_open_Htemplate;
        q = skip_template(id);
        crt_lex_token = t;
        crt_token = p;
        if (started) {
                /* Advance to following token */
                ADVANCE_LEXER;
        }
        return (q);
}


/*
    PARSE A SET OF TEMPLATE ARGUMENTS

    This routine parses the template arguments p.  Note that unlike token
    arguments the template argument sorts are deduced by look-ahead rather
    than from the template sort.
*/

static LIST(TOKEN)
parse_template_args(PPTOKEN *p)
{
        int t;
        PARSE_STATE st;
        LIST(TOKEN) args = NULL_list(TOKEN);
        if (p == NULL) {
                return (args);
        }

        /* Initialise parser */
        save_state(&st, 1);
        init_parser(p);
        ADVANCE_LEXER;
        t = crt_lex_token;
        if (t == lex_open_Htemplate) {
                /* Step over open bracket */
                ADVANCE_LEXER;
                t = crt_lex_token;
        }

        /* Scan through arguments */
        if (t != lex_close_Htemplate) {
                for (;;) {
                        TOKEN arg;
                        if (predict_typeid(2)) {
                                TYPE r = NULL_type;
                                have_type_specifier = 0;
                                if (predict_typename()) {
                                        /* Template argument */
                                        IDENTIFIER rid = NULL_id;
                                        parse_id(&rid);
                                        MAKE_tok_class(r, rid, arg);
                                } else {
                                        /* Type argument */
                                        parse_type(&r);
                                        MAKE_tok_type(btype_lang, r, arg);
                                }
                        } else {
                                /* Expression argument */
                                EXP e = NULL_exp;
                                TYPE r = NULL_type;
                                parse_exp(&e);
                                if (!IS_NULL_exp(e)) {
                                        r = DEREF_type(exp_type(e));
                                }
                                MAKE_tok_exp(r, 1, e, arg);
                        }
                        if (have_syntax_error) {
                                break;
                        }
                        CONS_tok(arg, args, args);
                        t = crt_lex_token;
                        if (t == lex_close_Htemplate) {
                                break;
                        } else if (t == lex_comma) {
                                ADVANCE_LEXER;
                        } else {
                                t = lex_close_Htemplate;
                                report(crt_loc, ERR_lex_expect(t));
                                break;
                        }
                }
        }

        /* Restore state */
        restore_state(&st);
        p = restore_parser();
        free_tok_list(p);

        /* Return result */
        args = REVERSE_list(args);
        return (args);
}


/*
    CHECK A TEMPLATE PARAMETER TYPE

    This routine checks the type t of the template parameter id.
*/

static void
templ_param_type(IDENTIFIER id, TYPE t)
{
        switch (TAG_type(t)) {
        case type_floating_tag:
        case type_top_tag:
        case type_bottom_tag:
                /* Illegal parameter types */
                report(crt_loc, ERR_temp_param_type(id, t));
                break;
        }
        return;
}


/*
    DEFINE A TEMPLATE PARAMETER

    This routine defines the template parameter id to be arg.
*/

static int
define_templ_param(IDENTIFIER id, TOKEN arg, IDENTIFIER tid, int def)
{
    int ok = 1;
    TOKEN sort = DEREF_tok(id_token_sort(id));
    unsigned tag = TAG_tok(sort);
    if (tag == tok_type_tag) {
        /* Type parameter */
        TYPE t;
        if (IS_tok_type(arg)) {
            t = DEREF_type(tok_type_value(arg));
            if (def) {
                    t = expand_type(t, 2);
            }
            if (!is_global_type(t)) {
                /* Type must have external linkage */
                ERROR err = ERR_temp_arg_local(t);
                err = concat_error(ERR_temp_arg_init(id, tid), err);
                report(crt_loc, err);
            }
            COPY_type(tok_type_value(arg), t);
        } else {
            /* Non-type argument supplied */
            t = type_error;
            report(crt_loc, ERR_temp_arg_type(id, tid));
            ok = 0;
        }
        COPY_type(tok_type_value(sort), t);

    } else if (tag == tok_exp_tag) {
        /* Expression parameter */
        EXP e;
        if (IS_tok_exp(arg)) {
            int over = 0;
            ERROR err = NULL_err;
            TYPE s1 = DEREF_type(tok_exp_type(sort));
            TYPE s2 = expand_type(s1, 2);
            if (!EQ_type(s1, s2)) {
                    templ_param_type(id, s2);
            }
            e = DEREF_exp(tok_exp_value(arg));
            if (def) {
                /* Perform conversion if necessary */
                unsigned etag = TAG_exp(e);
                e = convert_reference(e, REF_ASSIGN);
                e = expand_exp(e, 2, 0);
                if (IS_exp_address_mem(e)) {
                    /* Check for overloaded pointer to members */
                    EXP a = DEREF_exp(exp_address_mem_arg(e));
                    if (IS_exp_member(a)) {
                        IDENTIFIER mid = DEREF_id(exp_member_id(a));
                        if (IS_id_function_etc(mid)) {
                            mid = DEREF_id(id_function_etc_over(mid));
                            if (!IS_NULL_id(mid)) {
                                    over = 1;
                            }
                        }
                    }
                }
                if (IS_type_array(s2)) {
                    if (etag == exp_paren_tag) {
                            e = make_paren_exp(e);
                    }
                    e = init_array(s2, cv_none, e, 1, &err);
                } else {
                    e = init_assign(s2, cv_none, e, &err);
                }
                if (!IS_NULL_err(err)) {
                        err = init_error(err, 0);
                }
            }
            if (is_const_exp(e, 1)) {
                switch (TAG_type(s2)) {
                    case type_integer_tag:
                    case type_floating_tag:
                    case type_top_tag:
                    case type_bottom_tag:
                    case type_enumerate_tag:
                    case type_token_tag:
                    case type_error_tag:
                        /* Constants of these types are alright */
                        break;
                    default: {
                        /* Check linkage in other cases */
                        EXP pa = NULL_exp;
                        DECL_SPEC ln = find_exp_linkage(e, &pa, 0);
                        if (ln & dspec_extern) {
                            /* External linkage */
                            /* EMPTY */
                        } else if (ln & dspec_static) {
                            /* Internal linkage */
                            ERROR err2 = ERR_temp_arg_internal();
                            err = concat_error(err, err2);
                        } else {
                            /* No linkage */
                            ERROR err2 = ERR_temp_arg_bad();
                            err = concat_error(err, err2);
                        }
                        if (over) {
                            /* Overloaded pointer to member */
                            ERROR err2 = ERR_temp_arg_over();
                            err = concat_error(err, err2);
                        }
                        break;
                    }
                }
            } else {
                err = concat_error(err, ERR_temp_arg_const());
            }
            if (!IS_NULL_err(err)) {
                err = concat_error(ERR_temp_arg_init(id, tid), err);
                report(crt_loc, err);
            }
            COPY_type(tok_exp_type(arg), s2);
            COPY_exp(tok_exp_value(arg), e);
        } else {
            /* Non-expression argument supplied */
            e = make_error_exp(0);
            report(crt_loc, ERR_temp_arg_exp(id, tid));
            ok = 0;
        }
        COPY_exp(tok_exp_value(sort), e);

    } else {
        /* Template class parameter */
        IDENTIFIER sid;
        if (IS_tok_class(arg)) {
            sid = DEREF_id(tok_class_value(arg));
            if (!IS_NULL_id(sid) && IS_id_class_name_etc(sid)) {
                TYPE s = DEREF_type(id_class_name_etc_defn(sid));
                if (!is_global_type(s)) {
                    /* Type must have external linkage */
                    ERROR err = ERR_temp_arg_local(s);
                    err = concat_error(ERR_temp_arg_init(id, tid), err);
                    report(crt_loc, err);
                }
            }
            init_template_param(id, sid);
        } else {
            /* Non-template argument supplied */
            HASHID nm = KEYWORD(lex_zzzz);
            sid = DEREF_id(hashid_id(nm));
            report(crt_loc, ERR_temp_arg_templ(id, tid));
            ok = 0;
        }
        COPY_id(tok_class_value(sort), sid);

    }
    return (ok);
}


/*
    DEFAULT TEMPLATE ARGUMENTS FLAG

    This flag may be set to false to suppress template default arguments.
*/

int allow_templ_dargs = 1;


/*
    CHECK A SET OF TEMPLATE ARGUMENTS

    This routine checks the set of template arguments args for the template
    tid of sort tok.  Note that if tid is a function then there may be
    less arguments than parameters, in this case in_template_decl is set
    to indicate that certain template parameters remain unbound.
*/

static LIST(TOKEN)
check_templ_args(TOKEN tok, LIST(TOKEN) args, IDENTIFIER tid)
{
        int s;
        int reported = 0;
        LIST(TOKEN) a = args;
        LIST(TOKEN) b = NULL_list(TOKEN);
        LIST(TOKEN) d = DEREF_list(tok_templ_dargs(tok));
        LIST(IDENTIFIER) pids = DEREF_list(tok_templ_pids(tok));
        LIST(IDENTIFIER) qids = pids;
        if (in_template_decl && depends_on_args(args, pids, 0, 1)) {
                /* Be extra careful in this case */
                tok = expand_sort(tok, 1, 1);
                args = check_templ_args(tok, args, tid);
                return (args);
        }
        s = save_token_args(qids, NULL_list(TOKEN));
        if (!allow_templ_dargs)d = NULL_list(TOKEN);
        while (!IS_NULL_list(pids)) {
                TOKEN arg = NULL_tok;
                IDENTIFIER pid = DEREF_id(HEAD_list(pids));
                if (!IS_NULL_list(a)) {
                        /* Use argument from list */
                        arg = DEREF_tok(HEAD_list(a));
                } else if (!IS_NULL_list(d)) {
                        /* Use default argument */
                        arg = DEREF_tok(HEAD_list(d));
                        if (!IS_NULL_tok(arg)) {
                                /* Add copy to list of arguments */
                                arg = expand_sort(arg, -1, 1);
                                CONS_tok(arg, b, b);
                        }
                }
                if (IS_NULL_tok(arg)) {
                        /* Not enough arguments */
                        if (!reported) {
                                if (IS_id_function_etc(tid)) {
                                        /* Allow for argument deduction */
                                        a = NULL_list(TOKEN);
                                        in_template_decl++;
                                        break;
                                }
                                report(crt_loc, ERR_temp_arg_less(tid));
                                reported = 1;
                        }
                        arg = DEREF_tok(id_token_sort(pid));
                        IGNORE is_bound_tok(arg, 1);
                        arg = expand_sort(arg, 2, 1);
                        CONS_tok(arg, b, b);
                }
                IGNORE define_templ_param(pid, arg, tid, 1);
                if (!IS_NULL_list(d)) {
                        d = TAIL_list(d);
                }
                if (!IS_NULL_list(a)) {
                        a = TAIL_list(a);
                }
                pids = TAIL_list(pids);
        }
        if (!IS_NULL_list(a)) {
                /* Too many arguments */
                report(crt_loc, ERR_temp_arg_more(tid));
        }
        if (!IS_NULL_list(b)) {
                /* Add default arguments to list */
                b = REVERSE_list(b);
                args = APPEND_list(args, b);
        }
        restore_token_args(qids, s);
        return (args);
}


/*
    CHECK A SET OF DEDUCED TEMPLATE ARGUMENTS

    This routine checks the deduced template arguments args for the
    template tid with parameters pids.
*/

void
check_deduced_args(IDENTIFIER tid, LIST(IDENTIFIER) pids, LIST(TOKEN) args)
{
        while (!IS_NULL_list(pids) && !IS_NULL_list(args)) {
                IDENTIFIER pid = DEREF_id(HEAD_list(pids));
                TOKEN arg = DEREF_tok(HEAD_list(args));
                IGNORE define_templ_param(pid, arg, tid, 0);
                args = TAIL_list(args);
                pids = TAIL_list(pids);
        }
        return;
}


/*
    DOES A SET OF TEMPLATE ARGUMENTS MATCH A SORT?

    This routine checks whether the template arguments args form a match
    for an initial segment of the template sort tok.
*/

static int
match_template_args(TOKEN tok, LIST(TOKEN) args)
{
        LIST(IDENTIFIER) pids = DEREF_list(tok_templ_pids(tok));
        while (!IS_NULL_list(pids) && !IS_NULL_list(args)) {
                IDENTIFIER pid = DEREF_id(HEAD_list(pids));
                TOKEN sort = DEREF_tok(id_token_sort(pid));
                TOKEN arg = DEREF_tok(HEAD_list(args));
                if (TAG_tok(arg)!= TAG_tok(sort)) {
                        /* Argument sorts do not match */
                        return (0);
                }
                args = TAIL_list(args);
                pids = TAIL_list(pids);
        }
        if (!IS_NULL_list(args)) {
                /* Too many arguments */
                return (0);
        }
        return (1);
}


/*
    APPLY A FUNCTION TEMPLATE

    This routine applies the function template id to the arguments args.
    Because id may comprise several overloaded template functions it is
    necessary to check each to determine whether the template parameter
    sorts match the argument sorts.  If more than one match is found the
    result is an overloaded function.
*/

static IDENTIFIER
apply_func_templ(IDENTIFIER id, LIST(TOKEN) args, int def)
{
        int force = 0;
        IDENTIFIER tid = NULL_id;
        do {
                /* Build up result */
                IDENTIFIER fid = id;
                while (!IS_NULL_id(fid)) {
                        TYPE t = DEREF_type(id_function_etc_type(fid));
                        if (IS_type_templ(t)) {
                                TOKEN sort = DEREF_tok(type_templ_sort(t));
                                if (force || match_template_args(sort, args)) {
                                        /* Argument sorts match */
                                        IDENTIFIER sid = tid;
                                        int td = in_template_decl;
                                        args =
                                            check_templ_args(sort, args, fid);
                                        tid = instance_func(fid, args, 0, def);
                                        COPY_id(id_function_etc_over(tid), sid);
                                        in_template_decl = td;
                                }
                        }
                        fid = DEREF_id(id_function_etc_over(fid));
                }
                if (force) {
                        /* Should have bound arguments by now */
                        if (IS_NULL_id(tid)) {
                                tid = id;
                        }
                } else {
                        /* Try again allowing for mismatches */
                        force = 1;
                }
        } while (IS_NULL_id(tid));
        return (tid);
}


/*
    APPLY A TYPEDEF TEMPLATE

    This routine applies the typedef template id to the arguments args.
*/

static TYPE
apply_typedef_templ(IDENTIFIER id, LIST(TOKEN) args)
{
        TYPE t = DEREF_type(id_class_name_etc_defn(id));
        if (IS_type_templ(t)) {
                int td = in_template_decl;
                TOKEN sort = DEREF_tok(type_templ_sort(t));
                LIST(IDENTIFIER) pids = DEREF_list(tok_templ_pids(sort));
                args = check_templ_args(sort, args, id);
                t = DEREF_type(type_templ_defn(t));
                if (is_templ_type(t)) {
                        /* Template template parameter */
                        IDENTIFIER tid = DEREF_id(type_token_tok(t));
                        MAKE_type_token(cv_none, tid, args, t);
                } else {
                        /* Expand type definition */
                        int d = save_token_args(pids, args);
                        TYPE s = expand_type(t, 1);
                        if (EQ_type(s, t)) {
                                s = copy_typedef(id, t, cv_none);
                        }
                        restore_token_args(pids, d);
                        t = s;
                }
                in_template_decl = td;
        } else {
                report(crt_loc, ERR_temp_names_not(id));
                t = copy_typedef(id, t, cv_none);
        }
        return (t);
}


/*
    APPLY A CLASS TEMPLATE

    This routine applies the class template id to the arguments args.
*/

static IDENTIFIER
apply_type_templ(IDENTIFIER id, LIST(TOKEN) args, int def)
{
        if (IS_id_class_name(id)) {
                /* Class template */
                TYPE t;
                DECL_SPEC ds = DEREF_dspec(id_storage(id));
                if (ds & dspec_implicit) {
                        /* Allow for nested calls */
                        IDENTIFIER tid = find_template(id, 0);
                        if (!IS_NULL_id(tid)) {
                                id = tid;
                        }
                }
                t = DEREF_type(id_class_name_etc_defn(id));
                if (IS_type_templ(t)) {
                        int td = in_template_decl;
                        TOKEN sort = DEREF_tok(type_templ_sort(t));
                        args = check_templ_args(sort, args, id);
                        id = instance_type(id, args, 0, def);
                        in_template_decl = td;
                } else {
                        report(crt_loc, ERR_temp_names_not(id));
                }
        } else {
                /* Type alias template */
                TYPE t = apply_typedef_templ(id, args);
                if (IS_type_compound(t)) {
                        CLASS_TYPE ct = DEREF_ctype(type_compound_defn(t));
                        complete_class(ct, def);
                        id = DEREF_id(ctype_name(ct));
                } else {
                        HASHID nm = DEREF_hashid(id_name(id));
                        NAMESPACE ns = DEREF_nspace(id_parent(id));
                        decl_loc = crt_loc;
                        id = make_typedef(ns, nm, t, dspec_none);
                }
        }
        return (id);
}


/*
    APPLY A TEMPLATE TO A SET OF ARGUMENTS

    This routine applies the template id to the arguments args.
*/

IDENTIFIER
apply_template(IDENTIFIER id, LIST(TOKEN) args, int def, int force)
{
        DECL_SPEC ds = DEREF_dspec(id_storage(id));
        if (ds & dspec_template) {
                if (IS_id_function_etc(id)) {
                        id = apply_func_templ(id, args, def);
                } else {
                        id = apply_type_templ(id, args, def);
                }
        } else {
                TYPE form;
                MAKE_type_token(cv_none, id, args, form);
                if (force || is_templ_depend(form)) {
                        /* Dummy template identifier */
                        HASHID nm = DEREF_hashid(id_name(id));
                        NAMESPACE ns = DEREF_nspace(id_parent(id));
                        MAKE_id_undef(nm, dspec_none, ns, crt_loc, id);
                        COPY_type(id_undef_form(id), form);
                } else {
                        report(crt_loc, ERR_temp_names_not(id));
                }
        }
        return (id);
}


/*
    PARSE A SET OF NON-TYPE TEMPLATE ARGUMENTS

    This routine parses the template arguments p for the non-class template
    id.  This includes both template functions and dummy template identifiers
    such as in 'ptr->template id < ... >'.
*/

IDENTIFIER
parse_id_template(IDENTIFIER id, PPTOKEN *p, int def)
{
        LIST(TOKEN) args = parse_template_args(p);
        id = apply_template(id, args, def, 1);
        return (id);
}


/*
    PARSE A SET OF TYPE TEMPLATE ARGUMENTS

    This routine parses the template arguments p for the class template
    id.  def is passed to instance_type.
*/

IDENTIFIER
parse_type_template(IDENTIFIER id, PPTOKEN *p, int def)
{
        LIST(TOKEN) args = parse_template_args(p);
        id = apply_type_templ(id, args, def);
        return (id);
}


/*
    PARSE A SET OF TYPEDEF TEMPLATE ARGUMENTS

    This routine parses the template arguments p for the typedef
    template id.
*/

TYPE
parse_typedef_templ(IDENTIFIER id, PPTOKEN *p)
{
        LIST(TOKEN) args = parse_template_args(p);
        TYPE t = apply_typedef_templ(id, args);
        return (t);
}


/*
    DEDUCE A TEMPLATE TYPE

    This routine deduces the arguments for the template type id called
    without arguments.  Within a template class definition the template
    name gives the the template applied to the current arguments.
    Otherwise template declarations and definitions (for which used is
    false) are allowed but other instances are not.
*/

TYPE
deduce_type_template(IDENTIFIER id, int used)
{
        TYPE t = DEREF_type(id_class_name_etc_defn(id));
        if (used) {
                TYPE s = t;
                while (IS_type_templ(s)) {
                        s = DEREF_type(type_templ_defn(s));
                }
                if (IS_type_compound(s)) {
                        CLASS_TYPE cs = DEREF_ctype(type_compound_defn(s));
                        if (defining_class(cs)) {
                                /* In class definition */
                                return (s);
                        }
                }
                report(crt_loc, ERR_temp_local_not(t));
        }
        return (t);
}


/*
    CURRENT TEMPLATE NAMESPACE

    This variable is used within a template declaration to hold the
    namespace in which the template parameters are declared.
*/

NAMESPACE templ_namespace = NULL_nspace;


/*
    LIST OF ALL TEMPLATE PARAMETERS

    These lists are dummy values representing the lists of all template
    parameters and all template or token parameters.
*/

LIST(IDENTIFIER) any_templ_param = NULL_list(IDENTIFIER);
LIST(IDENTIFIER) any_token_param = NULL_list(IDENTIFIER);


/*
    PARSE A SET OF TEMPLATE PARAMETERS

    This routine parses a set of template parameters.  It is entered after
    the initial 'template' has been read.  ex is true if this was preceded
    by 'export'.
*/

TOKEN
template_params(int ex)
{
        int t;
        TOKEN tok;
        PPTOKEN *p;
        NAMESPACE ns;
        LOCATION loc;
        PARSE_STATE s;
        int have_darg = 0;
        unsigned long npars = 0;
        DECL_SPEC use = dspec_none;
        LIST(TOKEN) dargs = NULL_list(TOKEN);
        LIST(IDENTIFIER) pids = NULL_list(IDENTIFIER);

        /* Can't have template declarations inside blocks */
        if (in_function_defn) {
                report(crt_loc, ERR_temp_decl_scope());
        } else if (in_class_defn && really_in_function_defn) {
                report(crt_loc, ERR_temp_mem_local());
        }

        /* Mark exported templates */
        if (ex || option(OPT_templ_export))use |= dspec_extern;

        /* Check for initial '<' */
        if (crt_lex_token != lex_less) {
                /* Explicit instantiation */
                MAKE_tok_templ(use, NULL_nspace, tok);
                return (tok);
        }

        /* Start template parameter namespace */
        ns = make_namespace(NULL_id, nspace_templ_tag, 0);
        push_namespace(ns);
        in_template_decl++;
        record_location++;

        /* Prepare to parse template parameters */
        ADVANCE_LEXER;
        loc = crt_loc;
        p = skip_template(NULL_id);
        save_state(&s, 1);
        crt_loc = loc;
        init_parser(p);
        ADVANCE_LEXER;
        t = crt_lex_token;

        /* Parse template parameters */
        if (t != lex_close_Htemplate) {
                for (;;) {
                        /* Declare parameter */
                        IDENTIFIER pid = NULL_id;
                        decl_loc = crt_loc;
                        if (predict_template()) {
                                /* Type parameter */
                                parse_type_param(&pid);
                        } else {
                                /* Expression parameter */
                                if (crt_lex_token == lex_typename) {
                                        /* Replace 'typename' by 'class' */
                                        crt_lex_token = lex_class;
                                }
                                parse_param(NULL_type, CONTEXT_TEMPL_PARAM,
                                            &pid);
                        }

                        /* Add parameter to list */
                        if (!IS_NULL_id(pid)) {
                                DECL_SPEC ds = DEREF_dspec(id_storage(pid));
                                ds |= dspec_template;
                                COPY_dspec(id_storage(pid), ds);
                                if (do_dump) {
                                        dump_token_param(pid);
                                }
                                tok = DEREF_tok(id_token_sort(pid));
                                switch (TAG_tok(tok)) {
                                case tok_exp_tag: {
                                        /* Expression parameter */
                                        int c;
                                        EXP e;
                                        TYPE r;
                                        DECONS_tok_exp(r, c, e, tok);
                                        templ_param_type(pid, r);
                                        if (IS_NULL_exp(e)) {
                                                if (have_darg) {
                                                        have_darg = 2;
                                                }
                                                tok = NULL_tok;
                                        } else {
                                                COPY_exp(tok_exp_value(tok),
                                                         NULL_exp);
                                                MAKE_tok_exp(r, c, e, tok);
                                                have_darg = 1;
                                        }
                                        break;
                                }
                                case tok_type_tag: {
                                        /* Type parameter */
                                        TYPE r = DEREF_type(tok_type_value(tok));
                                        if (IS_NULL_type(r)) {
                                                if (have_darg) {
                                                        have_darg = 2;
                                                }
                                                tok = NULL_tok;
                                        } else {
                                                COPY_type(tok_type_value(tok),
                                                          NULL_type);
                                                MAKE_tok_type(btype_lang, r,
                                                              tok);
                                                have_darg = 1;
                                        }
                                        break;
                                }
                                case tok_class_tag: {
                                        /* Template class parameter */
                                        TYPE r =
                                            DEREF_type(tok_class_type(tok));
                                        IDENTIFIER cid =
                                            DEREF_id(tok_class_value(tok));
                                        if (IS_NULL_id(cid)) {
                                                if (have_darg) {
                                                        have_darg = 2;
                                                }
                                                tok = NULL_tok;
                                        } else {
                                                COPY_id(tok_class_value(tok),
                                                        NULL_id);
                                                MAKE_tok_class(r, cid, tok);
                                                have_darg = 1;
                                        }
                                        break;
                                }
                                default:
                                        /* Shouldn't occur */
                                        tok = NULL_tok;
                                        break;
                                }
                                if (have_darg == 2) {
                                        /* Missing default argument */
                                        report(crt_loc,
                                               ERR_temp_param_default(pid));
                                }
                                CONS_tok(tok, dargs, dargs);
                                CONS_id(pid, pids, pids);
                                npars++;
                        }

                        /* Check for next parameter */
                        t = crt_lex_token;
                        if (t == lex_close_Htemplate) {
                                /* End of parameter list */
                                break;
                        } else if (t == lex_comma) {
                                /* Move on to next parameter */
                                ADVANCE_LEXER;
                        } else {
                                /* Syntax error */
                                if (!have_syntax_error) {
                                        ERROR err = ERR_lex_parse(crt_token);
                                        report(crt_loc, err);
                                }
                                break;
                        }
                }
        }

        /* Restore parser */
        restore_state(&s);
        p = restore_parser();
        free_tok_list(p);

        /* Construct the result */
        MAKE_tok_templ(use, crt_namespace, tok);
        if (IS_NULL_list(pids)) {
                /* Explicit specialisation */
                IGNORE pop_namespace();
                in_template_decl--;
                record_location--;
        } else {
                IGNORE check_value(OPT_VAL_template_pars, npars);
                pids = REVERSE_list(pids);
                dargs = REVERSE_list(dargs);
                COPY_list(tok_templ_pids(tok), pids);
                COPY_list(tok_templ_dargs(tok), dargs);
                set_proc_token(pids);
                templ_namespace = ns;
        }
        return (tok);
}


/*
    CREATE A TEMPLATE TYPE QUALIFIER

    This routine creates a template type qualifier from the template
    parameters tok and the type t.  It also terminates the template
    parameter namespace while leaving its names in scope.
*/

TYPE
make_template_type(TOKEN tok, TYPE t)
{
        TYPE s;
        LIST(IDENTIFIER) pids = DEREF_list(tok_templ_pids(tok));
        if (!IS_NULL_list(pids)) {
                /* Remove template parameters */
                IGNORE restore_namespace();
        }
        MAKE_type_templ(cv_none, tok, NULL_type, 0, s);
        if (!IS_NULL_type(t)) {
                unsigned tag = TAG_type(t);
                NAMESPACE ns = DEREF_nspace(tok_templ_pars(tok));
                if (IS_NULL_nspace(ns)) {
                        /* Can't have 'template template < ... >' */
                        report(crt_loc, ERR_temp_explicit_templ());
                        s = NULL_type;
                } else {
                        if (tag == type_templ_tag) {
                                tok = DEREF_tok(type_templ_sort(t));
                                ns = DEREF_nspace(tok_templ_pars(tok));
                                if (IS_NULL_nspace(ns)) {
                                        /* Can't have 'template < ... >
                                         * template' */
                                        report(crt_loc,
                                               ERR_temp_explicit_templ());
                                        t = DEREF_type(type_templ_defn(t));
                                        tag = TAG_type(t);
                                }
                        }
                }
                if (tag == type_func_tag) {
                        /* Ignore linkage specifiers */
                        CV_SPEC cv = DEREF_cv(type_func_mqual(t));
                        cv &= ~cv_language;
                        cv |= cv_cpp;
                        COPY_cv(type_func_mqual(t), cv);
                }
                s = inject_pre_type(t, s, 0);
        }
        return (s);
}


/*
    END A TEMPLATE DECLARATION

    This routine ends a template declaration.  It removes the names from
    the template parameter namespace from scope.
*/

void
end_template(TOKEN tok)
{
        LIST(IDENTIFIER) pids = DEREF_list(tok_templ_pids(tok));
        if (!IS_NULL_list(pids)) {
                remove_namespace();
                templ_namespace = NULL_nspace;
                in_template_decl--;
                record_location--;
                if (in_template_decl) {
                        /* Find enclosing template namespace */
                        LIST(NAMESPACE) lns = LIST_stack(namespace_stack);
                        while (!IS_NULL_list(lns)) {
                                NAMESPACE ns = DEREF_nspace(HEAD_list(lns));
                                if (IS_nspace_templ(ns)) {
                                        templ_namespace = ns;
                                        break;
                                }
                                lns = TAIL_list(lns);
                        }
                }
        }
        if (!in_template_decl) {
                clear_templates(1);
        }
        return;
}


/*
    CHECK A TEMPLATE DECLARATOR

    This routine is called whenever the template type t is used to qualify
    a class definition or a function declarator.
*/

void
template_decl(TYPE t)
{
        while (!IS_NULL_type(t) && IS_type_templ(t)) {
                TOKEN sort = DEREF_tok(type_templ_sort(t));
                DECL_SPEC ds = DEREF_dspec(tok_templ_usage(sort));
                if (ds & dspec_used) {
                        /* Already used */
                        report(crt_loc, ERR_temp_decl_one());
                }
                ds |= dspec_used;
                COPY_dspec(tok_templ_usage(sort), ds);
                t = DEREF_type(type_templ_defn(t));
        }
        return;
}


/*
    EXPORT A SET OF TEMPLATE INSTANCES

    This routine exports the instances associated with the template type t.
    It returns the non-template component of t.
*/

static TYPE
export_instances(TYPE t, int def)
{
        while (IS_type_templ(t)) {
                TOKEN sort = DEREF_tok(type_templ_sort(t));
                INSTANCE apps = DEREF_inst(tok_templ_apps(sort));
                while (!IS_NULL_inst(apps)) {
                        DECL_SPEC acc = DEREF_dspec(inst_templ_access(apps));
                        if (!(acc & (dspec_alias | dspec_main))) {
                                IDENTIFIER id = DEREF_id(inst_templ_id(apps));
                                export_template(id, def);
                        }
                        acc |= dspec_typedef;
                        COPY_dspec(inst_templ_access(apps), acc);
                        apps = DEREF_inst(inst_next(apps));
                }
                t = DEREF_type(type_templ_defn(t));
        }
        return (t);
}


/*
    EXPORT A TEMPLATE IDENTIFIER

    This routine marks the template identifier id as having been exported.
    def is 2 for the first explicit declaration of a template, 1 for a
    redeclaration and 0 otherwise.
*/

void export_template(IDENTIFIER id, int def)
{
        DECL_SPEC ds = DEREF_dspec(id_storage(id));
        if (ds & (dspec_inherit | dspec_implicit)) {
                return;
        }
        if (ds & (dspec_inline | dspec_static)) {
                return;
        }
        if (def == 0 && (ds & dspec_typedef)) {
                /* Already exported */
                return;
        }
        ds |= dspec_typedef;
        COPY_dspec(id_storage(id), ds);
        if (def == 2 && !has_linkage(id)) {
                /* Can't export anonymous identifiers */
                report(crt_loc, ERR_temp_decl_export(id));
        }
        switch (TAG_id(id)) {
        case id_class_name_tag:
        case id_class_alias_tag: {
                /* Template classes */
                TYPE t = DEREF_type(id_class_name_etc_defn(id));
                t = export_instances(t, def);
                if (IS_type_compound(t)) {
                        CLASS_TYPE ct = DEREF_ctype(type_compound_defn(t));
                        IDENTIFIER cid = DEREF_id(ctype_name(ct));
                        if (EQ_id(id, cid)) {
                                NAMESPACE ns = DEREF_nspace(ctype_member(ct));
                                MEMBER mem =
                                    DEREF_member(nspace_ctype_first(ns));
                                while (!IS_NULL_member(mem)) {
                                        /* Scan through class members */
                                        IDENTIFIER pid =
                                            DEREF_id(member_id(mem));
                                        IDENTIFIER qid =
                                            DEREF_id(member_alt(mem));
                                        if (!IS_NULL_id(pid)) {
                                                export_template(pid, def);
                                        }
                                        if (!IS_NULL_id(qid) &&
                                            !EQ_id(qid, pid)) {
                                                export_template(qid, def);
                                        }
                                        mem = DEREF_member(member_next(mem));
                                }
                        }
                }
                break;
        }
        case id_function_tag:
        case id_mem_func_tag:
        case id_stat_mem_func_tag: {
                /* Template functions */
                TYPE t = DEREF_type(id_function_etc_type(id));
                IGNORE export_instances(t, def);
                update_tag(id, 0);
                break;
        }
        case id_stat_member_tag:
                /* Static data members */
                update_tag(id, 0);
                break;
        }
        return;
}


/*
    HAS A TEMPLATE BEEN EXPORTED?

    This routine checks whether the template instance id has been exported.
*/

int
is_exported(IDENTIFIER id)
{
        TYPE form;
        int def = 0;
        DECL_SPEC ds = DEREF_dspec(id_storage(id));
        if (ds & dspec_typedef) {
                return (1);
        }
        form = find_form(id, &def);
        if (!IS_NULL_type(form) && IS_type_instance(form)) {
                IDENTIFIER tid = DEREF_id(type_instance_id(form));
                ds = DEREF_dspec(id_storage(tid));
                if (ds & dspec_typedef) {
                        export_template(id, 0);
                        return (1);
                }
        }
        return (0);
}


/*
    CREATE A SET OF PRIMARY TEMPLATE ARGUMENTS

    This routine creates a list of primary template arguments corresponding
    to the template parameters pids.
*/

LIST(TOKEN)
make_primary_args(LIST(IDENTIFIER) pids)
{
        LIST(TOKEN) args = NULL_list(TOKEN);
        while (!IS_NULL_list(pids)) {
                IDENTIFIER pid = DEREF_id(HEAD_list(pids));
                TOKEN arg = apply_token(pid, NULL_list(TOKEN));
                CONS_tok(arg, args, args);
                pids = TAIL_list(pids);
        }
        return (REVERSE_list(args));
}


/*
    CHECK A SET OF PRIMARY TEMPLATE PARAMETERS

    This routine checks the template parameters given by the type t for
    the declaration of the primary template class or function id.  It
    returns the non-template component of t.
*/

TYPE
check_templ_params(TYPE t, IDENTIFIER id)
{
        int depth = 0;
        unsigned tag = TAG_type(t);
        while (tag == type_templ_tag) {
                TOKEN sort = DEREF_tok(type_templ_sort(t));
                NAMESPACE ns = DEREF_nspace(tok_templ_pars(sort));
                DECL_SPEC use = DEREF_dspec(tok_templ_usage(sort));
                LIST(IDENTIFIER) pids = DEREF_list(tok_templ_pids(sort));
                TYPE s = DEREF_type(type_templ_defn(t));
                tag = TAG_type(s);
                if (IS_NULL_list(pids)) {
                        /* No template parameters */
                        if (IS_NULL_nspace(ns)) {
                                /* Explicit instantiation */
                                report(decl_loc, ERR_temp_explicit_bad(id));
                        } else {
                                /* Specialisation */
                                report(decl_loc, ERR_temp_param_none(id));
                                COPY_id(nspace_name(ns), id);
                        }
                } else {
                        /* Create primary specialisation */
                        TYPE form;
                        TYPE prim;
                        INSTANCE apps;
                        DECL_SPEC ds = (dspec_template | dspec_extern |
                                        dspec_main);
                        LIST(TOKEN) args = make_primary_args(pids);
                        MAKE_type_token(cv_none, id, args, form);
                        MAKE_type_templ(cv_none, sort, form, 1, prim);
                        apps = DEREF_inst(tok_templ_apps(sort));
                        MAKE_inst_templ(prim, apps, id, ds, all_instances,
                                        apps);
                        COPY_inst(type_token_app(form), apps);
                        COPY_inst(tok_templ_apps(sort), apps);
                        all_instances = apps;
                        if (tag == type_compound_tag) {
                                CLASS_TYPE cs =
                                    DEREF_ctype(type_compound_defn(s));
                                COPY_type(ctype_form(cs), form);
                        } else if (tag == type_func_tag) {
                                /* Can't have default arguments with function */
                                if (check_templ_dargs(t)) {
                                        report(decl_loc, ERR_temp_param_func());
                                }
                        }
                        COPY_id(nspace_name(ns), id);
                }
                if (use & dspec_extern) {
                        export_template(id, 2);
                }
                depth++;
                t = s;
        }
        if (depth > 1) {
                /* More than one level of templates */
                report(decl_loc, ERR_temp_decl_bad());
        }
        return (t);
}


/*
    CHECK FOR TEMPLATE DEFAULT ARGUMENTS

    This routine returns true if the template type t has default arguments.
*/

int
check_templ_dargs(TYPE t)
{
        if (IS_type_templ(t)) {
                TOKEN sort = DEREF_tok(type_templ_sort(t));
                LIST(TOKEN) dargs = DEREF_list(tok_templ_dargs(sort));
                while (!IS_NULL_list(dargs)) {
                        TOKEN darg = DEREF_tok(HEAD_list(dargs));
                        if (!IS_NULL_tok(darg)) {
                                return (1);
                        }
                        dargs = TAIL_list(dargs);
                }
        }
        return (0);
}


/*
    FIND AN UNDERLYING TEMPLATE

    This routine checks whether the identifier id results from the
    application of a template.  If so it returns the underlying template.
*/

IDENTIFIER
find_template(IDENTIFIER id, int force)
{
        if (!IS_NULL_id(id)) {
                switch (TAG_id(id)) {
                case id_class_name_tag: {
                        /* Template classes */
                        CLASS_TYPE ct;
                        int templ = 0;
                        TYPE t = DEREF_type(id_class_name_defn(id));
                        while (IS_type_templ(t)) {
                                t = DEREF_type(type_templ_defn(t));
                                templ = 1;
                        }
                        ct = DEREF_ctype(type_compound_defn(t));
                        t = DEREF_type(ctype_form(ct));
                        if (!IS_NULL_type(t) && IS_type_token(t)) {
                                IDENTIFIER tid = DEREF_id(type_token_tok(t));
                                if (!IS_id_token(tid)) {
                                        return (tid);
                                }
                        }
                        if (templ && force) {
                                /* Primary template class */
                                return (id);
                        }
                        break;
                }
                case id_function_tag:
                case id_mem_func_tag:
                case id_stat_mem_func_tag: {
                        /* Template functions */
                        TYPE t = DEREF_type(id_function_etc_form(id));
                        if (!IS_NULL_type(t) && IS_type_token(t)) {
                                IDENTIFIER tid = DEREF_id(type_token_tok(t));
                                if (!IS_id_token(tid)) {
                                        return (tid);
                                }
                        }
                        if (force) {
                                t = DEREF_type(id_function_etc_type(id));
                                if (IS_type_templ(t)) {
                                        /* Primary template function */
                                        return (id);
                                }
                        }
                        break;
                }
                case id_ambig_tag: {
                        /* Ambiguous identifiers */
                        LIST(IDENTIFIER) pids;
                        pids = DEREF_list(id_ambig_ids(id));
                        if (!IS_NULL_list(pids)) {
                                IDENTIFIER pid = DEREF_id(HEAD_list(pids));
                                IDENTIFIER tid = find_template(pid, force);
                                if (!IS_NULL_id(tid)) {
                                        pids = TAIL_list(pids);
                                        while (!IS_NULL_list(pids)) {
                                                IDENTIFIER sid;
                                                pid = DEREF_id(HEAD_list(pids));
                                                sid = find_template(pid, force);
                                                if (!EQ_id(sid, tid)) {
                                                        return (NULL_id);
                                                }
                                                pids = TAIL_list(pids);
                                        }
                                        return (tid);
                                }
                        }
                        break;
                }
                }
        }
        return (NULL_id);
}


/*
    REDECLARE A TEMPLATE PARAMETER

    This routine checks the template parameter id for redeclarations.
*/

static IDENTIFIER
redecl_templ_param(IDENTIFIER id)
{
        HASHID nm = DEREF_hashid(id_name(id));
        MEMBER mem = search_member(crt_namespace, nm, 1);
        IDENTIFIER pid = DEREF_id(member_id(mem));
        if (!IS_NULL_id(pid)) {
                /* Parameter already defined */
                report(crt_loc, ERR_temp_param_dup(nm));
                nm = lookup_anon();
                id = DEREF_id(hashid_id(nm));
        }
        return (id);
}


/*
    DECLARE A TEMPLATE TYPE PARAMETER

    This routine declares a template type parameter named id.
*/

IDENTIFIER
make_type_param(IDENTIFIER id)
{
        TOKEN tok;
        MAKE_tok_type(btype_template, NULL_type, tok);
        id = redecl_templ_param(id);
        id = make_token_decl(tok, 0, id, NULL_id);
        return (id);
}


/*
    SET A DEFAULT TEMPLATE TYPE ARGUMENT

    This routine sets the default value for the template type parameter id
    to be t.
*/

void
init_type_param(IDENTIFIER id, TYPE t)
{
        DECL_SPEC ds = DEREF_dspec(id_storage(id));
        COPY_dspec(id_storage(id), (ds & ~dspec_pure));
        IGNORE define_type_token(id, t, 0);
        COPY_dspec(id_storage(id), ds);
        return;
}


/*
    DECLARE A TEMPLATE EXPRESSION PARAMETER

    This routine declares a template expression parameter named id of
    type t.
*/

IDENTIFIER
make_exp_param(TYPE t, IDENTIFIER id)
{
        TOKEN tok;
        t = rvalue_type(t);
        MAKE_tok_exp(t, 1, NULL_exp, tok);
        id = make_token_decl(tok, 0, id, NULL_id);
        return (id);
}


/*
    SET A DEFAULT TEMPLATE EXPRESSION ARGUMENT

    This routine sets the default value for the template expression
    parameter id to be e.
*/

void
init_exp_param(IDENTIFIER id, EXP e)
{
        DECL_SPEC ds = DEREF_dspec(id_storage(id));
        COPY_dspec(id_storage(id), (ds & ~dspec_pure));
        IGNORE define_exp_token(id, e, 1);
        COPY_dspec(id_storage(id), ds);
        return;
}


/*
    DECLARE A TEMPLATE TEMPLATE PARAMETER

    This routine declares a template template parameter named id of type t.
*/

IDENTIFIER
make_template_param(TYPE t, IDENTIFIER id)
{
        TOKEN tok;
        MAKE_tok_class(t, NULL_id, tok);
        id = redecl_templ_param(id);
        id = make_token_decl(tok, 0, id, NULL_id);
        return (id);
}


/*
    SET A TEMPLATE TEMPLATE ARGUMENT

    This routine sets the value for the template template parameter id to
    be tid.  This is used both to set a default argument value and to
    define a template template parameter.
*/

void
init_template_param(IDENTIFIER id, IDENTIFIER tid)
{
        if (!IS_NULL_id(tid)) {
                if (IS_id_class_name_etc(tid)) {
                        DECL_SPEC ds = DEREF_dspec(id_storage(id));
                        COPY_dspec(id_storage(id), (ds & ~dspec_pure));
                        IGNORE define_templ_token(id, tid);
                        COPY_dspec(id_storage(id), ds);
                } else {
                        report(crt_loc, ERR_temp_arg_templ_not(id, tid));
                }
        }
        return;
}


/*
    LIST OF DUMMY TYPE PARAMETERS

    This list is used to store all the dummy type parameters created by
    make_dummy_type to avoid duplicates.
*/

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


/*
    CREATE A DUMMY TYPE PARAMETER

    This routine creates a dummy type parameter named id in the namespace
    ns.  bt gives the token type kind.
*/

static TYPE
make_dummy_type(NAMESPACE ns, IDENTIFIER id, BASE_TYPE bt, LIST(TOKEN) args)
{
        TYPE t;
        HASHID nm = DEREF_hashid(id_name(id));
        LIST(IDENTIFIER) p = dummy_types;
        while (!IS_NULL_list(p)) {
                IDENTIFIER pid = DEREF_id(HEAD_list(p));
                HASHID pnm = DEREF_hashid(id_name(pid));
                NAMESPACE pns = DEREF_nspace(id_parent(pid));
                if (EQ_hashid(nm, pnm) && EQ_nspace(ns, pns)) {
                        TOKEN tok = DEREF_tok(id_token_sort(pid));
                        BASE_TYPE pt = DEREF_btype(tok_type_kind(tok));
                        if (bt == pt) {
                                id = pid;
                                break;
                        }
                }
                p = TAIL_list(p);
        }
        if (IS_NULL_list(p)) {
                /* Create new parameter */
                TOKEN tok;
                DECL_SPEC ds = (dspec_template | dspec_token | dspec_auto |
                                dspec_pure | dspec_implicit);
                MAKE_tok_type(bt, NULL_type, tok);
                MAKE_id_token(nm, ds, ns, crt_loc, tok, NULL_id, id);
                COPY_id(id_token_alt(id), id);
                CONS_id(id, dummy_types, dummy_types);
        }
        MAKE_type_token(cv_none, id, args, t);
        return (t);
}


/*
    DOES A TYPE REPRESENT A TEMPLATE SPECIALISATION?

    This routine checks whether the type t represents an explicit template
    specialisation or instantiation.
*/

int
is_templ_spec(TYPE t)
{
        while (!IS_NULL_type(t) && IS_type_templ(t)) {
                LIST(IDENTIFIER) pids;
                TOKEN sort = DEREF_tok(type_templ_sort(t));
                pids = DEREF_list(tok_templ_pids(sort));
                if (IS_NULL_list(pids)) {
                        return (1);
                }
                t = DEREF_type(type_templ_defn(t));
        }
        return (0);
}


/*
    IS A TYPE A TEMPLATE PARAMETER?

    This routine checks whether the type t represents a template parameter
    and a template declaration is currently being processed.
*/

int
is_templ_type(TYPE t)
{
        if (!IS_NULL_type(t) && IS_type_token(t)) {
                IDENTIFIER id = DEREF_id(type_token_tok(t));
                if (is_templ_param(id)) {
                        return (in_template_decl);
                }
        }
        return (0);
}


/*
    DOES A TYPE DEPEND ON A TEMPLATE TYPE PARAMETER?

    This routine checks whether the type t is dependent on any template
    parameter.
*/

int
is_templ_depend(TYPE t)
{
        if (in_template_decl) {
                /* Only need to check in a template declaration */
                return (depends_on(t, any_templ_param));
        }
        return (0);
}


/*
    IS AN IDENTIFIER A TEMPLATE TYPE PARAMETER?

    This routine checks whether the token identifier id represents a
    template type parameter.
*/

int
is_templ_param(IDENTIFIER id)
{
        DECL_SPEC ds = DEREF_dspec(id_storage(id));
        if ((ds & dspec_template) && (ds & dspec_auto)) {
                return (1);
        }
        return (0);
}


/*
    IS AN IDENTIFIER AN ALIAS FOR A TEMPLATE TYPE PARAMETER?

    This routine checks whether the identifier id is the internal name
    for a template type parameter.
*/

int
is_templ_alias(IDENTIFIER id)
{
        unsigned tag = TAG_id(id);
        if (tag == id_type_alias_tag) {
                TYPE t = DEREF_type(id_type_alias_defn(id));
                if (IS_type_token(t)) {
                        id = DEREF_id(type_token_tok(t));
                        tag = TAG_id(id);
                }
        } else if (tag == id_token_tag) {
                id = DEREF_id(id_token_alt(id));
                tag = TAG_id(id);
        }
        if (tag == id_token_tag && is_templ_param(id)) {
                DECL_SPEC ds = DEREF_dspec(id_storage(id));
                if (!(ds & dspec_implicit)) {
                        return (1);
                }
        }
        return (0);
}


/*
    IS AN IDENTIFIER A TEMPLATE DECLARATOR?

    This routine checks whether the declarator id represents a template
    instance.  If id is a function declaration then t gives the function
    type.
*/

int
is_templ_decl(IDENTIFIER id, TYPE t)
{
        if (crt_templ_qualifier) {
                /* Declaration is a template-id */
                IDENTIFIER tid = find_template(id, 0);
                if (!IS_NULL_id(tid)) {
                        return (1);
                }
        }
        if (!IS_NULL_type(t) && crt_id_qualifier != qual_none) {
                /* Function declarator is a qualified-id */
                int eq = 0;
                LIST(IDENTIFIER) pids = NULL_list(IDENTIFIER);
                IDENTIFIER pid = resolve_func(id, t, 1, 1, pids, &eq);
                if (!IS_NULL_id(pid)) {
                        IDENTIFIER tid = find_template(pid, 0);
                        if (!IS_NULL_id(tid)) {
                                return (1);
                        }
                }
        }
        return (0);
}


/*
    IS A NAMESPACE A TEMPLATE CLASS?

    This routine checks whether the namespace ns represents a template
    class or a nested class of a template class or a block of a template
    function.
*/

int
is_templ_nspace(NAMESPACE ns)
{
        while (!IS_NULL_nspace(ns)) {
                IDENTIFIER tid;
                IDENTIFIER id = DEREF_id(nspace_name(ns));
                if (IS_NULL_id(id)) {
                        break;
                }
                tid = find_template(id, 1);
                if (!IS_NULL_id(tid)) {
                        return (1);
                }
                ns = DEREF_nspace(id_parent(id));
        }
        return (0);
}


/*
    CHECK A TYPENAME

    This routine checks whether 'typename ns::id' can be used to declare
    a type.  If so this type is returned, otherwise the null type is
    returned.  Any following template arguments are dealt with in this
    routine.
*/

TYPE
check_typename(NAMESPACE ns, IDENTIFIER id, BASE_TYPE key)
{
        TYPE s = NULL_type;
        if (in_template_decl) {
                if (!IS_NULL_nspace(ns) && IS_nspace_ctype(ns)) {
                        IDENTIFIER tid = DEREF_id(nspace_name(ns));
                        TYPE t = DEREF_type(id_class_name_etc_defn(tid));
                        while (IS_type_templ(t)) {
                                /* Step over any template qualifiers */
                                t = DEREF_type(type_templ_defn(t));
                        }
                        if (is_templ_depend(t)) {
                                /* Qualifier depends on a template parameter */
                                int templ = 0;
                                LIST(TOKEN) args = NULL_list(TOKEN);
                                if (crt_lex_token == lex_less) {
                                        /* Step over template arguments */
                                        PPTOKEN *p =
                                            skip_template_args(NULL_id, 1);
                                        args = parse_template_args(p);
                                        templ = 1;
                                }
                                if (IS_id_class_name_etc(id)) {
                                        if (templ) {
                                                /* Apply template arguments */
                                                id = apply_template(id, args,
                                                                    0, 0);
                                        }
                                        s = DEREF_type(id_class_name_etc_defn(id));
                                        if (IS_type_templ(s)) {
                                                s = deduce_type_template(id, 1);
                                        }
                                        s = copy_typedef(id, s, cv_none);
                                        COPY_id(type_name(s), id);
                                        use_id(id, 0);
                                } else {
                                        BASE_TYPE bt = (btype_template |
                                                        btype_typename);
                                        if (templ) {
                                                bt |= btype_args;
                                        }
                                        s = make_dummy_type(ns, id, bt, args);
                                        if (key != btype_none) {
                                                /* Result should be a class */
                                                id = DEREF_id(type_token_tok(s));
                                                args = NULL_list(TOKEN);
                                                s = make_dummy_class(id, args,
                                                                     key);
                                        }
                                }
                        }
                }
        }
        return (s);
}


/*
    DECLARE A TYPENAME

    This routine handles a type declared using typename.  ns gives the
    name qualifiers used in the declaration and id gives the actual member
    name.  Any following template arguments are dealt with in this
    routine.
*/

TYPE
make_typename(NAMESPACE ns, IDENTIFIER id)
{
        TYPE s = check_typename(ns, id, btype_none);
        if (IS_NULL_type(s)) {
                int templ = 0;
                LIST(TOKEN) args = NULL_list(TOKEN);
                report(crt_loc, ERR_temp_res_qual());
                if (crt_lex_token == lex_less) {
                        /* Step over template arguments */
                        PPTOKEN *p = skip_template_args(NULL_id, 1);
                        args = parse_template_args(p);
                        templ = 1;
                }
                if (IS_id_class_name_etc(id)) {
                        /* Name denotes a type - return that */
                        if (templ) {
                                /* Apply template arguments */
                                id = apply_template(id, args, 0, 0);
                        }
                        s = DEREF_type(id_class_name_etc_defn(id));
                        if (IS_type_templ(s)) {
                                s = deduce_type_template(id, 1);
                        }
                        s = copy_typedef(id, s, cv_none);
                        COPY_id(type_name(s), id);
                        use_id(id, 0);
                } else {
                        /* Return the error type */
                        s = type_error;
                }
        }
        return (s);
}


/*
    LIST OF BAD TYPENAMES

    Without some action, an illegal typename can be reported many times.
    A list of all bad typename look-ups is maintained so that the error is
    only reported once.
*/

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


/*
    FIND THE TYPE GIVEN BY A TYPENAME

    This routine expands the type name id.  If no expansion is possible
    then the null type is returned.  type indicates whether the look-up
    should be for a type name or an object name (the latter is used when
    searching for a type previously declared using typename).
*/

TYPE
find_typename(IDENTIFIER id, LIST(TOKEN) args, BASE_TYPE bt, int type)
{
        TYPE t = NULL_type;
        NAMESPACE ns = DEREF_nspace(id_parent(id));
        NAMESPACE cns = rescan_nspace(ns);
        if (!EQ_nspace(cns, ns)) {
                /* Rescan type name */
                LIST(IDENTIFIER) p;
                HASHID nm = DEREF_hashid(id_name(id));
                IDENTIFIER tid = search_field(cns, nm, 0, type);
                if (!IS_NULL_id(tid) && IS_id_class_name_etc(tid)) {
                        /* Type name */
                        if (bt & btype_args) {
                                /* Apply template arguments */
                                tid = apply_template(tid, args, 0, 0);
                        }
                        t = DEREF_type(id_class_name_etc_defn(tid));
                        if (IS_type_templ(t)) {
                                t = deduce_type_template(tid, 1);
                        }
                        t = copy_typedef(tid, t, cv_none);
                        COPY_id(type_name(t), tid);
                        use_id(tid, 0);
                        return (t);
                }

                /* Check for template parameters */
                if (in_template_decl) {
                        if (!IS_NULL_nspace(cns) && IS_nspace_ctype(cns)) {
                                tid = DEREF_id(nspace_name(cns));
                                t = DEREF_type(id_class_name_etc_defn(tid));
                                while (IS_type_templ(t)) {
                                        t = DEREF_type(type_templ_defn(t));
                                }
                                if (is_templ_depend(t)) {
                                        t = make_dummy_type(cns, id, bt, args);
                                        return (t);
                                }
                        }
                }

                /* Report error */
                p = non_typenames;
                t = type_error;
                while (!IS_NULL_list(p)) {
                        IDENTIFIER pid = DEREF_id(HEAD_list(p));
                        HASHID pnm = DEREF_hashid(id_name(pid));
                        NAMESPACE pns = DEREF_nspace(id_parent(pid));
                        if (EQ_hashid(pnm, nm) && EQ_nspace(pns, cns)) {
                                /* Already reported */
                                break;
                        }
                }
                if (IS_NULL_list(p)) {
                        /* Report undefined type */
                        MAKE_id_type_alias(nm, dspec_none, cns, crt_loc, t,
                                           tid);
                        CONS_id(tid, non_typenames, non_typenames);
                        report(crt_loc, ERR_temp_res_type(cns, nm));
                }
        }
        return (t);
}


/*
    IDENTIFY TWO LISTS OF TEMPLATE PARAMETERS

    This routine identifies the list of template parameters ps with those
    in pt, returning true if this is possible.
*/

int
eq_templ_params(LIST(IDENTIFIER) ps, LIST(IDENTIFIER) pt)
{
        int ok = 1;
        while (!IS_NULL_list(ps) && !IS_NULL_list(pt)) {
                IDENTIFIER is = DEREF_id(HEAD_list(ps));
                IDENTIFIER it = DEREF_id(HEAD_list(pt));
                if (!EQ_id(is, it)) {
                        TOKEN ns, nt;
                        unsigned vs, vt;
                        if (IS_NULL_id(is)) {
                                return (0);
                        }
                        if (IS_NULL_id(it)) {
                                return (0);
                        }
                        ns = DEREF_tok(id_token_sort(is));
                        nt = DEREF_tok(id_token_sort(it));
                        vs = TAG_tok(ns);
                        vt = TAG_tok(nt);
                        if (vs != vt) {
                                /* Parameter sorts should be equal */
                                ok = 0;
                                break;
                        }
                        if (vs == tok_exp_tag) {
                                /* Check expression parameter types */
                                TYPE rs = DEREF_type(tok_exp_type(ns));
                                TYPE rt = DEREF_type(tok_exp_type(nt));
                                rs = expand_type(rs, 2);
                                if (eq_type(rs, rt)!= 1) {
                                        ok = 0;
                                        break;
                                }
                        }
                        if (vs == tok_class_tag) {
                                /* Check template class parameter types */
                                TYPE rs = DEREF_type(tok_class_type(ns));
                                TYPE rt = DEREF_type(tok_class_type(nt));
                                rs = expand_type(rs, 2);
                                if (eq_template(rs, rt, 0, 1, 0) != 3) {
                                        ok = 0;
                                        break;
                                }
                        }
                        it = DEREF_id(id_alias(it));
                        COPY_id(id_alias(is), it);
                }
                pt = TAIL_list(pt);
                ps = TAIL_list(ps);
        }
        if (!EQ_list(ps, pt)) {
                ok = 0;
        }
        return (ok);
}


/*
    RESTORE A LIST OF TEMPLATE PARAMETERS

    This routine clears the aliases set up by eq_templ_param from the
    list of template parameters ps.
*/

void
restore_templ_params(LIST(IDENTIFIER)ps)
{
        while (!IS_NULL_list(ps)) {
                IDENTIFIER is = DEREF_id(HEAD_list(ps));
                COPY_id(id_alias(is), is);
                ps = TAIL_list(ps);
        }
        return;
}


/*
    CHECK FOR TEMPLATE TYPE EQUALITY

    This routine checks whether the template types s and t are equal
    under a simple renaming of template parameters.  If def is false
    only the template parameters (and not the underlying type) are checked.
    mq and rf are as in eq_func_type, as is the return value.
*/

int
eq_template(TYPE s, TYPE t, int def, int mq, int rf)
{
        TOKEN as = DEREF_tok(type_templ_sort(s));
        TOKEN at = DEREF_tok(type_templ_sort(t));
        LIST(IDENTIFIER) ps = DEREF_list(tok_templ_pids(as));
        LIST(IDENTIFIER) pt = DEREF_list(tok_templ_pids(at));
        int eq = eq_templ_params(ps, pt);
        if (eq && def) {
                /* Check for equality of definitions */
                int ft = force_template;
                TYPE ds = DEREF_type(type_templ_defn(s));
                TYPE dt = DEREF_type(type_templ_defn(t));
                force_template = 0;
                eq = eq_func_type(ds, dt, mq, rf);
                force_template = ft;
        }
        restore_templ_params(ps);
        return (eq);
}


/*
    RENAME TEMPLATE PARAMETERS IN A TYPE

    This routine renames the parameters in the given template sort,
    returning the template type formed by applying this renaming to t.
*/

static TYPE
rename_templ_params(TOKEN sort, TYPE t, int rec)
{
        if (rec) {
                int d;
                LIST(TOKEN) args;
                LIST(IDENTIFIER) pids;
                LIST(IDENTIFIER) qids;
                pids = DEREF_list(tok_templ_pids(sort));
                sort = expand_sort(sort, 1, 1);
                qids = DEREF_list(tok_templ_pids(sort));
                args = make_primary_args(qids);
                d = save_token_args(pids, args);
                t = expand_type(t, 1);
                restore_token_args(pids, d);
        }
        MAKE_type_templ(cv_none, sort, t, 1, t);
        return (t);
}


/*
    CHECK FOR TEMPLATE TYPE SPECIALISATION

    This routine checks whether the type t is a specialisation of the
    template type s.  Type qualifiers are ignored if qu is false.
*/

int
deduce_template(TYPE s, TYPE t, int qu)
{
        int eq;
        TYPE r = DEREF_type(type_templ_defn(s));
        TOKEN sort = DEREF_tok(type_templ_sort(s));
        LIST(IDENTIFIER) pids = DEREF_list(tok_templ_pids(sort));
        if (in_template_decl && depends_on(t, pids)) {
                /* Rename parameters if necessary */
                CV_SPEC cv = DEREF_cv(type_qual(s));
                s = rename_templ_params(sort, r, 1);
                COPY_cv(type_qual(s), cv);
                eq = deduce_template(s, t, qu);
        } else {
                /* Perform argument deduction */
                int d;
                force_template++;
                d = save_token_args(pids, NULL_list(TOKEN));
                eq = eq_type_qual(r, t, qu);
                if (eq == 3) {
                        eq = 0;
                }
                restore_token_args(pids, d);
                force_template--;
        }
        return (eq);
}


/*
    REDECLARE A TEMPLATE TYPE

    This routine checks the redeclaration of the template id of type ps to
    have type pt.  The primary purpose of this is to check for default
    arguments in the redeclaration.  The non-template components are
    returned via ps and pt.
*/

void
redecl_template(TYPE *ps, TYPE *pt, IDENTIFIER id)
{
        TYPE s = *ps;
        TYPE t = *pt;
        while (IS_type_templ(s)) {
                s = DEREF_type(type_templ_defn(s));
        }
        while (IS_type_templ(t)) {
                TOKEN sort = DEREF_tok(type_templ_sort(t));
                DECL_SPEC use = DEREF_dspec(tok_templ_usage(sort));
                if (use & dspec_extern) {
                        export_template(id, 1);
                }
                if (check_templ_dargs(t)) {
                        /* Can't have default arguments in redeclaration */
                        report(decl_loc, ERR_temp_param_redecl());
                }
                t = DEREF_type(type_templ_defn(t));
        }
        *pt = t;
        *ps = s;
        return;
}


/*
    RESET THE PRIMARY FORM OF A TEMPLATE

    This routine changes the primary representation of a template from
    s to t.  This is done when, for example, the latter is a definition
    while the former is only a declaration.
*/

void
reset_primary_templ(TYPE s, TYPE t)
{
        unsigned ns = TAG_type(s);
        unsigned nt = TAG_type(t);
        while (ns == type_templ_tag && nt == type_templ_tag) {
                TOKEN as = DEREF_tok(type_templ_sort(s));
                TOKEN at = DEREF_tok(type_templ_sort(t));
                LIST(IDENTIFIER) ps = DEREF_list(tok_templ_pids(as));
                LIST(IDENTIFIER) pt = DEREF_list(tok_templ_pids(at));
                INSTANCE apps = DEREF_inst(tok_templ_apps(as));
                INSTANCE app = apps;
                LIST(TOKEN) dargs = DEREF_list(tok_templ_dargs(as));
                while (!IS_NULL_inst(app)) {
                        DECL_SPEC ds = DEREF_dspec(inst_templ_access(app));
                        if (ds & dspec_main) {
                                /* Replace primary template instance */
                                TYPE form = DEREF_type(inst_form(app));
                                LIST(TOKEN) args = make_primary_args(pt);
                                COPY_tok(type_templ_sort(form), at);
                                form = DEREF_type(type_templ_defn(form));
                                COPY_list(type_token_args(form), args);
                        }
                        app = DEREF_inst(inst_next(app));
                }
                if (check_templ_dargs(s)) {
                        /* Expand default arguments */
                        LIST(TOKEN) args = make_primary_args(pt);
                        int d = save_token_args(ps, args);
                        dargs = expand_args(dargs, 1, 1);
                        restore_token_args(ps, d);
                }
                COPY_list(tok_templ_dargs(at), dargs);
                COPY_inst(tok_templ_apps(at), apps);
                s = DEREF_type(type_templ_defn(s));
                t = DEREF_type(type_templ_defn(t));
                ns = TAG_type(s);
                nt = TAG_type(t);
        }
        return;
}


/*
    IS AN IDENTIFIER A TEMPLATE PARAMETER?

    This routine checks whether the token identifier id is one of the
    template or token parameters given by pids.
*/

int
depends_on_param(IDENTIFIER id, LIST(IDENTIFIER) pids)
{
        if (IS_id_token(id)) {
                DECL_SPEC ds = DEREF_dspec(id_storage(id));
                if (!(ds & dspec_ignore)) {
                        if (EQ_list(pids, any_templ_param)) {
                                /* Short-cut for list of all template
                                 * parameters */
                                if ((ds & dspec_template) &&
                                    (ds & dspec_auto)) {
                                        return (1);
                                }
                                return (0);
                        }
                        if (EQ_list(pids, any_token_param)) {
                                /* Short-cut for list of all token parameters */
                                if (ds & dspec_auto) {
                                        return (1);
                                }
                                return (0);
                        }
                        while (!IS_NULL_list(pids)) {
                                IDENTIFIER pid = DEREF_id(HEAD_list(pids));
                                if (EQ_id(pid, id)) {
                                        return (1);
                                }
                                pids = TAIL_list(pids);
                        }
                }
        }
        return (0);
}


/*
    DOES AN IDENTIFIER DEPEND ON A TEMPLATE PARAMETER?

    This routine checks whether the identifier id is one of the template
    parameters pids or is a template function with an argument depending
    on pids.  If use is true then any other identifiers found are marked
    as used.
*/

static int
depends_on_id(IDENTIFIER id, LIST(IDENTIFIER) pids, int use)
{
        if (!IS_NULL_id(id)) {
                NAMESPACE ns;
                switch (TAG_id(id)) {
                case id_class_name_tag: {
                        /* Check for template classes */
                        TYPE form;
                        CLASS_TYPE ct;
                        TYPE t = DEREF_type(id_class_name_defn(id));
                        while (IS_type_templ(t)) {
                                t = DEREF_type(type_templ_defn(t));
                        }
                        ct = DEREF_ctype(type_compound_defn(t));
                        form = DEREF_type(ctype_form(ct));
                        if (!IS_NULL_type(form)) {
                                if (depends_on(form, pids)) {
                                        return (1);
                                }
                        }
                        break;
                }
                case id_function_tag:
                case id_mem_func_tag:
                case id_stat_mem_func_tag: {
                        /* Check for template functions */
                        TYPE form = DEREF_type(id_function_etc_form(id));
                        if (!IS_NULL_type(form)) {
                                /* Check function form */
                                if (depends_on(form, pids)) {
                                        return (1);
                                }
                        }
                        if (use) {
                                reuse_id(id, 0);
                        }
                        break;
                }
                case id_token_tag: {
                        /* Check for template parameters */
                        if (depends_on_param(id, pids)) {
                                return (1);
                        }
                        break;
                }
                case id_ambig_tag: {
                        /* Check ambiguous identifiers */
                        LIST(IDENTIFIER) qids;
                        qids = DEREF_list(id_ambig_ids(id));
                        while (!IS_NULL_list(qids)) {
                                IDENTIFIER qid = DEREF_id(HEAD_list(qids));
                                if (depends_on_id(qid, pids, use)) {
                                        return (1);
                                }
                                qids = TAIL_list(qids);
                        }
                        break;
                }
                case id_stat_member_tag: {
                        /* Mark static data members */
                        if (use) {
                                reuse_id(id, 0);
                        }
                        break;
                }
                }
                ns = DEREF_nspace(id_parent(id));
                if (!IS_NULL_nspace(ns)) {
                        /* Check enclosing namespace */
                        IDENTIFIER cid = DEREF_id(nspace_name(ns));
                        return (depends_on_id(cid, pids, 0));
                }
        }
        return (0);
}


/*
    DOES A LIST OF TOKEN ARGUMENTS DEPEND ON A TEMPLATE PARAMETER?

    This routine checks whether the list of token arguments args depends
    on one of the template parameters pids.  If next is true then the
    algorithm is modified to check whether any token argument depends
    on a later template parameter (e.g. does the first element of args
    depend on the second, third, etc. element of pids).
*/

int
depends_on_args(LIST(TOKEN) args, LIST(IDENTIFIER) pids, int use, int next)
{
        while (!IS_NULL_list(args)) {
                TOKEN tok = DEREF_tok(HEAD_list(args));
                if (next) {
                        /* Move on to next parameter */
                        if (IS_NULL_list(pids)) {
                                break;
                        }
                        pids = TAIL_list(pids);
                }
                if (!IS_NULL_tok(tok)) {
                        switch (TAG_tok(tok)) {
                        case tok_exp_tag: {
                                EXP e = DEREF_exp(tok_exp_value(tok));
                                if (depends_on_exp(e, pids, use)) {
                                        return (1);
                                }
                                break;
                        }
                        case tok_stmt_tag: {
                                EXP e = DEREF_exp(tok_stmt_value(tok));
                                if (depends_on_exp(e, pids, use)) {
                                        return (1);
                                }
                                break;
                        }
                        case tok_nat_tag:
                        case tok_snat_tag: {
                                NAT n = DEREF_nat(tok_nat_etc_value(tok));
                                if (depends_on_nat(n, pids, use)) {
                                        return (1);
                                }
                                break;
                        }
                        case tok_type_tag: {
                                TYPE t = DEREF_type(tok_type_value(tok));
                                if (depends_on(t, pids)) {
                                        return (1);
                                }
                                break;
                        }
                        case tok_member_tag: {
                                OFFSET off = DEREF_off(tok_member_value(tok));
                                if (depends_on_off(off, pids, use)) {
                                        return (1);
                                }
                                break;
                        }
                        case tok_class_tag:
                                /* NOT YET IMPLEMENTED */
                                break;
                        }
                }
                args = TAIL_list(args);
        }
        return (0);
}


/*
    DOES AN INTEGRAL CONSTANT DEPEND ON A TEMPLATE PARAMETER?

    This routine checks whether the integral constant n depends on one
    of the template parameters pids.
*/

int
depends_on_nat(NAT n, LIST(IDENTIFIER) pids, int use)
{
        if (!IS_NULL_nat(n)) {
                switch (TAG_nat(n)) {
                case nat_calc_tag: {
                        EXP e = DEREF_exp(nat_calc_value(n));
                        return (depends_on_exp(e, pids, use));
                }
                case nat_token_tag: {
                        IDENTIFIER tid = DEREF_id(nat_token_tok(n));
                        LIST(TOKEN) args = DEREF_list(nat_token_args(n));
                        if (depends_on_param(tid, pids)) {
                                return (2);
                        }
                        if (depends_on_args(args, pids, use, 0)) {
                                return (1);
                        }
                        break;
                }
                }
        }
        return (0);
}


/*
    DOES A LIST OF EXPRESSIONS DEPEND ON A TEMPLATE PARAMETER?

    This routine checks whether the list of expressions p depends on one
    of the template parameters pids.
*/

static int
depends_on_exp_list(LIST(EXP) p, LIST(IDENTIFIER) pids, int use)
{
        while (!IS_NULL_list(p)) {
                EXP a = DEREF_exp(HEAD_list(p));
                if (depends_on_exp(a, pids, use)) {
                        return (1);
                }
                p = TAIL_list(p);
        }
        return (0);
}


/*
    DOES AN EXPRESSION DEPEND ON A TEMPLATE PARAMETER?

    This routine checks whether the expression e depends on one of the
    template parameters pids.  If e is actually a template parameter
    then 2 is returned.
*/

int
depends_on_exp(EXP e, LIST(IDENTIFIER) pids, int use)
{
        if (!IS_NULL_exp(e)) {
                unsigned tag = TAG_exp(e);
                TYPE t = DEREF_type(exp_type(e));
                if (tag == exp_token_tag) {
                        /* Check for template parameters */
                        IDENTIFIER tid = DEREF_id(exp_token_tok(e));
                        LIST(TOKEN) args = DEREF_list(exp_token_args(e));
                        if (depends_on_param(tid, pids)) {
                                return (2);
                        }
                        if (depends_on_args(args, pids, use, 0)) {
                                return (1);
                        }
                }
                if (depends_on(t, pids)) {
                        return (1);
                }
                ASSERT(ORDER_exp == 88);
                switch (tag) {
                case exp_identifier_tag:
                case exp_member_tag:
                case exp_ambiguous_tag:
                case exp_undeclared_tag: {
                        IDENTIFIER id = DEREF_id(exp_identifier_etc_id(e));
                        if (depends_on_id(id, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_int_lit_tag: {
                        NAT n = DEREF_nat(exp_int_lit_nat(e));
                        return (depends_on_nat(n, pids, use));
                }
                case exp_paren_tag:
                case exp_copy_tag: {
                        EXP a = DEREF_exp(exp_paren_etc_arg(e));
                        return (depends_on_exp(a, pids, use));
                }
                case exp_assign_tag: {
                        EXP a = DEREF_exp(exp_assign_ref(e));
                        EXP b = DEREF_exp(exp_assign_arg(e));
                        if (depends_on_exp(a, pids, use)) {
                                return (1);
                        }
                        if (depends_on_exp(b, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_init_tag: {
                        IDENTIFIER id = DEREF_id(exp_init_id(e));
                        EXP a = DEREF_exp(exp_init_arg(e));
                        if (depends_on_id(id, pids, use)) {
                                return (1);
                        }
                        if (depends_on_exp(a, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_preinc_tag: {
                        EXP a = DEREF_exp(exp_preinc_op(e));
                        if (depends_on_exp(a, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_postinc_tag: {
                        EXP a = DEREF_exp(exp_postinc_op(e));
                        if (depends_on_exp(a, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_indir_tag: {
                        EXP a = DEREF_exp(exp_indir_ptr(e));
                        if (depends_on_exp(a, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_contents_tag: {
                        EXP a = DEREF_exp(exp_contents_ptr(e));
                        if (depends_on_exp(a, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_address_tag: {
                        EXP a = DEREF_exp(exp_address_arg(e));
                        if (depends_on_exp(a, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_address_mem_tag: {
                        EXP a = DEREF_exp(exp_address_mem_arg(e));
                        if (depends_on_exp(a, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_func_tag: {
                        EXP a = DEREF_exp(exp_func_fn(e));
                        LIST(EXP) p = DEREF_list(exp_func_args(e));
                        if (depends_on_exp(a, pids, use)) {
                                return (1);
                        }
                        if (depends_on_exp_list(p, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_func_id_tag: {
                        IDENTIFIER id = DEREF_id(exp_func_id_id(e));
                        LIST(EXP) p = DEREF_list(exp_func_id_args(e));
                        if (depends_on_id(id, pids, use)) {
                                return (1);
                        }
                        if (depends_on_exp_list(p, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_call_tag: {
                        EXP a = DEREF_exp(exp_call_ptr(e));
                        EXP b = DEREF_exp(exp_call_arg(e));
                        if (depends_on_exp(a, pids, use)) {
                                return (1);
                        }
                        if (depends_on_exp(b, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_negate_tag:
                case exp_compl_tag:
                case exp_not_tag:
                case exp_abs_tag: {
                        EXP a = DEREF_exp(exp_negate_etc_arg(e));
                        if (depends_on_exp(a, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_plus_tag:
                case exp_minus_tag:
                case exp_mult_tag:
                case exp_div_tag:
                case exp_rem_tag:
                case exp_and_tag:
                case exp_or_tag:
                case exp_xor_tag:
                case exp_log_and_tag:
                case exp_log_or_tag:
                case exp_lshift_tag:
                case exp_rshift_tag:
                case exp_max_tag:
                case exp_min_tag: {
                        EXP a = DEREF_exp(exp_plus_etc_arg1(e));
                        EXP b = DEREF_exp(exp_plus_etc_arg2(e));
                        if (depends_on_exp(a, pids, use)) {
                                return (1);
                        }
                        if (depends_on_exp(b, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_test_tag: {
                        EXP a = DEREF_exp(exp_test_arg(e));
                        if (depends_on_exp(a, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_compare_tag: {
                        EXP a = DEREF_exp(exp_compare_arg1(e));
                        EXP b = DEREF_exp(exp_compare_arg2(e));
                        if (depends_on_exp(a, pids, use)) {
                                return (1);
                        }
                        if (depends_on_exp(b, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_cast_tag: {
                        EXP a = DEREF_exp(exp_cast_arg(e));
                        if (depends_on_exp(a, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_base_cast_tag: {
                        EXP a = DEREF_exp(exp_base_cast_arg(e));
                        if (depends_on_exp(a, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_dyn_cast_tag: {
                        EXP a = DEREF_exp(exp_dyn_cast_arg(e));
                        if (depends_on_exp(a, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_add_ptr_tag: {
                        EXP a = DEREF_exp(exp_add_ptr_ptr(e));
                        OFFSET off = DEREF_off(exp_add_ptr_off(e));
                        if (depends_on_exp(a, pids, use)) {
                                return (1);
                        }
                        if (depends_on_off(off, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_offset_size_tag: {
                        OFFSET off = DEREF_off(exp_offset_size_off(e));
                        TYPE s = DEREF_type(exp_offset_size_step(e));
                        if (depends_on_off(off, pids, use)) {
                                return (1);
                        }
                        if (depends_on(s, pids)) {
                                return (1);
                        }
                        break;
                }
                case exp_constr_tag: {
                        EXP a = DEREF_exp(exp_constr_call(e));
                        if (depends_on_exp(a, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_destr_tag: {
                        EXP a = DEREF_exp(exp_destr_call(e));
                        if (depends_on_exp(a, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_alloc_tag: {
                        EXP a = DEREF_exp(exp_alloc_call(e));
                        EXP b = DEREF_exp(exp_alloc_init(e));
                        if (depends_on_exp(a, pids, use)) {
                                return (1);
                        }
                        if (depends_on_exp(b, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_dealloc_tag: {
                        EXP a = DEREF_exp(exp_dealloc_term(e));
                        EXP b = DEREF_exp(exp_dealloc_call(e));
                        if (depends_on_exp(a, pids, use)) {
                                return (1);
                        }
                        if (depends_on_exp(b, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_rtti_tag: {
                        EXP a = DEREF_exp(exp_rtti_arg(e));
                        if (depends_on_exp(a, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_rtti_type_tag: {
                        TYPE s = DEREF_type(exp_rtti_type_arg(e));
                        if (depends_on(s, pids)) {
                                return (1);
                        }
                        break;
                }
                case exp_rtti_no_tag: {
                        TYPE s = DEREF_type(exp_rtti_no_arg(e));
                        if (depends_on(s, pids)) {
                                return (1);
                        }
                        break;
                }
                case exp_dynamic_tag: {
                        EXP a = DEREF_exp(exp_dynamic_arg(e));
                        if (depends_on_exp(a, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_aggregate_tag: {
                        LIST(EXP)p = DEREF_list(exp_aggregate_args(e));
                        if (depends_on_exp_list(p, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_initialiser_tag: {
                        LIST(EXP)p = DEREF_list(exp_initialiser_args(e));
                        if (depends_on_exp_list(p, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_nof_tag: {
                        EXP a = DEREF_exp(exp_nof_start(e));
                        EXP b = DEREF_exp(exp_nof_pad(e));
                        EXP c = DEREF_exp(exp_nof_end(e));
                        if (depends_on_exp(a, pids, use)) {
                                return (1);
                        }
                        if (depends_on_exp(b, pids, use)) {
                                return (1);
                        }
                        if (depends_on_exp(c, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_comma_tag: {
                        LIST(EXP) p = DEREF_list(exp_comma_args(e));
                        if (depends_on_exp_list(p, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_set_tag: {
                        EXP a = DEREF_exp(exp_set_arg(e));
                        if (depends_on_exp(a, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_unused_tag: {
                        EXP a = DEREF_exp(exp_unused_arg(e));
                        if (depends_on_exp(a, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_sequence_tag: {
                        LIST(EXP)p = DEREF_list(exp_sequence_first(e));
                        if (depends_on_exp_list(p, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_if_stmt_tag: {
                        EXP c = DEREF_exp(exp_if_stmt_cond(e));
                        EXP a = DEREF_exp(exp_if_stmt_true_code(e));
                        EXP b = DEREF_exp(exp_if_stmt_false_code(e));
                        if (depends_on_exp(c, pids, use)) {
                                return (1);
                        }
                        if (depends_on_exp(a, pids, use)) {
                                return (1);
                        }
                        if (depends_on_exp(b, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_try_block_tag: {
                        EXP a = DEREF_exp(exp_try_block_body(e));
                        if (depends_on_exp(a, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_exception_tag: {
                        EXP a = DEREF_exp(exp_exception_arg(e));
                        if (depends_on_exp(a, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_op_tag: {
                        EXP a = DEREF_exp(exp_op_arg1(e));
                        EXP b = DEREF_exp(exp_op_arg2(e));
                        if (depends_on_exp(a, pids, use)) {
                                return (1);
                        }
                        if (depends_on_exp(b, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_opn_tag: {
                        LIST(EXP) p = DEREF_list(exp_opn_args(e));
                        if (depends_on_exp_list(p, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_location_tag: {
                        EXP a = DEREF_exp(exp_location_arg(e));
                        if (depends_on_exp(a, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case exp_dummy_tag: {
                        EXP a = DEREF_exp(exp_dummy_value(e));
                        if (depends_on_exp(a, pids, use)) {
                                return (1);
                        }
                        break;
                }
                }
        }
        return (0);
}


/*
    DOES AN OFFSET DEPEND ON A TEMPLATE PARAMETER?

    This routine checks whether the offset off depends on one of the
    template parameters pids.
*/

int
depends_on_off(OFFSET off, LIST(IDENTIFIER) pids, int use)
{
        if (!IS_NULL_off(off)) {
                ASSERT(ORDER_off == 13);
                switch (TAG_off(off)) {
                case off_zero_tag: {
                        TYPE t = DEREF_type(off_zero_type(off));
                        if (depends_on(t, pids)) {
                                return (1);
                        }
                        break;
                }
                case off_type_tag: {
                        TYPE t = DEREF_type(off_type_type(off));
                        if (depends_on(t, pids)) {
                                return (1);
                        }
                        break;
                }
                case off_array_tag: {
                        TYPE t = DEREF_type(off_array_type(off));
                        if (depends_on(t, pids)) {
                                return (1);
                        }
                        break;
                }
                case off_extra_tag: {
                        TYPE t = DEREF_type(off_extra_type(off));
                        if (depends_on(t, pids)) {
                                return (1);
                        }
                        break;
                }
#if 0
                case off_base_tag: {
                        GRAPH graph = DEREF_graph(off_base_graph(off));
                        break;
                }
                case off_deriv_tag: {
                        GRAPH graph = DEREF_graph(off_deriv_graph(off));
                        OFFSET direct = DEREF_off(off_deriv_direct(off));
                        OFFSET indirect = DEREF_off(off_deriv_indirect(off));
                        break;
                }
                case off_member_tag: {
                        IDENTIFIER id = DEREF_id(off_member_id(off));
                        break;
                }
#endif
                case off_ptr_mem_tag: {
                        EXP a = DEREF_exp(off_ptr_mem_arg(off));
                        if (depends_on_exp(a, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case off_negate_tag: {
                        OFFSET a = DEREF_off(off_negate_arg(off));
                        if (depends_on_off(a, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case off_plus_tag: {
                        OFFSET a = DEREF_off(off_plus_arg1(off));
                        OFFSET b = DEREF_off(off_plus_arg2(off));
                        if (depends_on_off(a, pids, use)) {
                                return (1);
                        }
                        if (depends_on_off(b, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case off_mult_tag: {
                        OFFSET a = DEREF_off(off_mult_arg1(off));
                        EXP b = DEREF_exp(off_mult_arg2(off));
                        if (depends_on_off(a, pids, use)) {
                                return (1);
                        }
                        if (depends_on_exp(b, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case off_ptr_diff_tag: {
                        EXP a = DEREF_exp(off_ptr_diff_ptr1(off));
                        EXP b = DEREF_exp(off_ptr_diff_ptr2(off));
                        if (depends_on_exp(a, pids, use)) {
                                return (1);
                        }
                        if (depends_on_exp(b, pids, use)) {
                                return (1);
                        }
                        break;
                }
                case off_token_tag: {
                        IDENTIFIER tid = DEREF_id(off_token_tok(off));
                        LIST(TOKEN) args = DEREF_list(off_token_args(off));
                        if (depends_on_param(tid, pids)) {
                                return (2);
                        }
                        if (depends_on_args(args, pids, use, 0)) {
                                return (1);
                        }
                        break;
                }
                }
        }
        return (0);
}


/*
    DOES A TYPE DEPEND ON A TEMPLATE PARAMETER?

    This routine checks whether the type t depends on one of the template
    parameters pids.
*/

int
depends_on(TYPE t, LIST(IDENTIFIER) pids)
{
        if (!IS_NULL_type(t)) {
                ASSERT(ORDER_type == 18);
                switch (TAG_type(t)) {
                case type_ptr_tag:
                case type_ref_tag: {
                        TYPE s = DEREF_type(type_ptr_etc_sub(t));
                        return (depends_on(s, pids));
                }
                case type_ptr_mem_tag: {
                        TYPE s = DEREF_type(type_ptr_mem_sub(t));
                        CLASS_TYPE cr = DEREF_ctype(type_ptr_mem_of(t));
                        TYPE r = DEREF_type(ctype_form(cr));
                        if (depends_on(s, pids)) {
                                return (1);
                        }
                        return (depends_on(r, pids));
                }
                case type_func_tag: {
                        TYPE r = DEREF_type(type_func_ret(t));
                        LIST(TYPE) p = DEREF_list(type_func_mtypes(t));
                        if (depends_on(r, pids)) {
                                return (1);
                        }
                        while (!IS_NULL_list(p)) {
                                TYPE s = DEREF_type(HEAD_list(p));
                                if (depends_on(s, pids)) {
                                        return (1);
                                }
                                p = TAIL_list(p);
                        }
                        break;
                }
                case type_array_tag: {
                        TYPE s = DEREF_type(type_array_sub(t));
                        NAT n = DEREF_nat(type_array_size(t));
                        if (depends_on(s, pids)) {
                                return (1);
                        }
                        return (depends_on_nat(n, pids, 0));
                }
                case type_bitfield_tag: {
                        INT_TYPE it = DEREF_itype(type_bitfield_defn(t));
                        TYPE s = DEREF_type(itype_bitfield_sub(it));
                        NAT n = DEREF_nat(itype_bitfield_size(it));
                        if (depends_on(s, pids)) {
                                return (1);
                        }
                        return (depends_on_nat(n, pids, 0));
                }
                case type_compound_tag: {
                        CLASS_TYPE cs = DEREF_ctype(type_compound_defn(t));
                        IDENTIFIER cid = DEREF_id(ctype_name(cs));
                        return (depends_on_id(cid, pids, 0));
                }
                case type_enumerate_tag: {
                        ENUM_TYPE et = DEREF_etype(type_enumerate_defn(t));
                        IDENTIFIER eid = DEREF_id(etype_name(et));
                        return (depends_on_id(eid, pids, 0));
                }
                case type_token_tag: {
                        IDENTIFIER tid = DEREF_id(type_token_tok(t));
                        LIST(TOKEN) args = DEREF_list(type_token_args(t));
                        if (depends_on_param(tid, pids)) {
                                return (1);
                        }
                        if (depends_on_args(args, pids, 0, 0)) {
                                return (1);
                        }
                        if (IS_id_token(tid)) {
                                TOKEN sort = DEREF_tok(id_token_sort(tid));
                                if (IS_tok_type(sort)) {
                                        BASE_TYPE bt;
                                        bt = DEREF_btype(tok_type_kind(sort));
                                        if (bt & btype_typename) {
                                                /* Allow for typename */
                                                return (depends_on_id(tid, pids,
                                                                      0));
                                        }
                                }
                        }
                        break;
                }
                case type_templ_tag: {
                        int dep;
                        LIST(IDENTIFIER) qids;
                        TYPE s = DEREF_type(type_templ_defn(t));
                        TOKEN sort = DEREF_tok(type_templ_sort(t));
                        qids = DEREF_list(tok_templ_pids(sort));
                        while (!IS_NULL_list(qids)) {
                                /* Suppress template parameters */
                                IDENTIFIER qid = DEREF_id(HEAD_list(qids));
                                DECL_SPEC ds = DEREF_dspec(id_storage(qid));
                                ds |= dspec_ignore;
                                COPY_dspec(id_storage(qid), ds);
                                qids = TAIL_list(qids);
                        }
                        dep = depends_on(s, pids);
                        qids = DEREF_list(tok_templ_pids(sort));
                        while (!IS_NULL_list(qids)) {
                                /* Restore template parameters */
                                IDENTIFIER qid = DEREF_id(HEAD_list(qids));
                                DECL_SPEC ds = DEREF_dspec(id_storage(qid));
                                ds &= ~dspec_ignore;
                                COPY_dspec(id_storage(qid), ds);
                                qids = TAIL_list(qids);
                        }
                        return (dep);
                }
                }
        }
        return (0);
}


/*
    DOES A FUNCTION CALL DEPEND ON A TEMPLATE PARAMETER?

    This routine checks whether the function call 'id ( args )' depends
    on a template parameter.
*/

int
dependent_call(IDENTIFIER id, LIST(EXP) args)
{
        if (in_template_decl) {
                /* Only check in a template declaration */
                LIST(IDENTIFIER) pids = any_templ_param;
                if (depends_on_id(id, pids, 0)) {
                        return (1);
                }
                if (IS_id_function_etc(id)) {
                        while (!IS_NULL_id(id)) {
                                TYPE t = DEREF_type(id_function_etc_type(id));
                                if (depends_on(t, pids)) {
                                        return (1);
                                }
                                id = DEREF_id(id_function_etc_over(id));
                        }
                }
                while (!IS_NULL_list(args)) {
                        EXP a = DEREF_exp(HEAD_list(args));
                        if (!IS_NULL_exp(a)) {
                                /* Check argument type */
                                TYPE t = DEREF_type(exp_type(a));
                                if (depends_on(t, pids)) {
                                        return (1);
                                }
                        }
                        args = TAIL_list(args);
                }
        }
        return (0);
}


/*
    DOES A FUNCTION CAST DEPEND ON A TEMPLATE PARAMETER?

    This routine checks whether the resolution of the overloaded function
    id to the type t depends on a template parameter.
*/

int
dependent_cast(IDENTIFIER id, TYPE t)
{
        if (in_template_decl) {
                /* Only check in a template declaration */
                LIST(IDENTIFIER) pids = any_templ_param;
                if (depends_on_id(id, pids, 0)) {
                        return (1);
                }
                if (depends_on(t, pids)) {
                        return (1);
                }
        }
        return (0);
}


/*
    DOES A CONVERSION DEPEND ON A TEMPLATE PARAMETER?

    This routine checks whether the conversion 't ( args )' depends
    on a template parameter.
*/

int
dependent_conv(TYPE t, LIST(EXP) args)
{
        if (in_template_decl) {
                /* Only check in a template declaration */
                LIST(IDENTIFIER) pids = any_templ_param;
                if (depends_on(t, pids)) {
                        return (1);
                }
                while (!IS_NULL_list(args)) {
                        EXP a = DEREF_exp(HEAD_list(args));
                        if (!IS_NULL_exp(a)) {
                                /* Check argument type */
                                TYPE s = DEREF_type(exp_type(a));
                                if (depends_on(s, pids)) {
                                        return (1);
                                }
                        }
                        args = TAIL_list(args);
                }
        }
        return (0);
}


/*
    DOES AN IDENTIFIER DEPEND ON A TEMPLATE PARAMETER?

    This routine checks whether the identifier id depends on a template
    parameter.
*/

int
dependent_id(IDENTIFIER id)
{
        if (in_template_decl) {
                /* Only check in a template declaration */
                LIST(IDENTIFIER) pids = any_templ_param;
                if (depends_on_id(id, pids, 0)) {
                        return (1);
                }
        }
        return (0);
}


/*
    MARK THE IDENTIFIERS IN AN EXPRESSION AS USED

    This routine marks all the identifiers in the expression e as having
    been used.  This routine is combined with the depends_on functions
    only because they happen to give a convenient tree-walking skeleton.
*/

void
mark_used(EXP e)
{
        if (!suppress_usage) {
                IGNORE depends_on_exp(e, NULL_list(IDENTIFIER), 1);
        }
        return;
}


/*
    FIND AN INJECTED TYPE

    This routine modifies the type t which is injected from a template
    into an enclosing scope (for example, a friend of a template class)
    by qualifying it by copies of any unbound template qualifiers.
*/

TYPE
injected_type(TYPE t, int rec)
{
        IDENTIFIER pid = NULL_id;
        LIST(NAMESPACE)lns = LIST_stack(namespace_stack);
        while (!IS_NULL_list(lns)) {
                NAMESPACE ns = DEREF_nspace(HEAD_list(lns));
                IDENTIFIER id = DEREF_id(nspace_name(ns));
                if (!IS_NULL_id(id)) {
                        if (!EQ_id(id, pid)) {
                                TYPE s = NULL_type;
                                switch (TAG_id(id)) {
                                case id_class_name_tag:
                                case id_class_alias_tag:
                                        s = DEREF_type(id_class_name_etc_defn(id));
                                        break;
                                case id_function_tag:
                                case id_mem_func_tag:
                                case id_stat_mem_func_tag:
                                        s = DEREF_type(id_function_etc_type(id));
                                        break;
                                }

                                if (!IS_NULL_type(s) && IS_type_templ(s)) {
                                        LIST(IDENTIFIER)pids;
                                        TOKEN sort =
                                            DEREF_tok(type_templ_sort(s));
                                        pids = DEREF_list(tok_templ_pids(sort));
                                        if (depends_on(t, pids)) {
                                                t = rename_templ_params(sort, t,
                                                                        rec);
                                        }
                                }
                                pid = id;
                        }
                }
                lns = TAIL_list(lns);
        }
        return (t);
}


/*
    DUMMY TEMPLATE PARAMETER TYPE

    This variable gives a dummy template parameter type which allows the
    propagation of types dependent in some non-obvious fashion on some
    template parameter.
*/

TYPE type_templ_param;


/*
    INITIALISE TEMPLATE ROUTINES

    This routine initialises the template routines.  In particular it
    initialises the dummy template parameter type.
*/

void
init_templates(void)
{
        string s = ustrlit("<type>");
        unsigned long h = hash(s);
        HASHID nm = lookup_name(s, h, 0, lex_identifier);
        IDENTIFIER id = DEREF_id(hashid_id(nm));
        LIST(TOKEN) args = NULL_list(TOKEN);
        TYPE t = make_dummy_type(crt_namespace, id, btype_template, args);
        type_templ_param = t;
        CONS_id(NULL_id, NULL_list(IDENTIFIER), any_templ_param);
        CONS_id(NULL_id, NULL_list(IDENTIFIER), any_token_param);
        return;
}