Subversion Repositories tendra.SVN

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
                 Crown Copyright (c) 1997, 1998
    
    This TenDRA(r) Computer Program is subject to Copyright
    owned by the United Kingdom Secretary of State for Defence
    acting through the Defence Evaluation and Research Agency
    (DERA).  It is made available to Recipients with a
    royalty-free licence for its use, reproduction, transfer
    to other parties and amendment for any purpose not excluding
    product development provided that any such use et cetera
    shall be deemed to be acceptance of the following conditions:-
    
        (1) Its Recipients shall ensure that this Notice is
        reproduced upon any copies or amended versions of it;
    
        (2) Any amended version of it shall be clearly marked to
        show both the nature of and the organisation responsible
        for the relevant amendment or amendments;
    
        (3) Its onward transfer from a recipient to another
        party shall be deemed to be that party's acceptance of
        these conditions;
    
        (4) DERA gives no warranty or assurance as to its
        quality or suitability for any purpose and DERA accepts
        no liability whatsoever in relation to any use to which
        it may be put.
*/


#include "config.h"
#include "c_types.h"
#include "ctype_ops.h"
#include "exp_ops.h"
#include "graph_ops.h"
#include "hashid_ops.h"
#include "id_ops.h"
#include "inst_ops.h"
#include "member_ops.h"
#include "nspace_ops.h"
#include "tok_ops.h"
#include "type_ops.h"
#include "error.h"
#include "catalog.h"
#include "option.h"
#include "allocate.h"
#include "basetype.h"
#include "check.h"
#include "chktype.h"
#include "class.h"
#include "compile.h"
#include "construct.h"
#include "copy.h"
#include "declare.h"
#include "derive.h"
#include "destroy.h"
#include "dump.h"
#include "file.h"
#include "function.h"
#include "hash.h"
#include "identifier.h"
#include "instance.h"
#include "namespace.h"
#include "operator.h"
#include "overload.h"
#include "predict.h"
#include "redeclare.h"
#include "template.h"
#include "tokdef.h"
#include "token.h"
static void copy_template PROTO_S ( ( IDENTIFIER, int ) ) ;


/*
    LIST OF ALL TEMPLATE INSTANCES

    All template instances are formed into a linked list by their prev
    field (most recent first).
*/

INSTANCE all_instances = NULL_inst ;


/*
    JOIN TWO LISTS OF TEMPLATE ARGUMENTS

    This routine copies the template arguments p to the start of the list q.
*/

static LIST ( TOKEN ) add_template_args
    PROTO_N ( ( p, q ) )
    PROTO_T ( LIST ( TOKEN ) p X LIST ( TOKEN ) q )
{
    if ( !IS_NULL_list ( p ) ) {
        TOKEN tok = DEREF_tok ( HEAD_list ( p ) ) ;
        tok = expand_sort ( tok, -1, 1 ) ;
        p = TAIL_list ( p ) ;
        q = add_template_args ( p, q ) ;
        CONS_tok ( tok, q, q ) ;
    }
    return ( q ) ;
}


/*
    CREATE A PARTIAL FUNCTION INSTANCE

    This routine is a special case of instance_func which allows for the
    case where some of the template arguments are given explicitly and
    others are deduced.  id gives the template with any explicit arguments
    already bound, and args gives the implicitly deduced arguments which
    are bound to the parameters pids.
*/

static IDENTIFIER inst_func_deduce
    PROTO_N ( ( id, pids, args, d ) )
    PROTO_T ( IDENTIFIER id X LIST ( IDENTIFIER ) pids X
              LIST ( TOKEN ) args X int d )
{
    IDENTIFIER fid = DEREF_id ( id_alias ( id ) ) ;
    TYPE form = DEREF_type ( id_function_etc_form ( fid ) ) ;
    if ( !IS_NULL_type ( form ) && IS_type_token ( form ) ) {
        LIST ( TOKEN ) dargs ;
        fid = DEREF_id ( type_token_tok ( form ) ) ;
        dargs = DEREF_list ( type_token_args ( form ) ) ;
        if ( !IS_NULL_list ( dargs ) ) {
            /* Partially specified template */
            TYPE s = DEREF_type ( id_function_etc_type ( fid ) ) ;
            TOKEN sort = DEREF_tok ( type_templ_sort ( s ) ) ;
            restore_token_args ( pids, d ) ;
            args = add_template_args ( dargs, args ) ;
            pids = DEREF_list ( tok_templ_pids ( sort ) ) ;
            d = save_token_args ( pids, args ) ;
            fid = inst_func_deduce ( fid, pids, args, d ) ;
            return ( fid ) ;
        }
    }
    fid = instance_func ( fid, args, 1, 0 ) ;
    restore_token_args ( pids, d ) ;
    return ( fid ) ;
}


/*
    DEDUCE A BASE CLASS

    This routine returns a list of all the base classes of gr which can
    be deduced to be equal to the class ct.
*/

static LIST ( GRAPH ) deduce_graph
    PROTO_N ( ( gr, ct, pids ) )
    PROTO_T ( GRAPH gr X CLASS_TYPE ct X LIST ( IDENTIFIER ) pids )
{
    LIST ( GRAPH ) pr = NULL_list ( GRAPH ) ;
    CLASS_TYPE cr = DEREF_ctype ( graph_head ( gr ) ) ;
    DECL_SPEC acc = DEREF_dspec ( graph_access ( gr ) ) ;

    /* Check for equality */
    int d = save_token_args ( pids, NULL_list ( TOKEN ) ) ;
    if ( eq_ctype ( cr, ct ) ) CONS_graph ( gr, pr, pr ) ;
    restore_token_args ( pids, d ) ;

    /* Examine base classes */
    if ( acc & dspec_main ) {
        LIST ( GRAPH ) br = DEREF_list ( graph_tails ( gr ) ) ;
        while ( !IS_NULL_list ( br ) ) {
            GRAPH gs = DEREF_graph ( HEAD_list ( br ) ) ;
            LIST ( GRAPH ) ps = deduce_graph ( gs, ct, pids ) ;
            while ( !IS_NULL_list ( ps ) ) {
                /* Add deduced graphs to list */
                LIST ( GRAPH ) pt = pr ;
                DESTROY_CONS_graph ( destroy, gs, ps, ps ) ;
                while ( !IS_NULL_list ( pt ) ) {
                    /* Search for previous deductions */
                    GRAPH gt = DEREF_graph ( HEAD_list ( pt ) ) ;
                    if ( eq_graph ( gt, gs ) ) break ;
                    pt = TAIL_list ( pt ) ;
                }
                if ( IS_NULL_list ( pt ) ) {
                    /* Not previously deduced */
                    CONS_graph ( gs, pr, pr ) ;
                }
            }
            br = TAIL_list ( br ) ;
        }
    }
    return ( pr ) ;
}


/*
    DEDUCE A DERIVED FUNCTION TEMPLATE PARAMETER TYPE

    This routine attempts to deduce the function template parameter type t
    from the corresponding function argument type s in the case where both
    are classes.  s can be deduced to be a derived class of a template class
    rather than having to be equal to t.
*/

static int deduce_derive
    PROTO_N ( ( t, s, pids ) )
    PROTO_T ( TYPE t X TYPE s X LIST ( IDENTIFIER ) pids )
{
    CLASS_TYPE cs = DEREF_ctype ( type_compound_defn ( s ) ) ;
    CLASS_INFO ci = DEREF_cinfo ( ctype_info ( cs ) ) ;
    if ( ci & cinfo_templ_base ) {
        /* Only check if s has a template base class */
        CLASS_TYPE ct = DEREF_ctype ( type_compound_defn ( t ) ) ;
        GRAPH gs = DEREF_graph ( ctype_base ( cs ) ) ;
        LIST ( GRAPH ) ps = deduce_graph ( gs, ct, pids ) ;
        if ( !IS_NULL_list ( ps ) ) {
            /* Deduction succeeded */
            DESTROY_CONS_graph ( destroy, gs, ps, ps ) ;
            if ( IS_NULL_list ( ps ) ) {
                /* Unambiguous deduction */
                TYPE fs ;
                cs = DEREF_ctype ( graph_head ( gs ) ) ;
                fs = DEREF_type ( ctype_form ( cs ) ) ;
                if ( !IS_NULL_type ( fs ) && IS_type_token ( fs ) ) {
                    IDENTIFIER fid = DEREF_id ( type_token_tok ( fs ) ) ;
                    if ( !IS_id_token ( fid ) ) {
                        /* cs is a template class */
                        return ( eq_ctype ( ct, cs ) ) ;
                    }
                }
                return ( 0 ) ;
            }
            DESTROY_list ( ps, SIZE_graph ) ;
        }
    }
    return ( 0 ) ;
}


/*
    DEDUCE A FUNCTION TEMPLATE PARAMETER TYPE

    This routine attempts to deduce the function template parameter type t
    from the corresponding function argument type s.  Qualification
    conversions and other inexact type conversions are allowed.  The
    routine returns true if the deduction was successful.
*/

static int deduce_param
    PROTO_N ( ( t, s, pids ) )
    PROTO_T ( TYPE t X TYPE s X LIST ( IDENTIFIER ) pids )
{
    int go = 1 ;
    int depth = 0 ;
    int all_const = 1 ;
    do {
        unsigned nt = TAG_type ( t ) ;
        unsigned ns = TAG_type ( s ) ;
        CV_SPEC qt = find_cv_qual ( t ) ;
        CV_SPEC qs = find_cv_qual ( s ) ;
        qt &= cv_qual ;
        qs &= cv_qual ;
        if ( qt != qs ) {
            /* Allow for qualification conversions */
            if ( ( qs & ~qt ) || !all_const ) {
                return ( eq_type ( t, s ) ) ;
            }
        }
        if ( depth && !( qt & cv_const ) ) all_const = 0 ;
        if ( nt == ns ) {
            switch ( nt ) {
                case type_ptr_tag :
                case type_ref_tag : {
                    /* Continue checking pointer types */
                    t = DEREF_type ( type_ptr_etc_sub ( t ) ) ;
                    s = DEREF_type ( type_ptr_etc_sub ( s ) ) ;
                    depth++ ;
                    break ;
                }
                case type_ptr_mem_tag : {
                    /* Continue checking pointer to member types */
                    CLASS_TYPE ct = DEREF_ctype ( type_ptr_mem_of ( t ) ) ;
                    CLASS_TYPE cs = DEREF_ctype ( type_ptr_mem_of ( s ) ) ;
                    if ( !eq_ctype ( ct, cs ) ) return ( 0 ) ;
                    t = DEREF_type ( type_ptr_mem_sub ( t ) ) ;
                    s = DEREF_type ( type_ptr_mem_sub ( s ) ) ;
                    depth += 2 ;
                    break ;
                }
                case type_compound_tag : {
                    /* Allow derived template classes */
                    if ( depth < 2 && deduce_derive ( t, s, pids ) ) {
                        return ( 1 ) ;
                    }
                    go = 0 ;
                    break ;
                }
                default : {
                    /* Now check for equality */
                    go = 0 ;
                    break ;
                }
            }
        } else {
            /* Now check for equality */
            go = 0 ;
        }
    } while ( go ) ;
    return ( eq_type_unqual ( t, s ) ) ;
}


/*
    PERFORM ARGUMENT DEDUCTION FOR A FUNCTION TEMPLATE

    This routine performs argument deduction for the call of the function
    template id with the arguments args.  Qualification conversions and
    other inexact deductions are allowed if qual is true.  The null
    identifier is returned to indicate that argument deduction fails.
    The instance corresponding to the deduced arguments is only created
    and returned if create is true.  If force is true then type deduction
    continues after a failure has occurred.
*/

