/*
* Copyright (c) 2002, The Tendra Project <http://www.ten15.org/>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice unmodified, this list of conditions, and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Crown Copyright (c) 1997, 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.
*
* $TenDRA: tendra/src/producers/common/construct/instance.c,v 1.10 2004/08/15 11:13:35 bp Exp $
*/
#include "config.h"
#include "producer.h"
#include "msgcat.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(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(LIST (TOKEN) p, 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(IDENTIFIER id, LIST (IDENTIFIER) pids, LIST (TOKEN) args,
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(GRAPH gr, CLASS_TYPE ct, 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(TYPE t, TYPE s, 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(TYPE t, TYPE s, 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(IDENTIFIER id, LIST (EXP) args, int qual, int force, int create,
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(IDENTIFIER id, TYPE t, 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(TYPE t, 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(IDENTIFIER id, 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(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(IDENTIFIER tid, TOKEN tok, LIST (TOKEN) args, 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(IDENTIFIER id, TOKEN sort, 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(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(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(INSTANCE app, 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(INSTANCE app, TYPE form, 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(INSTANCE spec, 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(TYPE form, 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(IDENTIFIER cid, IDENTIFIER id, int res, 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(IDENTIFIER id, 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(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(TYPE form, 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(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(IDENTIFIER id, LIST (TOKEN) args, int func, 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(IDENTIFIER id, LIST (TOKEN) args, int type, 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(CLASS_TYPE ct, 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(LIST (IDENTIFIER) pids, 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(IDENTIFIER tid, INSTANCE spec, 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(IDENTIFIER id, DECL_SPEC ds, 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(IDENTIFIER *pid, TYPE t, TYPE form, int type, 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(IDENTIFIER *pid, TYPE t, DECL_SPEC ds, int type, int force,
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, NULL, 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(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(IDENTIFIER tid, 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(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(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(IDENTIFIER id, 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(IDENTIFIER tid, IDENTIFIER id, IDENTIFIER sid, EXP e, 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(IDENTIFIER id, IDENTIFIER tid, TYPE form, 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(IDENTIFIER id, 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(LIST (IDENTIFIER) p, 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(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;
}
syntax highlighted by Code2HTML, v. 0.9.1