IDENTIFIER deduce_args
    PROTO_N ( ( id, args, qual, force, create, err ) )
    PROTO_T ( IDENTIFIER id X LIST ( EXP ) args X int qual X int force X
              int create X ERROR *err )
{
    IDENTIFIER rid = NULL_id ;
    TYPE s = DEREF_type ( id_function_etc_type ( id ) ) ;
    TYPE r = DEREF_type ( type_templ_defn ( s ) ) ;
    if ( IS_type_func ( r ) ) {
        int d ;
        int ok = 2 ;
        int started = 0 ;
        ERROR err2 = NULL_err ;
        TOKEN sort = DEREF_tok ( type_templ_sort ( s ) ) ;
        LIST ( TYPE ) pars = DEREF_list ( type_func_mtypes ( r ) ) ;
        LIST ( IDENTIFIER ) pids = DEREF_list ( tok_templ_pids ( sort ) ) ;
        if ( err == KILL_err ) err = &err2 ;
        force_template++ ;
        d = save_token_args ( pids, NULL_list ( TOKEN ) ) ;

        /* Scan through arguments */
        while ( !IS_NULL_list ( pars ) && !IS_NULL_list ( args ) ) {
            int dep = 1 ;
            TYPE p = DEREF_type ( HEAD_list ( pars ) ) ;
            EXP a = DEREF_exp ( HEAD_list ( args ) ) ;
            if ( qual ) dep = depends_on ( p, pids ) ;
            if ( !IS_NULL_exp ( a ) && dep ) {
                int eq ;
                TYPE q ;
                int d2 = 0 ;
                ERROR ferr = NULL_err ;
                if ( started ) {
                    /* Each argument deduction is independent */
                    d2 = save_token_args ( pids, NULL_list ( TOKEN ) ) ;
                }
                if ( IS_type_ref ( p ) ) {
                    /* Use referenced type for type deduction */
                    p = DEREF_type ( type_ref_sub ( p ) ) ;
                    a = resolve_cast ( p, a, &ferr, 0, 0, pids ) ;
                    if ( IS_NULL_exp ( a ) ) {
                        q = redef_type ;
                    } else {
                        q = DEREF_type ( exp_type ( a ) ) ;
                    }
                } else {
                    /* Convert argument type */
                    a = resolve_cast ( p, a, &ferr, 0, 0, pids ) ;
                    if ( IS_NULL_exp ( a ) ) {
                        q = redef_type ;
                    } else {
                        q = DEREF_type ( exp_type ( a ) ) ;
                        switch ( TAG_type ( q ) ) {
                            case type_func_tag : {
                                /* Function to pointer */
                                MAKE_type_ptr ( cv_none, q, q ) ;
                                break ;
                            }
                            case type_array_tag : {
                                /* Array to pointer */
                                q = DEREF_type ( type_array_sub ( q ) ) ;
                                MAKE_type_ptr ( cv_none, q, q ) ;
                                break ;
                            }
                            case type_bitfield_tag : {
                                /* Promote bitfields */
                                q = promote_type ( q ) ;
                                break ;
                            }
                            default : {
                                /* Ignore type qualifiers */
                                q = qualify_type ( q, cv_none, 0 ) ;
                                break ;
                            }
                        }
                    }
                }
                if ( qual ) {
                    /* Allow qualification conversions */
                    eq = deduce_param ( p, q, pids ) ;
                } else {
                    /* Require exact conversion */
                    eq = eq_type ( p, q ) ;
                }
                if ( !eq ) {
                    /* Type deduction should be identical */
                    ok = force ;
                }
                if ( started ) {
                    /* Combine argument deductions */
                    if ( !merge_token_args ( pids, d2, qual ) ) ok = force ;
                }
                started = 1 ;
                if ( !ok ) break ;
            }
            args = TAIL_list ( args ) ;
            pars = TAIL_list ( pars ) ;
        }

        /* Check the combined results */
        if ( ok ) {
            LIST ( TOKEN ) targs = make_token_args ( id, pids, err ) ;
            if ( ok != 2 ) {
                /* Report unviable resolution */
                add_error ( err, ERR_over_match_viable_none ( id ) ) ;
            }
            if ( IS_NULL_err ( *err ) || force ) {
                /* Successful deduction */
                if ( create ) {
                    check_deduced_args ( id, pids, targs ) ;
                    rid = inst_func_deduce ( id, pids, targs, d ) ;
                    force_template-- ;
                    return ( rid ) ;
                }
                rid = id ;
            }
            DESTROY_list ( targs, SIZE_tok ) ;
        }
        restore_token_args ( pids, d ) ;
        force_template-- ;
    }
    UNUSED ( qual ) ;
    return ( rid ) ;
}


/*
    SPECIALISE A FUNCTION TEMPLATE TO A FUNCTION TYPE

    This routine constructs checks whether an instance of the function
    template id of type t exists.  The null identifier is returned to
    indicate that no such instance exists.  peq is as in resolve_func.
*/

IDENTIFIER deduce_func
    PROTO_N ( ( id, t, peq ) )
    PROTO_T ( IDENTIFIER id X TYPE t X int *peq )
{
    TYPE s = DEREF_type ( id_function_etc_type ( id ) ) ;
    if ( IS_type_templ ( s ) ) {
        /* Template function */
        int d ;
        int eq = 0 ;
        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 ) ) ;
        force_template++ ;
        d = save_token_args ( pids, NULL_list ( TOKEN ) ) ;
        if ( IS_type_func ( r ) && IS_type_func ( t ) ) {
            eq = eq_func_type ( r, t, 1, 0 ) ;
        } else {
            int cmp = eq_type ( r, t ) ;
            if ( cmp == 1 || cmp == 2 ) eq = 3 ;
        }
        if ( eq >= 2 ) {
            /* Types match - form instance */
            ERROR err = NULL_err ;
            LIST ( TOKEN ) args = make_token_args ( id, pids, &err ) ;
            if ( IS_NULL_err ( err ) ) {
                /* Successful deduction */
                IDENTIFIER rid ;
                check_deduced_args ( id, pids, args ) ;
                rid = inst_func_deduce ( id, pids, args, d ) ;
                force_template-- ;
                *peq = eq ;
                return ( rid ) ;
            }
            destroy_error ( err, 1 ) ;
        }
        restore_token_args ( pids, d ) ;
        force_template-- ;
    } else {
        /* Simple function */
        int eq = eq_func_type ( s, t, 1, 0 ) ;
        if ( eq >= 2 ) {
            *peq = eq ;
            return ( id ) ;
        }
    }
    *peq = 0 ;
    return ( NULL_id ) ;
}


/*
    DEDUCE A CONVERSION FUNCTION TYPE

    This routine is used to find the type of the specialisation of a
    template conversion function of type t which may be used for a
    conversion to type r.  If no such specialisation exists the null
    type is returned.
*/

TYPE deduce_conv
    PROTO_N ( ( t, r ) )
    PROTO_T ( TYPE t X TYPE r )
{
    if ( IS_type_templ ( t ) ) {
        int d ;
        TOKEN sort = DEREF_tok ( type_templ_sort ( t ) ) ;
        LIST ( IDENTIFIER ) pids = DEREF_list ( tok_templ_pids ( sort ) ) ;
        force_template++ ;
        d = save_token_args ( pids, NULL_list ( TOKEN ) ) ;
        t = DEREF_type ( type_templ_defn ( t ) ) ;
        t = deduce_conv ( t, r ) ;
        restore_token_args ( pids, d ) ;
        force_template-- ;
    } else {
        int eq ;
        CV_SPEC cv ;
        TYPE s = DEREF_type ( type_func_ret ( t ) ) ;
        if ( IS_type_ref ( s ) && !IS_type_ref ( r ) ) {
            s = DEREF_type ( type_ref_sub ( s ) ) ;
        }
        cv = DEREF_cv ( type_qual ( r ) ) ;
        s = qualify_type ( s, cv, 0 ) ;
        eq = eq_type ( s, r ) ;
        if ( eq == 1 || eq == 2 ) {
            /* Match found */
            t = expand_type ( t, 2 ) ;
        } else {
            /* No match found */
            t = NULL_type ;
        }
    }
    return ( t ) ;
}


/*
    FIND AN UNDERLYING TEMPLATE

    This routine finds the underlying form for the template application id.
    If this is an undefined class then pi is set to true.
*/

TYPE find_form
    PROTO_N ( ( id, pi ) )
    PROTO_T ( IDENTIFIER id X int *pi )
{
    TYPE t = NULL_type ;
    if ( !IS_NULL_id ( id ) ) {
        switch ( TAG_id ( id ) ) {
            case id_class_name_tag : {
                /* Template classes */
                CLASS_TYPE ct ;
                CLASS_INFO ci ;
                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 ) ) ;
                ci = DEREF_cinfo ( ctype_info ( ct ) ) ;
                if ( !( ci & cinfo_defined ) ) *pi = 1 ;
                t = DEREF_type ( ctype_form ( ct ) ) ;
                break ;
            }
            case id_function_tag :
            case id_mem_func_tag :
            case id_stat_mem_func_tag : {
                /* Template functions */
                t = DEREF_type ( id_function_etc_form ( id ) ) ;
                break ;
            }
            case id_stat_member_tag : {
                /* Static data members of template classes */
                EXP d = DEREF_exp ( id_stat_member_term ( id ) ) ;
                if ( !IS_NULL_exp ( d ) && IS_exp_paren ( d ) ) {
                    t = DEREF_type ( exp_type ( d ) ) ;
                }
                break ;
            }
        }
    }
    return ( t ) ;
}


/*
    FIND THE INSTANCES FOR A TEMPLATE

    This routine returns the list of all instances for the template tid.
*/

static INSTANCE find_templ_apps
    PROTO_N ( ( tid ) )
    PROTO_T ( IDENTIFIER tid )
{
    TYPE s ;
    TOKEN sort ;
    INSTANCE apps ;
    if ( IS_id_class_name_etc ( tid ) ) {
        s = DEREF_type ( id_class_name_etc_defn ( tid ) ) ;
    } else {
        s = DEREF_type ( id_function_etc_type ( tid ) ) ;
    }
    sort = DEREF_tok ( type_templ_sort ( s ) ) ;
    apps = DEREF_inst ( tok_templ_apps ( sort ) ) ;
    return ( apps ) ;
}


/*
    FIND A TEMPLATE INSTANCE

    This routine searches for a previous instance of the template tid
    of sort tok with the template arguments args.
*/

static IDENTIFIER find_instance
    PROTO_N ( ( tid, tok, args, def ) )
    PROTO_T ( IDENTIFIER tid X TOKEN tok X LIST ( TOKEN ) args X int def )
{
    INSTANCE apps ;
    int fs = force_tokdef ;
    int ft = force_template ;
    force_tokdef = 0 ;
    force_template = 0 ;
    apps = DEREF_inst ( tok_templ_apps ( tok ) ) ;
    while ( !IS_NULL_inst ( apps ) ) {
        DECL_SPEC acc = DEREF_dspec ( inst_templ_access ( apps ) ) ;
        if ( !( acc & dspec_alias ) ) {
            IDENTIFIER fid ;
            LIST ( TOKEN ) fargs ;
            TYPE form = DEREF_type ( inst_form ( apps ) ) ;
            while ( IS_type_templ ( form ) ) {
                form = DEREF_type ( type_templ_defn ( form ) ) ;
            }
            fid = DEREF_id ( type_token_tok ( form ) ) ;
            fargs = DEREF_list ( type_token_args ( form ) ) ;
            if ( eq_token_args ( tid, fid, args, fargs ) ) {
                /* Match found */
                IDENTIFIER id = DEREF_id ( inst_templ_id ( apps ) ) ;
                if ( def ) {
                    /* Mark instance as used */
                    acc |= dspec_used ;
                    COPY_dspec ( inst_templ_access ( apps ), acc ) ;
                }
                force_template = ft ;
                force_tokdef = fs ;
                return ( id ) ;
            }
        }
        apps = DEREF_inst ( inst_next ( apps ) ) ;
    }
    force_template = ft ;
    force_tokdef = fs ;
    return ( NULL_id ) ;
}


/*
    VALIDATE A SET OF TEMPLATE ARGUMENTS

    This routine performs a final validation for the template arguments
    args for the template id of sort sort.
*/

static void valid_template_args
    PROTO_N ( ( id, sort, args ) )
    PROTO_T ( IDENTIFIER id X TOKEN sort X LIST ( TOKEN ) args )
{
    LIST ( IDENTIFIER ) pids = DEREF_list ( tok_templ_pids ( sort ) ) ;
    while ( !IS_NULL_list ( pids ) && !IS_NULL_list ( args ) ) {
        TOKEN arg = DEREF_tok ( HEAD_list ( args ) ) ;
        IDENTIFIER pid = DEREF_id ( HEAD_list ( pids ) ) ;
        TOKEN par = DEREF_tok ( id_token_sort ( pid ) ) ;
        unsigned kind = TAG_tok ( par ) ;
        if ( !IS_NULL_tok ( arg ) && TAG_tok ( arg ) == kind ) {
            if ( kind == tok_type_tag ) {
                /* Check template types */
                TYPE t = DEREF_type ( tok_type_value ( arg ) ) ;
                BASE_TYPE bt = DEREF_btype ( tok_type_kind ( par ) ) ;
                bt &= btype_named ;
                if ( bt != btype_none ) {
                    /* Check elaborated template type */
                    int ok = 0 ;
                    unsigned tag = TAG_type ( t ) ;
                    if ( bt == btype_enum ) {
                        if ( tag == type_enumerate_tag ) ok = 1 ;
                    } else {
                        if ( tag == type_compound_tag ) {
                            CLASS_TYPE ct ;
                            BASE_TYPE key ;
                            ct = DEREF_ctype ( type_compound_defn ( t ) ) ;
                            key = find_class_key ( ct ) ;
                            ok = equal_key ( bt, key ) ;
                        }
                    }
                    if ( !ok && tag != type_error_tag ) {
                        /* Report type mismatch */
                        ERROR err = ERR_temp_res_key ( bt, pid, id, t ) ;
                        report ( crt_loc, err ) ;
                    }
                }
                break ;
            }
        }
        pids = TAIL_list ( pids ) ;
        args = TAIL_list ( args ) ;
    }
    return ;
}


/*
    FIND THE MOST SPECIALISED OF A LIST OF SPECIALISATIONS

    This routine forms the main body of body_match.  It runs a tournament
    to find the most specialised of the template specialisations apps.
    Note that the result is not necessarily more specialised than all
    the other elements of apps, but if there is such a most specialised
    element then it will be the winner of this tournament.
*/

static INSTANCE best_match_aux
    PROTO_N ( ( apps ) )
    PROTO_T ( LIST ( INSTANCE ) apps )
{
    int cmp ;
    TYPE t, s ;
    INSTANCE a, b ;
    if ( IS_NULL_list ( apps ) ) return ( NULL_inst ) ;
    a = DEREF_inst ( HEAD_list ( apps ) ) ;
    b = best_match_aux ( TAIL_list ( apps ) ) ;
    if ( IS_NULL_inst ( b ) ) return ( a ) ;
    t = DEREF_type ( inst_form ( a ) ) ;
    s = DEREF_type ( inst_form ( b ) ) ;
    cmp = eq_type ( t, s ) ;
    if ( cmp == 2 ) return ( b ) ;
    if ( cmp == 3 ) return ( a ) ;
    return ( NULL_inst ) ;
}


/*
    FIND THE MOST SPECIALISED OF A LIST OF SPECIALISATIONS

    This routine finds the most specialised of the template specialisations
    apps.  It returns the null instance if there is no match or the result
    is ambiguous.
*/

static INSTANCE best_match
    PROTO_N ( ( apps ) )
    PROTO_T ( LIST ( INSTANCE ) apps )
{
    INSTANCE a = best_match_aux ( apps ) ;
    if ( !IS_NULL_inst ( a ) && LENGTH_list ( apps ) > 2 ) {
        TYPE t = DEREF_type ( inst_form ( a ) ) ;
        while ( !IS_NULL_list ( apps ) ) {
            INSTANCE b = DEREF_inst ( HEAD_list ( apps ) ) ;
            if ( !EQ_inst ( b, a ) ) {
                TYPE s = DEREF_type ( inst_form ( b ) ) ;
                int cmp = eq_type ( t, s ) ;
                if ( cmp != 3 ) {
                    /* a is not more specialised than b */
                    a = NULL_inst ;
                    break ;
                }
            }
            apps = TAIL_list ( apps ) ;
        }
    }
    return ( a ) ;
}


/*
    DOES A TEMPLATE CLASS INSTANCE SPECIALISE A MEMBER?

    This routine checks whether the template class instance app contains
    a specialisation of the member mid.
*/

static int specialise_member
    PROTO_N ( ( app, mid ) )
    PROTO_T ( INSTANCE app X IDENTIFIER mid )
{
    if ( !IS_NULL_id ( mid ) ) {
        LIST ( IDENTIFIER ) mems = DEREF_list ( inst_templ_mems ( app ) ) ;
        while ( !IS_NULL_list ( mems ) ) {
            IDENTIFIER nid = DEREF_id ( HEAD_list ( mems ) ) ;
            if ( EQ_id ( nid, mid ) ) return ( 1 ) ;
            mems = TAIL_list ( mems ) ;
        }
    }
    return ( 0 ) ;
}


/*
    FIND THE MOST SPECIALISED TEMPLATE MATCHING AN INSTANCE

    This routine returns the most specialised template specialisation
    from the list apps which contains a specialisation of the member mid
    and matches the instance form.  If there is no such specialisation
    or the result is ambiguous then the null instance is returned.
*/

static INSTANCE match_form
    PROTO_N ( ( app, form, mid ) )
    PROTO_T ( INSTANCE app X TYPE form X IDENTIFIER mid )
{
    INSTANCE best = NULL_inst ;
    LIST ( INSTANCE ) match = NULL_list ( INSTANCE ) ;
    force_template++ ;
    while ( !IS_NULL_inst ( app ) ) {
        DECL_SPEC acc = DEREF_dspec ( inst_templ_access ( app ) ) ;
        if ( !( acc & ( dspec_alias | dspec_main ) ) ) {
            if ( ( acc & dspec_extern ) || specialise_member ( app, mid ) ) {
                TYPE prev = DEREF_type ( inst_form ( app ) ) ;
                int cmp = eq_type ( prev, form ) ;
                if ( cmp == 1 || cmp == 2 ) {
                    /* Matches specialisation */
                    CONS_inst ( app, match, match ) ;
                }
            }
        }
        app = DEREF_inst ( inst_next ( app ) ) ;
    }
    if ( !IS_NULL_list ( match ) ) {
        /* Determine most specialised match */
        best = best_match ( match ) ;
        if ( IS_NULL_inst ( best ) ) {
            /* Ambiguous specialisation */
            report ( crt_loc, ERR_temp_class_spec_ambig ( form ) ) ;
        } else {
            /* Unambiguous specialisation */
            IDENTIFIER bid = DEREF_id ( inst_templ_id ( best ) ) ;
            report ( crt_loc, ERR_temp_class_spec_match ( bid ) ) ;
        }
        DESTROY_list ( match, SIZE_inst ) ;
    }
    force_template-- ;
    return ( best ) ;
}


/*
    DEDUCE ARGUMENTS FOR TEMPLATE SPECIALISATION

    This routine deduces the arguments required for the template
    specialisation spec to instantiate the matching instance form.
*/

static TYPE specialise_args
    PROTO_N ( ( spec, form ) )
    PROTO_T ( INSTANCE spec X TYPE form )
{
    TYPE s = DEREF_type ( inst_form ( spec ) ) ;
    if ( IS_type_templ ( s ) ) {
        int d ;
        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 ) ) ;
        force_template++ ;
        d = save_token_args ( pids, NULL_list ( TOKEN ) ) ;
        eq = eq_type ( r, form ) ;
        if ( eq == 1 || eq == 2 ) {
            /* Argument deduction successful */
            ERROR err = NULL_err ;
            IDENTIFIER id = DEREF_id ( inst_templ_id ( spec ) ) ;
            LIST ( TOKEN ) args = make_token_args ( id, pids, &err ) ;
            if ( IS_NULL_err ( err ) ) {
                /* Successful deduction */
                MAKE_type_token ( cv_none, id, args, form ) ;
                COPY_inst ( type_token_app ( form ), spec ) ;
            } else {
                destroy_error ( err, 1 ) ;
            }
        }
        restore_token_args ( pids, d ) ;
        force_template-- ;
    } else {
        IDENTIFIER id = DEREF_id ( inst_templ_id ( spec ) ) ;
        MAKE_type_token ( cv_none, id, NULL_list ( TOKEN ), form ) ;
    }
    return ( form ) ;
}


/*
    MATCH A TEMPLATE SPECIALISATION

    This routine finds the template specialisation which best matches the
    template instance given by form.  If mid is the null identifier then
    only explicit specialisations are considered.  Otherwise any
    specialisation which specialises the member mid is considered.
*/

static TYPE specialise_form
    PROTO_N ( ( form, mid ) )
    PROTO_T ( TYPE form X IDENTIFIER mid )
{
    if ( !IS_NULL_type ( form ) && IS_type_token ( form ) ) {
        IDENTIFIER tid = DEREF_id ( type_token_tok ( form ) ) ;
        INSTANCE app = DEREF_inst ( type_token_app ( form ) ) ;
        if ( !IS_NULL_inst ( app ) ) {
            TYPE spec = DEREF_type ( inst_templ_spec ( app ) ) ;
            if ( IS_NULL_type ( spec ) || !IS_NULL_id ( mid ) ) {
                /* Not previously determined */
                DECL_SPEC acc = DEREF_dspec ( inst_templ_access ( app ) ) ;
                if ( acc & dspec_explicit ) {
                    /* Explicit specialisation */
                    IDENTIFIER id = DEREF_id ( inst_templ_id ( app ) ) ;
                    LIST ( TOKEN ) args = NULL_list ( TOKEN ) ;
                    MAKE_type_token ( cv_none, id, args, spec ) ;
                    COPY_inst ( type_token_app ( spec ), app ) ;
                } else if ( acc & dspec_instance ) {
                    /* Find matching partial specialisations */
                    INSTANCE best = find_templ_apps ( tid ) ;
                    best = match_form ( best, form, mid ) ;
                    if ( !IS_NULL_inst ( best ) ) {
                        /* Use matching specialisation */
                        spec = specialise_args ( best, form ) ;
                    } else {
                        /* Use primary form */
                        spec = form ;
                    }
                }
                if ( IS_NULL_id ( mid ) ) {
                    /* Record best explicit match */
                    COPY_type ( inst_templ_spec ( app ), spec ) ;
                }
            }
            if ( !IS_NULL_type ( spec ) ) form = spec ;
        }
    }
    return ( form ) ;
}


/*
    FIND THE COPIED VERSION OF A CLASS MEMBER

    This routine is identical to find_copied except that it allows for
    template instances when type is not 2.
*/

static IDENTIFIER find_copied_member
    PROTO_N ( ( cid, id, res, type ) )
    PROTO_T ( IDENTIFIER cid X IDENTIFIER id X int res X int type )
{
    if ( type != 2 ) {
        int undef = 0 ;
        TYPE form = find_form ( id, &undef ) ;
        if ( !IS_NULL_type ( form ) && IS_type_token ( form ) ) {
            /* Template instance */
            IDENTIFIER tid = DEREF_id ( type_token_tok ( form ) ) ;
            LIST ( TOKEN ) args = DEREF_list ( type_token_args ( form ) ) ;
            tid = find_copied ( cid, tid, 1 ) ;
            id = apply_template ( tid, args, 0, 0 ) ;
            return ( id ) ;
        }
    }
    id = find_copied ( cid, id, res ) ;
    return ( id ) ;
}


/*
    MATCH A TEMPLATE MEMBER SPECIALISATION

    This routine finds the specialisation best matching the template instance
    or class template member id and specialising the member pid.
*/

static IDENTIFIER match_specialise
    PROTO_N ( ( id, pid ) )
    PROTO_T ( IDENTIFIER id X IDENTIFIER pid )
{
    int undef = 0 ;
    IDENTIFIER tid = NULL_id ;
    TYPE form = find_form ( id, &undef ) ;
    if ( !IS_NULL_type ( form ) ) {
        if ( IS_type_token ( form ) ) {
            /* Template instance */
            TYPE spec = specialise_form ( form, pid ) ;
            tid = DEREF_id ( type_token_tok ( spec ) ) ;
        } else if ( IS_type_instance ( form ) ) {
            /* Member of template class */
            NAMESPACE cns ;
            tid = DEREF_id ( type_instance_id ( form ) ) ;
            cns = DEREF_nspace ( id_parent ( id ) ) ;
            if ( !IS_NULL_nspace ( cns ) ) {
                IDENTIFIER cid = DEREF_id ( nspace_name ( cns ) ) ;
                IDENTIFIER rid = match_specialise ( cid, tid ) ;
                if ( !IS_NULL_id ( rid ) ) {
                    if ( EQ_id ( rid, cid ) ) {
                        tid = id ;
                    } else {
                        tid = find_copied_member ( rid, tid, 1, 2 ) ;
                    }
                }
            }
        }
    }
    return ( tid ) ;
}


/*
    SET TEMPLATE PARAMETERS

    This routine sets the template parameters for the instance form.
*/

static int set_templ_args
    PROTO_N ( ( form ) )
    PROTO_T ( TYPE form )
{
    int d = 0 ;
    INSTANCE app = DEREF_inst ( type_token_app ( form ) ) ;
    TYPE spec = DEREF_type ( inst_form ( app ) ) ;
    if ( IS_type_templ ( spec ) ) {
        TOKEN sort = DEREF_tok ( type_templ_sort ( spec ) ) ;
        LIST ( IDENTIFIER ) pids = DEREF_list ( tok_templ_pids ( sort ) ) ;
        LIST ( TOKEN ) args = DEREF_list ( type_token_args ( form ) ) ;
        d = save_token_args ( pids, args ) ;
    }
    return ( d ) ;
}


/*
    RESTORE TEMPLATE PARAMETERS

    This routine restores the template parameters for the instance form.
    d is the value returned from the corresponding call to set_templ_args.
*/

static void restore_templ_args
    PROTO_N ( ( form, d ) )
    PROTO_T ( TYPE form X int d )
{
    INSTANCE app = DEREF_inst ( type_token_app ( form ) ) ;
    TYPE spec = DEREF_type ( inst_form ( app ) ) ;
    if ( IS_type_templ ( spec ) ) {
        TOKEN sort = DEREF_tok ( type_templ_sort ( spec ) ) ;
        LIST ( IDENTIFIER ) pids = DEREF_list ( tok_templ_pids ( sort ) ) ;
        restore_token_args ( pids, d ) ;
    }
    return ;
}


/*
    REPORT THE INSTANTIATION OF A TEMPLATE

    This routine reports the instantiation of the template with the
    given form.
*/

static void report_instance
    PROTO_N ( ( form ) )
    PROTO_T ( TYPE form )
{
    ERROR err = ERR_temp_inst_def ( form ) ;
    if ( !IS_NULL_err ( err ) ) {
        if ( is_templ_depend ( form ) ) {
            destroy_error ( err, 1 ) ;
        } else {
            report ( crt_loc, err ) ;
        }
    }
    return ;
}


/*
    INSTANTIATE A FUNCTION TEMPLATE

    This routine creates an instance of the function template id with
    the template arguments args.
*/

IDENTIFIER instance_func
    PROTO_N ( ( id, args, func, def ) )
    PROTO_T ( IDENTIFIER id X LIST ( TOKEN ) args X int func X int def )
{
    int d = 0 ;
    IDENTIFIER tid ;
    IDENTIFIER rid = DEREF_id ( id_alias ( id ) ) ;
    TYPE s = DEREF_type ( id_function_etc_type ( rid ) ) ;
    TOKEN sort = DEREF_tok ( type_templ_sort ( s ) ) ;
    LIST ( IDENTIFIER ) pids = DEREF_list ( tok_templ_pids ( sort ) ) ;
    if ( func ) {
        /* Arguments already bound */
        /* EMPTY */
    } else {
        /* Bind arguments to parameters */
        d = save_token_args ( pids, args ) ;
    }

    /* Find template instance */
    tid = find_instance ( rid, sort, args, def ) ;
    if ( IS_NULL_id ( tid ) ) {
        /* Create a new instance */
        TYPE form ;
        ERROR perr ;
        PTR ( LOCATION ) ploc ;
        valid_template_args ( rid, sort, args ) ;
        MAKE_type_token ( cv_none, rid, args, form ) ;
        dump_template++ ;
        ploc = MAKE_ptr ( SIZE_loc ) ;
        COPY_loc ( ploc, crt_loc ) ;
        perr = set_prefix ( ERR_temp_inst_comment ( form, ploc ) ) ;
        if ( incr_value ( OPT_VAL_instance_depth ) ) {
            DECL_SPEC ds ;
            INSTANCE apps  ;
            HASHID nm = DEREF_hashid ( id_name ( rid ) ) ;

            /* Create new instance */
            tid = copy_id ( rid, 2 ) ;
            nm = expand_name ( nm, NULL_ctype ) ;
            COPY_hashid ( id_name ( tid ), nm ) ;
            ds = DEREF_dspec ( id_storage ( tid ) ) ;
            ds &= ~( dspec_used | dspec_called | dspec_done | dspec_defn ) ;
            ds &= ~dspec_template ;
            ds |= dspec_instance ;
            COPY_dspec ( id_storage ( tid ), ds ) ;
            COPY_exp ( id_function_etc_defn ( tid ), NULL_exp ) ;
            COPY_id ( id_function_etc_over ( tid ), NULL_id ) ;
            COPY_type ( id_function_etc_form ( tid ), form ) ;

            /* Check operator type */
            s = DEREF_type ( id_function_etc_type ( tid ) ) ;
#if LANGUAGE_CPP
            if ( IS_hashid_op ( nm ) ) {
                int mem = 1 ;
                int alloc = 0 ;
                if ( IS_id_function ( tid ) ) mem = 0 ;
                s = check_operator ( s, tid, mem, &alloc ) ;
                if ( alloc ) recheck_allocator ( tid, alloc ) ;
            }
#endif

            /* Add new template application */
            if ( IS_type_func ( s ) ) {
                /* Full specialisation */
                ds = dspec_instance ;
            } else {
                /* Partial specialisation */
                ds = dspec_implicit ;
            }
            if ( def ) ds |= dspec_used ;
            if ( is_templ_depend ( form ) ) ds |= dspec_mutable ;
            apps = DEREF_inst ( tok_templ_apps ( sort ) ) ;
            MAKE_inst_templ ( form, apps, tid, ds, all_instances, apps ) ;
            COPY_inst ( type_token_app ( form ), apps ) ;
            COPY_inst ( tok_templ_apps ( sort ), apps ) ;
            all_instances = apps ;
            if ( do_dump ) {
                /* Dump template instance information */
                dump_declare ( tid, &crt_loc, 0 ) ;
                dump_instance ( tid, form, form ) ;
            }

        } else {
            /* Instantiation depth too great */
            tid = rid ;
        }
        decr_value ( OPT_VAL_instance_depth ) ;
        restore_prefix ( perr ) ;
        DESTROY_ptr ( ploc, SIZE_loc ) ;
        dump_template-- ;
    }
    if ( func ) {
        /* Check for templates */
        s = DEREF_type ( id_function_etc_type ( tid ) ) ;
        if ( IS_type_templ ( s ) ) tid = NULL_id ;
    } else {
        restore_token_args ( pids, d ) ;
    }
    return ( tid ) ;
}


/*
    INSTANTIATE A CLASS TEMPLATE

    This routine creates an instance of the class template id with the
    template arguments args.  def is true if the class should be
    defined.
*/

IDENTIFIER instance_type
    PROTO_N ( ( id, args, type, def ) )
    PROTO_T ( IDENTIFIER id X LIST ( TOKEN ) args X int type X int def )
{
    int d = 0 ;
    int undef = 0 ;
    CLASS_TYPE cs ;
    IDENTIFIER tid ;
    TYPE form = NULL_type ;
    IDENTIFIER rid = DEREF_id ( id_alias ( id ) ) ;
    TYPE s = DEREF_type ( id_class_name_defn ( rid ) ) ;
    TOKEN sort = DEREF_tok ( type_templ_sort ( s ) ) ;
    LIST ( IDENTIFIER ) pids = DEREF_list ( tok_templ_pids ( sort ) ) ;
    if ( type ) {
        /* Arguments already bound */
        /* EMPTY */
    } else {
        /* Bind arguments to parameters */
        d = save_token_args ( pids, args ) ;
    }

    /* Check template class */
    while ( IS_type_templ ( s ) ) {
        s = DEREF_type ( type_templ_defn ( s ) ) ;
    }
    cs = DEREF_ctype ( type_compound_defn ( s ) ) ;

    /* Find template instance */
    tid = find_instance ( rid, sort, args, def ) ;
    if ( !IS_NULL_id ( tid ) && def ) {
        form = find_form ( tid, &undef ) ;
    }
    if ( IS_NULL_id ( tid ) || ( def && undef ) ) {
        /* Create a new instance or define an existing one */
        ERROR perr ;
        PTR ( LOCATION ) ploc ;
        if ( IS_NULL_type ( form ) ) {
            valid_template_args ( rid, sort, args ) ;
            MAKE_type_token ( cv_none, rid, args, form ) ;
        }
        dump_template++ ;
        if ( def ) report_instance ( form ) ;
        ploc = MAKE_ptr ( SIZE_loc ) ;
        COPY_loc ( ploc, crt_loc ) ;
        perr = set_prefix ( ERR_temp_inst_comment ( form, ploc ) ) ;
        if ( incr_value ( OPT_VAL_instance_depth ) ) {
            TYPE t ;
            int d2 = 0 ;
            DECL_SPEC ds ;
            CLASS_TYPE ct ;
            int created = 0 ;
            TYPE spec = form ;
            CLASS_INFO ci = cinfo_templ_base ;

            /* Create new instance if necessary */
            if ( IS_NULL_id ( tid ) ) {
                INSTANCE apps ;
                tid = copy_id ( rid, 2 ) ;
                ds = DEREF_dspec ( id_storage ( tid ) ) ;
                ds &= ~( dspec_used | dspec_done | dspec_defn ) ;
                ds &= ~dspec_template ;
                ds |= dspec_instance ;
                COPY_dspec ( id_storage ( tid ), ds ) ;

                /* Add new template application */
                ds = dspec_instance ;
                if ( def ) ds |= dspec_used ;
                if ( is_templ_depend ( form ) ) ds |= dspec_mutable ;
                apps = DEREF_inst ( tok_templ_apps ( sort ) ) ;
                MAKE_inst_templ ( form, apps, tid, ds, all_instances, apps ) ;
                COPY_inst ( type_token_app ( form ), apps ) ;
                COPY_inst ( tok_templ_apps ( sort ), apps ) ;
                all_instances = apps ;
                created = 1 ;
            }

            /* Check for matching specialisations */
            if ( def ) {
                spec = specialise_form ( form, NULL_id ) ;
                if ( !EQ_type ( spec, form ) ) {
                    /* Specialisation found */
                    d2 = set_templ_args ( spec ) ;
                    rid = DEREF_id ( type_token_tok ( spec ) ) ;
                    s = DEREF_type ( id_class_name_defn ( rid ) ) ;
                    while ( IS_type_templ ( s ) ) {
                        s = DEREF_type ( type_templ_defn ( s ) ) ;
                    }
                    cs = DEREF_ctype ( type_compound_defn ( s ) ) ;
                    if ( !created ) {
                        if ( do_dump ) dump_instance ( tid, form, spec ) ;
                    }
                }
            }

            /* Instantiate class members */
            t = DEREF_type ( id_class_name_defn ( tid ) ) ;
            s = DEREF_type ( ctype_form ( cs ) ) ;
            COPY_type ( ctype_form ( cs ), t ) ;
            while ( IS_type_templ ( t ) ) {
                ci = cinfo_template ;
                t = DEREF_type ( type_templ_defn ( t ) ) ;
            }
            ct = DEREF_ctype ( type_compound_defn ( t ) ) ;
            COPY_type ( ctype_form ( ct ), form ) ;
            if ( do_dump && created ) {
                /* Dump template instance information */
                dump_declare ( tid, &crt_loc, 0 ) ;
                dump_instance ( tid, form, spec ) ;
            }
            copy_members ( ct, cs, ci, def ) ;
            COPY_type ( ctype_form ( cs ), s ) ;
            if ( !EQ_type ( spec, form ) ) {
                /* Reset specialisation parameters */
                restore_templ_args ( spec, d2 ) ;
            }

        } else {
            /* Instantiation depth too great */
            if ( IS_NULL_id ( tid ) ) tid = rid ;
        }
        decr_value ( OPT_VAL_instance_depth ) ;
        restore_prefix ( perr ) ;
        DESTROY_ptr ( ploc, SIZE_loc ) ;
        dump_template-- ;
    }
    if ( type ) {
        /* Check for templates */
        TYPE t = DEREF_type ( id_class_name_defn ( tid ) ) ;
        if ( IS_type_templ ( t ) ) tid = NULL_id ;
    } else {
        restore_token_args ( pids, d ) ;
    }
    return ( tid ) ;
}


/*
    COMPLETE A CLASS DEFINITION

    This routine is called with def true whenever a class type is
    encountered which is complete but not defined in a context where a
    complete type is required.  If ct is a template class instance then
    the definition is provided.  If def is false then ct is marked as
    complete if possible.
*/

void complete_class
    PROTO_N ( ( ct, def ) )
    PROTO_T ( CLASS_TYPE ct X int def )
{
    CLASS_INFO ci = DEREF_cinfo ( ctype_info ( ct ) ) ;
    if ( !( ci & cinfo_defined ) ) {
        IDENTIFIER cid = DEREF_id ( ctype_name ( ct ) ) ;
        IDENTIFIER sid = match_specialise ( cid, NULL_id ) ;
        if ( !IS_NULL_id ( sid ) && IS_id_class_name ( sid ) ) {
            /* Template class instance */
            CLASS_TYPE cs ;
            CLASS_INFO cj ;
            TYPE s = DEREF_type ( id_class_name_defn ( sid ) ) ;
            while ( IS_type_templ ( s ) ) {
                s = DEREF_type ( type_templ_defn ( s ) ) ;
            }
            cs = DEREF_ctype ( type_compound_defn ( s ) ) ;
            if ( !EQ_ctype ( cs, ct ) ) {
                /* Allow for nested template classes */
                complete_class ( cs, def ) ;
            }
            cj = DEREF_cinfo ( ctype_info ( cs ) ) ;
            if ( cj & cinfo_complete ) {
                /* Template class is complete */
                ci |= cinfo_complete ;
                COPY_cinfo ( ctype_info ( ct ), ci ) ;
                if ( def ) {
                    /* Define template class */
                    TYPE form = DEREF_type ( ctype_form ( ct ) ) ;
                    if ( !IS_NULL_type ( form ) ) {
                        if ( IS_type_token ( form ) ) {
                            /* Template instance */
                            IDENTIFIER tid ;
                            LIST ( TOKEN ) args ;
                            tid = DEREF_id ( type_token_tok ( form ) ) ;
                            args = DEREF_list ( type_token_args ( form ) ) ;
                            IGNORE instance_type ( tid, args, 0, 1 ) ;
                        } else {
                            /* Nested template class */
                            EXP e ;
                            MAKE_exp_value ( s, e ) ;
                            IGNORE define_templ_member ( cid, sid, form, e ) ;
                        }
                    }
                }
            }
        }
    }
    return ;
}


/*
    CHECK THE ARGUMENTS OF A TEMPLATE SPECIALISATION

    This routine checks the template specialisation declared with
    parameters pids and arguments form.
*/

static void check_spec_args
    PROTO_N ( ( pids, form ) )
    PROTO_T ( LIST ( IDENTIFIER ) pids X TYPE form )
{
    LIST ( TOKEN ) args = DEREF_list ( type_token_args ( form ) ) ;
    while ( !IS_NULL_list ( args ) ) {
        TOKEN a = DEREF_tok ( HEAD_list ( args ) ) ;
        if ( IS_tok_exp ( a ) ) {
            /* Non-type argument */
            EXP e = DEREF_exp ( tok_exp_value ( a ) ) ;
            if ( depends_on_exp ( e, pids, 0 ) == 1 ) {
                report ( crt_loc, ERR_temp_class_spec_depend ( form ) ) ;
            }
        }
        args = TAIL_list ( args ) ;
    }
    return ;
}


/*
    CHECK A TEMPLATE SPECIALISATION

    This routine checks the specialisation spec of the template tid.
    It identifies spec with any previous matching specialisation and
    returns this previous version.  If no such match is found the
    original instance is returned.
*/

static INSTANCE check_specialise
    PROTO_N ( ( tid, spec, type ) )
    PROTO_T ( IDENTIFIER tid X INSTANCE spec X int type )
{
    INSTANCE apps ;
    ERROR merr = NULL_err ;
    INSTANCE eq = NULL_inst ;
    TYPE form = DEREF_type ( inst_form ( spec ) ) ;

    /* Scan through previous instances */
    force_template++ ;
    apps = find_templ_apps ( tid ) ;
    while ( !IS_NULL_inst ( apps ) ) {
        if ( !EQ_inst ( spec, apps ) ) {
            DECL_SPEC acc = DEREF_dspec ( inst_templ_access ( apps ) ) ;
            if ( !( acc & dspec_alias ) ) {
                /* Compare with previous instance */
                TYPE prev = DEREF_type ( inst_form ( apps ) ) ;
                int cmp = eq_type ( form, prev ) ;
                if ( acc & dspec_main ) {
                    /* Comparison with primary template */
                    if ( cmp == 1 && ( type < 2 && crt_templ_qualifier ) ) {
                        ERROR err = ERR_temp_class_spec_primary ( form ) ;
                        report ( crt_loc, err ) ;
                        crt_templ_qualifier = 0 ;
                    } else if ( cmp == 2 || cmp == 4 ) {
                        ERROR err = ERR_temp_class_spec_primary ( form ) ;
                        report ( crt_loc, err ) ;
                    }
                }
                if ( cmp == 2 && ( acc & dspec_instance ) ) {
                    if ( !( acc & dspec_explicit ) && type < 3 ) {
                        /* Specialisation matches previous use */
                        ERROR err = ERR_temp_spec_post ( form, prev ) ;
                        merr = concat_error ( merr, err ) ;
                    }
                }
                if ( cmp == 1 ) {
                    /* Equality of template specialisations */
                    eq = apps ;
                    break ;
                }
            }
        }
        apps = DEREF_inst ( inst_next ( apps ) ) ;
    }
    force_template-- ;

    /* Identify equal specialisations */
    if ( !IS_NULL_inst ( eq ) ) {
        TYPE prev = DEREF_type ( inst_form ( eq ) ) ;
        DECL_SPEC acc = DEREF_dspec ( inst_templ_access ( spec ) ) ;
        acc |= dspec_alias ;
        COPY_dspec ( inst_templ_access ( spec ), acc ) ;
        COPY_inst ( inst_alias ( spec ), eq ) ;
        if ( !IS_NULL_err ( merr ) ) destroy_error ( merr, 1 ) ;

        /* Identify template parameters */
        if ( IS_type_templ ( form ) && IS_type_templ ( prev ) ) {
            TOKEN as = DEREF_tok ( type_templ_sort ( form ) ) ;
            TOKEN at = DEREF_tok ( type_templ_sort ( prev ) ) ;
            LIST ( IDENTIFIER ) ps = DEREF_list ( tok_templ_pids ( as ) ) ;
            LIST ( IDENTIFIER ) pt = DEREF_list ( tok_templ_pids ( at ) ) ;
            IGNORE eq_templ_params ( ps, pt ) ;

            /* Re-check more recent instances */
            apps = all_instances ;
            while ( !EQ_inst ( apps, spec ) ) {
                acc = DEREF_dspec ( inst_templ_access ( apps ) ) ;
                if ( !( acc & dspec_alias ) ) {
                    IDENTIFIER pid ;
                    prev = DEREF_type ( inst_form ( apps ) ) ;
                    while ( IS_type_templ ( prev ) ) {
                        prev = DEREF_type ( type_templ_defn ( prev ) ) ;
                    }
                    pid = DEREF_id ( type_token_tok ( prev ) ) ;
                    IGNORE check_specialise ( pid, apps, 3 ) ;
                }
                apps = DEREF_inst ( inst_templ_prev ( apps ) ) ;
            }
        }
    } else {
        /* Report matching instances */
        if ( !IS_NULL_err ( merr ) ) report ( crt_loc, merr ) ;
        eq = spec ;
    }
    return ( eq ) ;
}


/*
    ADJUST THE LINKAGE OF A TEMPLATE FUNCTION

    A specialisation of a template function is inline only if it is
    explicitly declared to be, independently of whether its function
    template is.  However no storage class specifiers may be given for
    a specialisation.  This routine adjusts the linkage of the template
    function id of form form declared with declaration specifiers ds.
*/

static void adjust_func_templ
    PROTO_N ( ( id, ds, form ) )
    PROTO_T ( IDENTIFIER id X DECL_SPEC ds X TYPE form )
{
    if ( !IS_NULL_type ( form ) ) {
        DECL_SPEC pds ;
        int redecl = 0 ;
        if ( IS_type_token ( form ) ) {
            INSTANCE app = DEREF_inst ( type_token_app ( form ) ) ;
            if ( !IS_NULL_inst ( app ) ) {
                /* Check for redeclarations */
                DECL_SPEC acc = DEREF_dspec ( inst_templ_access ( app ) ) ;
                if ( acc & dspec_static ) redecl = 1 ;
                acc |= dspec_static ;
                COPY_dspec ( inst_templ_access ( app ), acc ) ;
            }
        } else if ( IS_type_instance ( form ) ) {
            /* Check for redeclarations */
            DECL_SPEC acc = DEREF_dspec ( type_instance_access ( form ) ) ;
            if ( acc & dspec_static ) redecl = 1 ;
            acc |= dspec_static ;
            COPY_dspec ( type_instance_access ( form ), acc ) ;
        }
        if ( !redecl && !IS_NULL_id ( id ) && IS_id_function_etc ( id ) ) {
            /* Adjust inline specifier */
            pds = DEREF_dspec ( id_storage ( id ) ) ;
            pds &= ~dspec_inline ;
            if ( ds & dspec_inline ) {
                /* Mark inline functions */
                pds |= dspec_inline ;
            }
            COPY_dspec ( id_storage ( id ), pds ) ;
        }
        pds = ( ds & dspec_storage ) ;
        if ( pds != dspec_none ) {
            /* Check for storage class specifiers */
            report ( decl_loc, ERR_dcl_stc_expl_spec ( pds ) ) ;
        }
        if ( crt_linkage == dspec_c ) {
            /* Check for C linkage */
            report ( decl_loc, ERR_temp_decl_linkage () ) ;
        }
    }
    return ;
}


/*
    EXAMINE A TEMPLATE SPECIALISATION TYPE

    This routine examines the template specialisation t of the given form.
    expl is true for an explicit specialisation and false for a simple
    redeclaration.  The routine returns 2 if t represents an explicit
    instantiation, 1 if it represents an explicit specialisation, and 0
    otherwise.
*/

static int bind_templ_spec
    PROTO_N ( ( pid, t, form, type, expl ) )
    PROTO_T ( IDENTIFIER *pid X TYPE t X TYPE form X int type X int expl )
{
    int def = 0 ;
    if ( expl ) {
        DECL_SPEC acc ;
        TOKEN sort = NULL_tok ;
        NAMESPACE ns = crt_namespace ;
        LIST ( IDENTIFIER ) pids = NULL_list ( IDENTIFIER ) ;
        INSTANCE spec = DEREF_inst ( type_token_app ( form ) ) ;
        IDENTIFIER tid = DEREF_id ( type_token_tok ( form ) ) ;
        if ( IS_type_templ ( t ) ) {
            /* Find template information */
            sort = DEREF_tok ( type_templ_sort ( t ) ) ;
            pids = DEREF_list ( tok_templ_pids ( sort ) ) ;
            ns = DEREF_nspace ( tok_templ_pars ( sort ) ) ;
        }
        if ( IS_NULL_list ( pids ) ) {
            if ( IS_NULL_nspace ( ns ) ) {
                /* Explicit instantiation */
                acc = DEREF_dspec ( inst_templ_access ( spec ) ) ;
                if ( acc & dspec_register ) {
                    report ( decl_loc, ERR_temp_spec_reinst ( form ) ) ;
                } else if ( acc & dspec_explicit ) {
                    report ( decl_loc, ERR_temp_spec_redecl ( form ) ) ;
                }
                acc |= dspec_register ;
                COPY_dspec ( inst_templ_access ( spec ), acc ) ;
                def = ( type == 2 ? 3 : 2 ) ;
            } else {
                /* Explicit specialisation */
                spec = check_specialise ( tid, spec, type ) ;
                if ( type != 2 ) {
                    acc = DEREF_dspec ( inst_templ_access ( spec ) ) ;
                    if ( acc & dspec_register ) {
                        report ( decl_loc, ERR_temp_spec_redecl ( form ) ) ;
                    } else if ( acc & dspec_explicit ) {
                        report ( decl_loc, ERR_temp_spec_respec ( form ) ) ;
                    } else if ( acc & ( dspec_used | dspec_called ) ) {
                        report ( decl_loc, ERR_temp_spec_used ( form ) ) ;
                    }
                    acc |= dspec_explicit ;
                    COPY_dspec ( inst_templ_access ( spec ), acc ) ;
                }
                *pid = DEREF_id ( inst_templ_id ( spec ) ) ;
                def = 1 ;
            }

        } else {
            /* Partial specialisation */
            check_spec_args ( pids, form ) ;
            if ( check_templ_dargs ( t ) ) {
                /* Check template default arguments */
                report ( decl_loc, ERR_temp_class_spec_darg () ) ;
            }
            MAKE_type_templ ( cv_none, sort, form, 1, form ) ;
            COPY_type ( inst_form ( spec ), form ) ;
            spec = check_specialise ( tid, spec, type ) ;
            acc = DEREF_dspec ( inst_templ_access ( spec ) ) ;
            acc &= ~dspec_instance ;
            acc |= dspec_template ;
            if ( type == 0 && !( acc & dspec_main ) ) {
                /* Can't partially specialise functions */
                report ( decl_loc, ERR_temp_decl_func () ) ;
            }
            if ( type != 2 ) {
                if ( acc & dspec_called ) {
                    /* Have specialised members */
                    report ( decl_loc, ERR_temp_spec_used ( form ) ) ;
                }
                acc |= dspec_extern ;
            }
            COPY_dspec ( inst_templ_access ( spec ), acc ) ;
            *pid = DEREF_id ( inst_templ_id ( spec ) ) ;
        }
        if ( type == 0 && check_func_dargs ( t, 0, 0 ) ) {
            /* Check function default arguments */
            report ( decl_loc, ERR_temp_class_spec_darg () ) ;
        }
    }
    return ( def ) ;
}


/*
    TEMPLATE SPECIALISATION FLAG

    This flag is set by bind_specialise to indicate the type of template
    declaration encountered.  The values are as in bind_templ_spec.
*/

int bound_specialise = 0 ;


/*
    BIND TEMPLATE PARAMETERS IN A TEMPLATE SPECIALISATION

    This routine binds any template parameters in the template
    specialisation given by the declarator id of type t and declaration
    specifiers ds.  It returns those components of t which bind to the
    underlying object.  For example in 'template < class T > int A < T >::a'
    the component of the type which binds to 'a' is 'int'.  The
    'template < class T >' component binds to the 'A < T >::' qualifier.
    Note that this analysis may be done prior to any replacement of
    inferred types in t.
*/

TYPE bind_specialise
    PROTO_N ( ( pid, t, ds, type, force, init ) )
    PROTO_T ( IDENTIFIER *pid X TYPE t X DECL_SPEC ds X
              int type X int force X int init )
{
    IDENTIFIER id = *pid ;
    if ( !IS_NULL_id ( id ) ) {
        DECL_SPEC pds = DEREF_dspec ( id_storage ( id ) ) ;
        if ( pds & dspec_instance ) {
            /* Template instance */
            TYPE f ;
            int def = 0 ;
            int undef = 0 ;
            INSTANCE spec = NULL_inst ;

            /* Examine enclosing class */
            NAMESPACE ns = DEREF_nspace ( id_parent ( id ) ) ;
            if ( !IS_NULL_nspace ( ns ) && IS_nspace_ctype ( ns ) ) {
                IDENTIFIER cid = DEREF_id ( nspace_name ( ns ) ) ;
                IDENTIFIER sid = cid ;
                t = bind_specialise ( &sid, t, ds, 2, 0, init ) ;
                if ( !EQ_id ( sid, cid ) ) {
                    /* Changed specialisation */
                    IDENTIFIER qid = id ;
                    id = find_copied_member ( sid, qid, 0, type ) ;
                    if ( type != 2 ) {
                        /* Update namespace stacks */
                        QUALIFIER q = qual_nested ;
                        end_declarator ( qid, 1 ) ;
                        begin_declarator ( id, q, NULL_nspace, 1 ) ;
                    }
                    *pid = id ;
                }
                f = find_form ( sid, &undef ) ;
                if ( !IS_NULL_type ( f ) && IS_type_token ( f ) ) {
                    spec = DEREF_inst ( type_token_app ( f ) ) ;
                }
            }

            /* Select overloaded function */
            if ( type == 0 && IS_id_function_etc ( id ) ) {
                TYPE fn = t ;
                if ( !IS_NULL_type ( fn ) ) {
                    TYPE ret ;
                    int eq = 0 ;
                    IDENTIFIER fid ;
                    LIST ( IDENTIFIER ) pids = NULL_list ( IDENTIFIER ) ;
                    if ( IS_type_templ ( fn ) ) {
                        fn = DEREF_type ( type_templ_defn ( fn ) ) ;
                    }
                    ret = inferred_return ( fn, id ) ;
                    fid = resolve_func ( id, fn, 1, 0, pids, &eq ) ;
                    if ( IS_NULL_id ( fid ) ) {
                        /* No match found */
                        report ( decl_loc, ERR_temp_spec_type ( fn, id ) ) ;
                    } else if ( IS_id_ambig ( fid ) ) {
                        /* More than one match found */
                        IGNORE report_ambiguous ( fid, 0, 0, 0 ) ;
                        fid = NULL_id ;
                    }
                    if ( !IS_NULL_type ( ret ) ) {
                        /* Restore return type */
                        COPY_type ( type_func_ret ( fn ), ret ) ;
                    }
                    *pid = fid ;
                    id = fid ;
                }
            }

            /* Examine template instance */
            f = find_form ( id, &undef ) ;
            if ( !IS_NULL_type ( f ) && IS_type_token ( f ) ) {
                IDENTIFIER tid = DEREF_id ( type_token_tok ( f ) ) ;
                unsigned tag = TAG_id ( tid ) ;
                if ( tag != id_token_tag ) {
                    /* Examine template qualifiers */
                    if ( !IS_NULL_type ( t ) && IS_type_templ ( t ) ) {
                        def = bind_templ_spec ( &id, t, f, type, 1 ) ;
                        if ( def == 1 || def == 2 ) {
                            /* Explicit specialisations */
                            t = DEREF_type ( type_templ_defn ( t ) ) ;
                        } else if ( def == 0 ) {
                            /* Partial specialisations */
                            if ( type == 2 || crt_templ_qualifier ) {
                                t = DEREF_type ( type_templ_defn ( t ) ) ;
                            }
                        }
                    } else {
                        int expl = 0 ;
                        TYPE s = type_error ;
                        if ( !( ds & dspec_friend ) ) {
                            /* A friend declaration is allowed */
                            report ( decl_loc, ERR_temp_spec_prefix () ) ;
                            expl = 1 ;
                        }
                        def = bind_templ_spec ( &id, s, f, type, expl ) ;
                    }
                    if ( tag == id_class_name_tag ) {
                        if ( type == 0 ) {
                            report ( decl_loc, ERR_temp_spec_bad ( f ) ) ;
                            id = NULL_id ;
                        }
                    } else {
                        if ( type ) {
                            report ( decl_loc, ERR_temp_spec_bad ( f ) ) ;
                            id = NULL_id ;
                        } else {
                        }
                    }
                    bound_specialise = def ;
                    *pid = id ;
                }
            } else {
                /* Check for explicit instantiations */
                if ( !IS_NULL_type ( t ) && IS_type_templ ( t ) ) {
                    TOKEN sort = DEREF_tok ( type_templ_sort ( t ) ) ;
                    NAMESPACE pns = DEREF_nspace ( tok_templ_pars ( sort ) ) ;
                    if ( IS_NULL_nspace ( pns ) ) {
                        if ( !IS_NULL_type ( f ) && IS_type_instance ( f ) ) {
                            def = ( type == 2 ? 3 : 2 ) ;
                        } else {
                            report ( decl_loc, ERR_temp_explicit_templ () ) ;
                        }
                        t = DEREF_type ( type_templ_defn ( t ) ) ;
                        bound_specialise = def ;
                    }
                }
            }

            /* Adjust function linkage */
            if ( !type ) adjust_func_templ ( id, ds, f ) ;

            /* Record member specialisations */
            if ( !IS_NULL_inst ( spec ) ) {
                DECL_SPEC acc ;
                LIST ( IDENTIFIER ) mems ;
                if ( def == 0 || def == 1 ) {
                    if ( !IS_NULL_type ( f ) && IS_type_instance ( f ) ) {
                        IDENTIFIER mid = DEREF_id ( type_instance_id ( f ) ) ;
                        mems = DEREF_list ( inst_templ_mems ( spec ) ) ;
                        CONS_id ( mid, mems, mems ) ;
                        COPY_list ( inst_templ_mems ( spec ), mems ) ;
                    }
                }
                acc = DEREF_dspec ( inst_templ_access ( spec ) ) ;
                acc |= dspec_called ;
                COPY_dspec ( inst_templ_access ( spec ), acc ) ;
            }

            /* Mark explicit specialisations */
            if ( def == 1 && !IS_NULL_id ( id ) ) {
                if ( type != 2 ) {
                    /* Explicit specialisations are exported */
                    export_template ( id, 1 ) ;
                }
            }

            /* Mark explicit instantiations */
            if ( def == 2 && !IS_NULL_id ( id ) ) {
                if ( force == 2 ) {
                    /* template-id required in instantiation */
                    ERROR err = ERR_temp_explicit_id ( id ) ;
                    report ( decl_loc, err ) ;
                }
                if ( init ) {
                    /* Can't have definition as well */
                    report ( decl_loc, ERR_temp_explicit_def () ) ;
                }
                define_template ( id, 1 ) ;
            }

        } else if ( force ) {
            /* Not a template instance */
            if ( pds & dspec_template ) {
                if ( IS_id_function_etc ( id ) ) {
                    /* Template function */
                    allow_templ_dargs = 0 ;
                    *pid = parse_id_template ( id, NIL ( PPTOKEN ), 0 ) ;
                    allow_templ_dargs = 1 ;
                    crt_templ_qualifier = 1 ;
                    t = bind_specialise ( pid, t, ds, type, 2, init ) ;
                } else {
                    /* Template class */
                    report ( decl_loc, ERR_temp_param_none ( id ) ) ;
                    *pid = NULL_id ;
                }
            } else {
                NAMESPACE ns = DEREF_nspace ( id_parent ( id ) ) ;
                if ( !IS_NULL_nspace ( ns ) && IS_nspace_ctype ( ns ) ) {
                    IDENTIFIER cid = DEREF_id ( nspace_name ( ns ) ) ;
                    IDENTIFIER sid = cid ;
                    t = bind_specialise ( &sid, t, ds, 2, 0, init ) ;
                    if ( !EQ_id ( sid, cid ) ) {
                        /* Changed specialisation */
                        IDENTIFIER qid = id ;
                        id = find_copied_member ( sid, qid, 0, type ) ;
                        if ( type != 2 ) {
                            /* Update namespace stacks */
                            QUALIFIER q = qual_nested ;
                            end_declarator ( qid, 1 ) ;
                            begin_declarator ( id, q, NULL_nspace, 1 ) ;
                        }
                        *pid = id ;
                    }
                }
                if ( IS_type_templ ( t ) ) {
                    /* Invalid template declaration */
                    id = underlying_id ( id ) ;
                    report ( decl_loc, ERR_temp_param_none ( id ) ) ;
                    *pid = NULL_id ;
                }
            }
        }
    }
    return ( t ) ;
}


/*
    SYNTHESISE A LIST OF ARGUMENTS FOR A FUNCTION

    This routine synthesises a list of dummy arguments for the function
    template id.
*/

static LIST ( EXP ) synthesise_args
    PROTO_N ( ( id ) )
    PROTO_T ( IDENTIFIER id )
{
    LIST ( TYPE ) pars ;
    LIST ( EXP ) args = NULL_list ( EXP ) ;
    TYPE fn = DEREF_type ( id_function_etc_type ( id ) ) ;
    while ( IS_type_templ ( fn ) ) {
        fn = DEREF_type ( type_templ_defn ( fn ) ) ;
    }
    pars = DEREF_list ( type_func_mtypes ( fn ) ) ;
    while ( !IS_NULL_list ( pars ) ) {
        EXP a ;
        TYPE t = DEREF_type ( HEAD_list ( pars ) ) ;
        if ( IS_type_ref ( t ) ) {
            /* Do reference conversions */
            t = DEREF_type ( type_ref_sub ( t ) ) ;
        }
        MAKE_exp_value ( t, a ) ;
        CONS_exp ( a, args, args ) ;
        pars = TAIL_list ( pars ) ;
    }
    return ( REVERSE_list ( args ) ) ;
}


/*
    FIND THE MORE SPECIALISED OF TWO FUNCTION TEMPLATES

    This routine compares the function templates tid and sid.  It returns
    1 if tid is more specialised than sid, 2 if sid is more specialised,
    and 0 otherwise.
*/

int compare_specs
    PROTO_N ( ( tid, sid ) )
    PROTO_T ( IDENTIFIER tid X IDENTIFIER sid )
{
    if ( !EQ_id ( tid, sid ) ) {
        IDENTIFIER ds, dt ;
        LIST ( EXP ) args ;
        ERROR err = NULL_err ;
        args = synthesise_args ( sid ) ;
        ds = deduce_args ( tid, args, 0, 0, 0, &err ) ;
        free_exp_list ( args, 1 ) ;
        args = synthesise_args ( tid ) ;
        dt = deduce_args ( sid, args, 0, 0, 0, &err ) ;
        free_exp_list ( args, 1 ) ;
        if ( !IS_NULL_err ( err ) ) destroy_error ( err, 1 ) ;
        if ( !IS_NULL_id ( ds ) ) {
            if ( IS_NULL_id ( dt ) ) return ( 2 ) ;
        } else {
            if ( !IS_NULL_id ( dt ) ) return ( 1 ) ;
        }
    }
    return ( 0 ) ;
}


/*
    CHECK A TEMPLATE FUNCTION DECLARATION

    This routine checks the declaration of the function id which is
    either a template function itself or overloads a template function.
*/

void templ_func_decl
    PROTO_N ( ( id ) )
    PROTO_T ( IDENTIFIER id )
{
    while ( !IS_NULL_id ( id ) ) {
        DECL_SPEC ds = DEREF_dspec ( id_storage ( id ) ) ;
        ds |= dspec_template ;
        COPY_dspec ( id_storage ( id ), ds ) ;
        id = DEREF_id ( id_function_etc_over ( id ) ) ;
    }
    return ;
}


/*
    LIST OF TEMPLATES FOR DEFINITION

    The variable pending_templates holds a list of all the template
    instances which should be defined at the next available opportunity.
    The variable still_pending_templates holds a list of all those
    instances which should be defined if the corresponding template
    is defined at some later stage.
*/

LIST ( IDENTIFIER ) pending_templates = NULL_list ( IDENTIFIER ) ;
LIST ( IDENTIFIER ) still_pending_templates = NULL_list ( IDENTIFIER ) ;


/*
    MARK ALL MEMBERS OF A TEMPLATE CLASS FOR DEFINITION

    This routine adds all members of the template class ct to the list of
    templates to be defined.
*/

static void define_members
    PROTO_N ( ( ct ) )
    PROTO_T ( CLASS_TYPE ct )
{
    TYPE form = DEREF_type ( ctype_form ( ct ) ) ;
    if ( !IS_NULL_type ( form ) ) {
        NAMESPACE ns = DEREF_nspace ( ctype_member ( ct ) ) ;
        MEMBER mem = DEREF_member ( nspace_ctype_first ( ns ) ) ;
        GRAPH gr = DEREF_graph ( ctype_base ( ct ) ) ;
        LIST ( GRAPH ) br = DEREF_list ( graph_tails ( gr ) ) ;
        while ( !IS_NULL_list ( br ) ) {
            /* Scan through base classes */
            GRAPH gs = DEREF_graph ( HEAD_list ( br ) ) ;
            CLASS_TYPE cs = DEREF_ctype ( graph_head ( gs ) ) ;
            define_members ( cs ) ;
            br = TAIL_list ( br ) ;
        }
        while ( !IS_NULL_member ( mem ) ) {
            /* Scan through class members */
            IDENTIFIER id = DEREF_id ( member_id ( mem ) ) ;
            IDENTIFIER alt = DEREF_id ( member_alt ( mem ) ) ;
            if ( !IS_NULL_id ( id ) ) {
                define_template ( id, 1 ) ;
            }
            if ( !IS_NULL_id ( alt ) && !EQ_id ( id, alt ) ) {
                define_template ( alt, 2 ) ;
            }
            mem = DEREF_member ( member_next ( mem ) ) ;
        }
    }
    return ;
}


/*
    MARK A TEMPLATE FOR DEFINITION

    This routine adds the template instance id to the list of templates
    to be defined.  The identifier is marked as defined even though the
    actual definition occurs later.  Explicit instantiations are indicated
    by expl being true.
*/

void define_template
    PROTO_N ( ( id, expl ) )
    PROTO_T ( IDENTIFIER id X int expl )
{
    DECL_SPEC ds = DEREF_dspec ( id_storage ( id ) ) ;
    if ( ds & dspec_inherit ) {
        /* Inherited functions */
        id = DEREF_id ( id_alias ( id ) ) ;
        ds = DEREF_dspec ( id_storage ( id ) ) ;
    }
    if ( ( ds & dspec_instance ) && !( ds & dspec_implicit ) ) {
        if ( dependent_id ( id ) ) {
            /* Ignore template dependent instantiations */
            return ;
        }
        switch ( TAG_id ( id ) ) {
            case id_class_name_tag :
            case id_class_alias_tag : {
                if ( expl ) {
                    TYPE t = DEREF_type ( id_class_name_etc_defn ( id ) ) ;
                    if ( IS_type_compound ( t ) ) {
                        /* Define all template members */
                        ERROR err = check_incomplete ( t ) ;
                        if ( IS_NULL_err ( err ) ) {
                            CLASS_TYPE ct ;
                            NAMESPACE ns = DEREF_nspace ( id_parent ( id ) ) ;
                            check_decl_nspace ( id, ns, 1, crt_namespace ) ;
                            ct = DEREF_ctype ( type_compound_defn ( t ) ) ;
                            define_members ( ct ) ;
                        } else {
                            ERROR err2 = ERR_temp_explicit_incompl () ;
                            err = concat_error ( err, err2 ) ;
                            report ( crt_loc, err ) ;
                        }
                    }
                }
                break ;
            }
            case id_function_tag :
            case id_mem_func_tag :
            case id_stat_mem_func_tag : {
                /* Template functions */
                if ( !( ds & dspec_defn ) ) {
                    CONS_id ( id, pending_templates, pending_templates ) ;
                    COPY_dspec ( id_storage ( id ), ( ds | dspec_defn ) ) ;
                    COPY_loc ( id_loc ( id ), crt_loc ) ;
                }
                if ( expl == 2 ) {
                    /* Allow for overloaded functions */
                    id = DEREF_id ( id_function_etc_over ( id ) ) ;
                    if ( !IS_NULL_id ( id ) ) define_template ( id, 2 ) ;
                }
                break ;
            }
            case id_stat_member_tag : {
                /* Static data members of class templates */
                if ( !( ds & dspec_defn ) ) {
                    TYPE t = DEREF_type ( id_stat_member_type ( id ) ) ;
                    CV_SPEC cv = DEREF_cv ( type_qual ( t ) ) ;
                    COPY_dspec ( id_storage ( id ), ( ds | dspec_defn ) ) ;
                    COPY_loc ( id_loc ( id ), crt_loc ) ;
                    CONS_id ( id, pending_templates, pending_templates ) ;
                    if ( cv == ( cv_lvalue | cv_const ) ) {
                        copy_template ( id, 2 ) ;
                    }
                }
                break ;
            }
        }
    }
    return ;
}


/*
    BIND TEMPLATE ARGUMENTS AND DEFINE A TEMPLATE

    This routine binds the template arguments for the identifier tid to
    the specialisation sid and then defines id to be e.  It returns false
    if any template argument depends on an unbound template parameter.
*/

static int bind_template
    PROTO_N ( ( tid, id, sid, e, bound ) )
    PROTO_T ( IDENTIFIER tid X IDENTIFIER id X IDENTIFIER sid X
              EXP e X int bound )
{
    if ( IS_NULL_id ( tid ) ) {
        /* Binding complete - copy definition */
        if ( !bound ) {
            /* Suppress output of object definition */
            DECL_SPEC ds = DEREF_dspec ( id_storage ( id ) ) ;
            COPY_dspec ( id_storage ( id ), ( ds | dspec_done ) ) ;
        }
        copy_object ( id, e ) ;

    } else {
        /* Examine parent namespace */
        TYPE p, q ;
        int undef = 0 ;
        IDENTIFIER pid = NULL_id ;
        IDENTIFIER qid = NULL_id ;
        NAMESPACE pns = DEREF_nspace ( id_parent ( tid ) ) ;
        NAMESPACE qns = DEREF_nspace ( id_parent ( sid ) ) ;
        if ( !IS_NULL_nspace ( pns ) ) {
            switch ( TAG_nspace ( pns ) ) {
                case nspace_block_tag :
                case nspace_param_tag :
                case nspace_dummy_tag :
                case nspace_ctype_tag : {
                    /* Find enclosing class or function */
                    pid = DEREF_id ( nspace_name ( pns ) ) ;
                    qid = DEREF_id ( nspace_name ( qns ) ) ;
                    break ;
                }
            }
        }

        /* Bind template arguments */
        p = find_form ( tid, &undef ) ;
        q = find_form ( sid, &undef ) ;
        if ( !IS_NULL_type ( p ) && IS_type_token ( p ) ) {
            /* Template application found */
            TYPE s = NULL_type ;
            IDENTIFIER rid = NULL_id ;
            DECL_SPEC acc = dspec_none ;
            if ( !IS_NULL_type ( q ) && IS_type_token ( q ) ) {
                /* Check for specialised template */
                INSTANCE spec = DEREF_inst ( type_token_app ( q ) ) ;
                s = DEREF_type ( inst_form ( spec ) ) ;
                rid = DEREF_id ( inst_templ_id ( spec ) ) ;
                acc = DEREF_dspec ( inst_templ_access ( spec ) ) ;
            }
            if ( !( acc & dspec_template ) ) {
                /* Not specialised - use primary template */
                rid = DEREF_id ( type_token_tok ( p ) ) ;
                if ( IS_id_class_name ( rid ) ) {
                    s = DEREF_type ( id_class_name_defn ( rid ) ) ;
                } else {
                    s = DEREF_type ( id_function_etc_type ( rid ) ) ;
                }
                acc = dspec_main ;
            }
            if ( IS_type_templ ( s ) ) {
                /* Bind template parameters */
                int d ;
                LIST ( TOKEN ) args ;
                LIST ( IDENTIFIER ) pids ;
                TOKEN sort = DEREF_tok ( type_templ_sort ( s ) ) ;
                pids = DEREF_list ( tok_templ_pids ( sort ) ) ;
                if ( acc & dspec_main ) {
                    /* Bound to primary template */
                    args = DEREF_list ( type_token_args ( p ) ) ;
                } else {
                    /* Bound to template specialisation */
                    args = NULL_list ( TOKEN ) ;
                }
                d = save_token_args ( pids, args ) ;
                if ( IS_NULL_list ( args ) ) {
                    force_template++ ;
                    s = DEREF_type ( type_templ_defn ( s ) ) ;
                    if ( !eq_type ( s, p ) ) {
                        FAIL ( bind_template failed ) ;
                        bound = 0 ;
                    }
                    force_template-- ;
                }

                /* Bind parent templates */
                if ( is_templ_depend ( p ) ) bound = 0 ;
                if ( IS_id_class_name ( rid ) ) {
                    TYPE t ;
                    CLASS_TYPE cs ;
                    s = DEREF_type ( id_class_name_defn ( rid ) ) ;
                    while ( IS_type_templ ( s ) ) {
                        s = DEREF_type ( type_templ_defn ( s ) ) ;
                    }
                    cs = DEREF_ctype ( type_compound_defn ( s ) ) ;
                    s = DEREF_type ( ctype_form ( cs ) ) ;
                    t = DEREF_type ( id_class_name_defn ( tid ) ) ;
                    COPY_type ( ctype_form ( cs ), t ) ;
                    bound = bind_template ( pid, id, qid, e, bound ) ;
                    COPY_type ( ctype_form ( cs ), s ) ;
                } else {
                    bound = bind_template ( pid, id, qid, e, bound ) ;
                }
                restore_token_args ( pids, d ) ;

            } else {
                /* Explicit specialisation */
                bound = 0 ;
            }

        } else {
            /* Bind parent templates */
            bound = bind_template ( pid, id, qid, e, bound ) ;
        }
    }
    return ( bound ) ;
}


/*
    DEFINE A TEMPLATE MEMBER

    This routine defines the template member id given by form to be the
    specialisation tid of value e.  It returns the declaration specifiers
    of the result.
*/

DECL_SPEC define_templ_member
    PROTO_N ( ( id, tid, form, e ) )
    PROTO_T ( IDENTIFIER id X IDENTIFIER tid X TYPE form X EXP e )
{
    ERROR perr ;
    DECL_SPEC ds ;
    PTR ( LOCATION ) ploc ;
    dump_template++ ;
    report_instance ( form ) ;
    ploc = MAKE_ptr ( SIZE_loc ) ;
    COPY_loc ( ploc, crt_loc ) ;
    perr = set_prefix ( ERR_temp_inst_comment ( form, ploc ) ) ;
    if ( incr_value ( OPT_VAL_instance_depth ) ) {
        /* Bind template arguments and copy function */
        LOCATION loc ;
        loc = crt_loc ;
        bad_crt_loc++ ;
        DEREF_loc ( id_loc ( tid ), crt_loc ) ;
        IGNORE bind_template ( id, id, tid, e, 1 ) ;
        ds = DEREF_dspec ( id_storage ( id ) ) ;
        clear_templates ( 0 ) ;
        crt_loc = loc ;
        bad_crt_loc-- ;
    } else {
        /* Instantiation depth too great */
        ds = DEREF_dspec ( id_storage ( id ) ) ;
    }
    decr_value ( OPT_VAL_instance_depth ) ;
    restore_prefix ( perr ) ;
    DESTROY_ptr ( ploc, SIZE_loc ) ;
    dump_template-- ;
    return ( ds ) ;
}


/*
    DEFINE A TEMPLATE

    This routine defines the template application id.  force is true to
    indicate the end of the translation unit when a non-exported template
    should have been defined.
*/

static void copy_template
    PROTO_N ( ( id, force ) )
    PROTO_T ( IDENTIFIER id X int force )
{
    /* Find the template information */
    EXP e = NULL_exp ;
    TYPE form = NULL_type ;
    IDENTIFIER tid = NULL_id ;
    DECL_SPEC ds = DEREF_dspec ( id_storage ( id ) ) ;
    switch ( TAG_id ( id ) ) {
        case id_function_tag :
        case id_mem_func_tag :
        case id_stat_mem_func_tag : {
            /* Template functions */
            e = DEREF_exp ( id_function_etc_defn ( id ) ) ;
            if ( !IS_NULL_exp ( e ) ) {
                /* Template already defined */
                return ;
            }
            form = DEREF_type ( id_function_etc_form ( id ) ) ;
            tid = match_specialise ( id, NULL_id ) ;
            if ( !IS_NULL_id ( tid ) ) {
                /* Find function definition */
                DECL_SPEC tds = DEREF_dspec ( id_storage ( tid ) ) ;
                if ( ( tds & dspec_instance ) && !( tds & dspec_defn ) ) {
                    /* This is itself a template */
                    if ( !EQ_id ( tid, id ) ) copy_template ( tid, force ) ;
                }
                e = DEREF_exp ( id_function_etc_defn ( tid ) ) ;
            }
            break ;
        }
        case id_stat_member_tag : {
            /* Static data members */
            int undef = 0 ;
            e = DEREF_exp ( id_stat_member_init ( id ) ) ;
            if ( !IS_NULL_exp ( e ) ) {
                /* Template member already defined */
                return ;
            }
            form = find_form ( id, &undef ) ;
            tid = match_specialise ( id, NULL_id ) ;
            if ( !IS_NULL_id ( tid ) ) {
                /* Find static member definition */
                DECL_SPEC tds = DEREF_dspec ( id_storage ( tid ) ) ;
                if ( ( tds & dspec_instance ) && !( tds & dspec_defn ) ) {
                    /* This is itself a template */
                    if ( !EQ_id ( tid, id ) ) copy_template ( tid, force ) ;
                }
                e = DEREF_exp ( id_stat_member_init ( tid ) ) ;
            }
            if ( force == 2 ) {
                /* Check for constants only */
                if ( IS_NULL_exp ( e ) ) return ;
                switch ( TAG_exp ( e ) ) {
                    case exp_int_lit_tag : break ;
                    case exp_null_tag : break ;
                    default : return ;
                }
            }
            break ;
        }
    }

    /* Check for exported templates */
    if ( is_exported ( tid ) ) {
        export_template ( id, 0 ) ;
        ds = DEREF_dspec ( id_storage ( id ) ) ;
    }

    /* Define the object */
    if ( IS_NULL_exp ( e ) ) {
        if ( force && !is_exported ( id ) ) {
            /* Check for non-exported templates */
            if ( !( ds & dspec_inline ) ) {
                report ( crt_loc, ERR_temp_decl_undef ( id ) ) ;
                export_template ( id, 0 ) ;
                ds = DEREF_dspec ( id_storage ( id ) ) ;
            }
        }
        CONS_id ( id, still_pending_templates, still_pending_templates ) ;
        ds &= ~dspec_defn ;
    } else {
        ds = define_templ_member ( id, tid, form, e ) ;
        ds |= dspec_defn ;
    }
    COPY_dspec ( id_storage ( id ), ds ) ;
    return ;
}


/*
    DEFINE A LIST OF TEMPLATES

    This routine calls copy_template for each element of the list p.
*/

static void copy_template_list
    PROTO_N ( ( p, force ) )
    PROTO_T ( LIST ( IDENTIFIER ) p X int force )
{
    if ( !IS_NULL_list ( p ) ) {
        IDENTIFIER id ;
        DESTROY_CONS_id ( destroy, id, p, p ) ;
        copy_template_list ( p, force ) ;
        DEREF_loc ( id_loc ( id ), crt_loc ) ;
        copy_template ( id, force ) ;
    }
    return ;
}


/*
    DEFINE ALL PENDING TEMPLATES

    This routine defines all the template instances in the list of pending
    templates.  The list of still pending templates is only checked if
    templ is nonzero.  A templ value of 2 is used to indicate the end of
    the file.
*/

void clear_templates
    PROTO_N ( ( templ ) )
    PROTO_T ( int templ )
{
    if ( !in_function_defn && !in_class_defn ) {
        LIST ( IDENTIFIER ) p = pending_templates ;
        if ( templ ) {
            /* Include still pending templates */
            p = APPEND_list ( still_pending_templates, p ) ;
            still_pending_templates = NULL_list ( IDENTIFIER ) ;
        }
        if ( !IS_NULL_list ( p ) ) {
            LOCATION loc ;
            loc = crt_loc ;
            bad_crt_loc++ ;
            while ( !IS_NULL_list ( p ) ) {
                /* Scan through pending templates */
                pending_templates = NULL_list ( IDENTIFIER ) ;
                copy_template_list ( p, 0 ) ;
                p = pending_templates ;
            }
            if ( templ == 2 ) {
                /* Check for exported templates */
                p = still_pending_templates ;
                if ( !IS_NULL_list ( p ) ) {
                    still_pending_templates = NULL_list ( IDENTIFIER ) ;
                    copy_template_list ( p, 1 ) ;
                }
            }
            crt_loc = loc ;
            bad_crt_loc-- ;
        }
    }
    return ;
}