/*ident "@(#)cls4:src/template.c 1.72" */ /******************************************************************************* C++ source for the C++ Language System, Release 3.0. This product is a new release of the original cfront developed in the computer science research center of AT&T Bell Laboratories. Copyright (c) 1993 UNIX System Laboratories, Inc. Copyright (c) 1991, 1992 AT&T and UNIX System Laboratories, Inc. Copyright (c) 1984, 1989, 1990 AT&T. All Rights Reserved. THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE of AT&T and UNIX System Laboratories, Inc. The copyright notice above does not evidence any actual or intended publication of such source code. *******************************************************************************/ /****************************************************************************** * Copyright (c) 1989 by Object Design, Inc., Burlington, Mass. * All rights reserved. *******************************************************************************/ /******************************************************************* * template.c * * TBD * * 1) The template copying process could probably be speeded up * substantially, by only placing "graph-like" nodes in the hash * table. The current implementation plays it safe, and places * all nodes in the hash table. * * 2) remove `$' in lex.c * * 3) Add something to lalex to handle x>. Currently parsed * as a right shift rather than nested template instance. *********************************************************************/ #include "tree_copy.h" #include "cfront.h" #include #include "template.h" #include #include #include #include "hash.h" extern int bound; // is not mentioned in the header file bit tempdcl; static Ptempl_inst dummyinst=0; static Ptempl_inst curr_inst=0; static Pfunct_inst fdummyinst=0; static Pfunct_inst fcurr_inst=0; static bit notinstflag=0; //static bit matchflag=0; Ptable bound_expr_tbl; const int max_string_size = 1024; const int default_copy_hash_size = 1000; const int MAX_INST_DEPTH = 16; // maximum instantiations at one time // static data member definitions Pfunt templ_compilation::f_list=0; Pbase_inst basic_inst::head=0; Ptempl templ_compilation::list=0; Pcons templ_compilation::last_cons=0; Pcons templ_compilation::last_friend_cons=0; Pcons templ_compilation::templ_refs=0; Pcons templ_compilation::friend_templ_refs=0; Ptstate templ_compilation::save_templ=0; Ptempl_base templ_compilation::parsed_template=0; Pfunt templ_compilation::f_owner=0; Ptempl templ_compilation::owner=0; Plist templ_compilation::param_end=0; Plist templ_compilation::params=0; Pexpr templ_compilation::actuals=0; bool templ_compilation::formals_in_progress=false; // The canonical template compilation instance. templ_compilation *templp; Ptable templ_compilation::templates=0; // Save and restore global state around a template instantiation void state::save() { Cdcl = ::Cdcl; Cstmt = ::Cstmt; curloc = ::curloc; curr_file = ::curr_file; curr_expr = ::curr_expr; curr_icall = ::curr_icall; curr_loop = ::curr_loop; curr_block = ::curr_block; curr_switch = ::curr_switch; bound = ::bound ; inline_restr = ::inline_restr; last_line = ::last_line; } void state::restore() { ::Cdcl = Cdcl; ::Cstmt = Cstmt; ::curloc = curloc; ::curr_file = curr_file; ::curr_expr = curr_expr; ::curr_icall = curr_icall; ::curr_loop = curr_loop; ::curr_block = curr_block; ::curr_switch = curr_switch; ::bound = bound; ::inline_restr = inline_restr; ::last_line = last_line; } void state::init() { ::bound = 0; ::inline_restr = 0; // lastline needs to be initialized probaly via a call to putline } #if 0 bit basetype::parametrized_class() { return ((base == COBJ) && Ptclass(Pbase(this)->b_name->tp)->class_base==UNINSTANTIATED); } bit classdef::parametrized_class() { return (class_base == UNINSTANTIATED); } #endif Templ_type get_class_base(Pbase b) { if (b->base != COBJ) error('i',"::get_class_base: badAT(%k) cobjX",b->base); return Ptclass(Pbase(b)->b_name->tp)->class_base ; } Templ_type get_templ_base(Pbase b) { if (b->base != COBJ) error('i',"::get_templ_base: badAT(%k) cobjX",b->base); return Pclass(Pbase(b)->b_name->tp)->templ_base; } Ptclass get_template_class(Pbase b) { Templ_type t = get_class_base(b) ; if (! ((t==INSTANTIATED) || (t==UNINSTANTIATED))) error ('i', "C is not aYC"); return Ptclass(Pbase(b)->b_name->tp); } #if 0 Ptempl_inst get_templ_inst(Pbase b) { return (get_template_class(b))->inst; } #endif static bit same_class_templ(Pclass c1, Pclass c2) { // error('d',"same_class_templ: c1 %s c2 %s c1 %d c2 %d", c1->string, c2->string, c1->class_base, c2->class_base); if (c1 == c2) return true; if ((c1->class_base == INSTANTIATED) && (c2->class_base == UNINSTANTIATED) && (Ptclass(c1)->inst->def== Ptclass(c2)->inst->def)) return true; if ((c2->class_base == INSTANTIATED) && (c1->class_base == UNINSTANTIATED) && (Ptclass(c1)->inst->def== Ptclass(c2)->inst->def)) return true; return false; } bit classdef::same_class(Pclass pc,int access) /* Predicate to determine whether two classes are indeed the same. * cfront normally relies on pointer identity, however, this test * is insufficient when parametrized class instantiationa are involved, * since there are potentially many instances of a COBJ and CLASS * for a given instantiation. * * it would seem reasonable to always have the occurrence of * an UNINSTANTIATED and INSTANTIATED instance be checked; * this occurs, for example, in * template class Tag{}; * template struct Shrub { * struct Link { Link* next; }; * int advance(Tag&); // UNINSTANTIATED * }; * * void f() { * Shrub s; Tag t; * s.advance(t); } // INSTANTIATED -- same class! * * however, it seems that same_class also determines whether or not * an instantiation needs to be done??? and so currently making this * a blanket check causes certain classes under certain conditions * not to be instantiated -- if this is truly the case, this should * be broken into two functions */ { // error('d',"%t->same_class(%t, %d)",this,pc,access); if (this == 0 || pc == 0 ) return false; if (this == pc) return true; if (class_base==VANILLA && pc->class_base==VANILLA) { if (local_sig && pc->local_sig) { return strcmp(local_sig, pc->local_sig)==0; } if (nested_sig && pc->nested_sig) { return strcmp(nested_sig, pc->nested_sig)==0; } if (local_sig==pc->local_sig && nested_sig==pc->nested_sig) { return strcmp(string, pc->string)==0; } return false; } Templ_type this_base = this->class_base; Templ_type pc_base = pc->class_base; if ((this_base == CL_TEMPLATE) && (pc_base == INSTANTIATED) && (Ptclass(pc)->inst->def_basetype()->b_name->tp==this)) return true; // The inverse symmetric test if ((pc_base == CL_TEMPLATE) && (this_base == INSTANTIATED) && (Ptclass(this)->inst->def_basetype()->b_name->tp==pc)) return true; // Check whether the templates were determined to be // identical after instantiation. if ((pc_base == INSTANTIATED) && (this_base == INSTANTIATED) && (strcmp(pc->string,string)==0 || (Ptclass(this)->inst->same(Ptclass(pc)->inst)))) return true; if (access == 0) return false; // change to treatment of friend instantiation // now makes this a possibility if (this_base == INSTANTIATED && pc_base == UNINSTANTIATED) { char *str = Ptclass(this)->inst->def->namep->string; // error('d',"same_class: this: %s pc: %s", str, pc->string); return (strcmp(str,pc->string)==0); } if (pc_base == INSTANTIATED && this_base == UNINSTANTIATED) { char *str = Ptclass(pc)->inst->def->namep->string; // error('d',"same_class: pc: %s this: %s", str, string); return (strcmp(str,string)==0); } return false; } bool templ_inst::same(Ptempl_inst t) /* determine whether two instantiations are identical; * test asumes that the templates have been instantiated. */ { return ((forward && (forward==t->forward)) || (forward == t) || (t->forward == this)) ? true : false; } Ptempl templ_compilation::is_template(Pname p) /* Determine whether the name refers to the canonical * template class during syntax analysis */ { // error('d',"is_template(%n)",p ); if (p->tp && (p->tp->base==COBJ)) { Templ_type tc = get_class_base(Pbase(p->tp)); Templ_type sc = get_templ_base(Pbase(p->tp)); if (tc == CL_TEMPLATE || (tc == INSTANTIATED && sc == CL_TEMPLATE)) { Pname n = templates->look(p->string,0); return (n ? Ptempl(n->tp) : 0); } else return 0; } return 0; } Ptempl templ_compilation::is_template(char *s) { // error('d',"is_template(char *%s)",s ); Pname n = templates->look(s,0); return (n ? Ptempl(n->tp) : 0); } Pfunt templ_compilation::is_template(char *s, TOK t) { // error('d',"is_template(char *%s, %k)",s,t ); Pname n = templates->look(s,t); return (n ? Pfunt(n->tp) : 0); } void templ_compilation::start() /* Set up the environment for parsing a template. * This involves setting up a new nesting level into * which the "type type" template parameters can be * entered so that the lexer can find them as TNAMEs. * templ_compilation::collect adds new types. */ { templp->in_progress = true ; params = param_end = 0; owner = 0; f_owner = 0; parsed_template = 0; // has_expr_formals = 0; // SYM modified_tn = 0; } void templ_compilation::collect(TOK parm_type, Pname n) /* Collect each parameter as it is parsed, adding * it to the list of parms. Validate each parameter */ { // ::error('d',"templ_compilation::collect(%k,%n)",parm_type,n); switch (parm_type) { case STRUCT: case CLASS: /* A "type type" parameter ... * tp: ``any_type'', a wildcard match * tdef: base==TNAME, inserts in ktbl, sets modified_tn * lex_level: +1, so restore() can HIDE it after */ n->tp = ::any_type; n = n->tdef(); n->lex_level = bl_level+1; n->n_template_arg = template_type_formal; break; default: error("theZT for%n must beC or struct, not%k", n, parm_type); break; } append_parameter(n); } void templ_compilation::append_parameter(Pname n) { // append the "non-type" parameter to the end of the list // ::error('d',"templ_compilation::append(%n)",n); if (params) { param_end->l = new name_list(n,0); param_end = param_end->l; } else params = param_end = new name_list(n,0); PERM(n); PERM(n->tp); } void templ_compilation::collect(Pname n) /* Collect non "type type" parameters. * tp indicates type of the formal parameter */ { // ::error('d',"templ_compilation::collect(%n)",n); // grammar should be sufficient to protect against undesirable // types. Any additional checks go here. n->n_template_arg = template_expr_formal; extern int must_be_friend; if (must_be_friend == 0) bound_expr_tbl->insert(n,0); append_parameter(n) ; } static void check_non_type_formal(Pname n) { // validate the type for a non-type formal // error('d',"check_non_type_formal: %n %k", n, n->tp->base); Ptype tp = n->tp->skiptypedefs(); switch (tp->base) { // case W_CHAR: // case W_STRING: case FLOAT: case DOUBLE: case LDOUBLE: if (tp->base==LDOUBLE && ansi_opt==0) error('w',"long double supported under ``+a1'' option only, generating ``double%n''",n); error('s',"ZE ofT float, double, or long double"); return; case ZTYPE: case CHAR: case SHORT: case INT: case LONG: case FIELD: case EOBJ: case COBJ: // case TYPE: case ANY: { // a basetype node TOK bad_base = 0; if (Pbase(n->tp)->b_volatile) bad_base=VOLATILE; // ? ?? ??? ???? ????? // if (Pbase(n->tp)->b_typedef) bad_base=TYPEDEF; if (Pbase(n->tp)->b_inline) bad_base=INLINE; if (Pbase(n->tp)->b_virtual) bad_base=VIRTUAL; if (bad_base) error ("bad %k declarator forY formal %n", bad_base,n); goto hack; } case PTR: { if (tp != n->tp) { hack: Pbase b = new basetype(0,0); *b= *Pbase(n->tp); // see const_formal_hack() for explanation if (b->b_const == 0) b->b_const = 2; n->tp = b; break; } Pptr b = new ptr(0,0); *b = *Pptr(n->tp); if (b->b_const == 0) b->b_const = 2; n->tp = b; break; } case RPTR: case VEC: break; // constant by definition default: error("badZT%t for formalZ %n",tp,n); } return; } void templ_compilation::enter_parameters() /* The template parameters, if any, have been parsed. * Member function templates may choose to default their * template arguments to the class arguments -- if so, * make the defaulting happen */ { for (Plist list = params; list; list = list->l) { Pname n = list->f; switch(n->n_template_arg) { case template_type_formal: // SYM -- param should be in parse table... // Bring the names out of hiding n->n_key = 0; break; case template_expr_formal: check_non_type_formal(n); n->tp->dcl(gtbl); break; default: error ('i', "bad template formal" ) ; } } // SYM -- tn stuff no longer needed // param_tn = modified_tn ; // modified_tn = 0 ; } void templ::resolve_forward_decl(Plist params, Pclass c) /* Resolve the forward declaration of a template to its * true definition. The template and class type data * structures must be reused, since there may be * outstanding references to them. */ { check_formals(params); formals = params; defined = true; members = c->mem_list; } static bit reinstat = 0; /* 3.1/4.0: merge there two */ void templ::instantiate_forward_decl() { for (Ptempl_inst i = insts ; i ; i = i->next) if (Ptclass(Pbase(i->tname->tp)->b_name->tp)->class_base == INSTANTIATED && ! i->forward) { // reinstantiate it reinstat = 1; i->instantiate(true) ; reinstat = 0; } } void function_template::instantiate_forward_decl() { for (Pfunct_inst i = insts ; i ; i = i->next) if (Ptfct(i->tname->tp)->fct_base == INSTANTIATED) { // reinstantiate it i->instantiate(true) ; } } // verify that the qualifier used to decl the member function matches the // template arguments in name, ie. // template c::member_function() {} // match it's Ps and Qs. bool templ_inst::check_qualifier(Plist formals) { Pexpr actual = actuals ; for (Plist formal = formals ; formal && actual ; formal = formal->l, actual = actual->e2) switch (formal->f->n_template_arg) { case template_type_formal: { Pbase b = Pbase(actual->e1->tp) ; if (! ((b->base == TYPE) && (b->b_name->base == TNAME) && (strcmp (Pname(b->b_name)->string, formal->f->string) == 0))) return false ; break ; } case template_expr_formal: if (! ((actual->e1->base == NAME) && (strcmp(Pname(actual->e1)->string, formal->f->string) == 0))) return false ; break ; default: error ('i',"bad template formal") ; } return true ; } extern int add_first; void introduce_global_name( Pname ft ) { // error( 'd', "introduce_global(%n)", ft ); Pname n = gtbl->look(ft->string,0); if ( n == 0 ) { Pname nn = gtbl->insert(new name(ft->string),0); nn->n_template_fct = 1; if (ft->n_sto==FRIEND) { nn->n_scope = EXTERN; } nn->tp = ft->tp; PERM(nn); } else { // error( 'd', "introduce_global(%n) n: %n %t", ft, n, n->tp ); switch( n->tp->base ) { default: error("YF%n renamed object ofT%t",ft,n->tp); break; case OVERLOAD: { Pname n2 = Pgen(n->tp)->add(ft); n2->n_template_fct++; n->n_template_fct++; } break; case FCT: { Pgen g = new gen; PERM(g); add_first = 1; (void) g->add(n); add_first = 0; Pname n2 = g->add(ft); n2->n_template_fct++; n->tp = g; n->n_template_fct++; } break; } } } void templ_compilation::introduce_class_templ(Pname namep) /* make the class template visible when compiling the * template class definition, * so that it can be referenced while compiling the class body. */ { // error('d',"introduce_class_templ: %n", namep ); /* this check is because introduce_call_templ is invoked in two places: * st_class() in norm.c for class definition, and templ_compilation::end() * call at end() is always for a forward declaration */ owner = is_template(namep); if (!owner){ check_formals_for_dups(params); // place for it?? // create a template definition owner = new templ(params, namep); Pname nn = new name(namep->string); Pname lookup_name = templp->templates->insert(nn,0); lookup_name->tp = Ptype(owner); // lie, to permit use of the table } } void templ_compilation::introduce_funct_templ(Pname namep) { /* make the function template visible when compiling the * template function definition, * so that it can be referenced while compiling the function body. */ // error('d',"introduce_funct_templ: %n", namep ); Pfunt t = new function_template(params,namep); Pname n = templp->templates->look(namep->string,FCT); if ( n == 0 ) { Pname nn = new name(namep->string); n = templp->templates->insert(nn,FCT); } else t->gen_list = Pfunt(n->tp); n->tp = Ptype(t); f_owner = t; introduce_global_name(namep); } static int has_formal_type(Ptclass pt_cl, Plist list) { // error('d',"has_formal_type: %s", pt_cl->string); int has_formal = 0; for (Pexpr formals = pt_cl->inst->actuals; formals; formals=formals->e2) { Pexpr fe = formals->e1; if (fe->base != NAME || !fe->tp || fe->tp->base != TYPE) continue; Pname tn = fe->tp->bname(); if (!tn->is_template_arg()) continue; // error('d',"has_formal_type: found: %n", tn); list = list->l = new name_list(tn,0); ++has_formal; } return has_formal; } static Pname Tmpl; static int has_formal_type(Pname nn, Plist list) { // error('d',"has_formal_type: %n %t", nn, nn->tp); Pname bn; Ptype t, nt = nn->tp; while(t=nt->is_ptr()) nt = Pptr(t)->typ; while(t=nt->is_ref()) nt = Pptr(t)->typ; if(nt->base != TYPE) return 0; while (nt->base == TYPE) { bn = nt->bname(); nt = bn->tp; } if (nt->base == COBJ) { // declaration: X Pclass c1 = nt->classtype(); if (c1->is_templ_instance()) return has_formal_type(Ptclass(c1),list); // encapsulate later ... if (c1->class_base == CL_TEMPLATE) { // error('d',"class template: %n set to tmpl", bn); Tmpl = bn; return -1; } } if (bn->is_template_arg()) { // error('d',"has_formal_type: found: %n", bn); list = list->l = new name_list(bn,0); return true; } return false; } static bit hbf = 0; void handle_bound_friend(Ptempl_base pb, Pname fn) { /* point of this is to extrapolate the template function * declaration implicit within a declaration such as * friend min(X,X); */ // error('d',"handle_bound_friend (%n %n)", Ptempl(pb)->namep, fn); Plist formals, f_list = new name_list(0,0); int i = 0, formal_cnt = pb->get_formals_count(); Pfct f = fn->fct_type(); if (f->body) return; // no need to extrapolate struct bleh { Pname n; int used; }; bleh *pbleh = new bleh[formal_cnt]; for (formals = pb->get_formals(); formals; formals=formals->l) { pbleh[i].n = formals->f; pbleh[i++].used = 0; } int has_formal = 0; for (Pname n = f->argtype; n; n=n->n_list) // build up f_list of formals in signature has_formal += has_formal_type(n,f_list); if (!has_formal) return; // *** XXX : incomplete: what if f(pb<...>,T,...) if (has_formal == -1) { // f(pb<...> if (strcmp(Tmpl->string,Ptempl(pb)->namep->string)) return; // pb is formal argument to function: all formals used // error('d',"has_formal == -1, %n == %n",Tmpl,Ptempl(pb)->namep); } else for (formals=f_list->l; formals; formals=formals->l) { Pname n = formals->f; if (!n) break; for (i = 0; i < formal_cnt; ++i ) if (strcmp(n->string,pbleh[i].n->string)==0) { pbleh[i].used++; // error('d',"match: %n used: %d", pbleh[i].n, pbleh[i].used); break; } } templp->save_templ = new templ_state; templp->params = templp->param_end = 0; for (i = 0; i < formal_cnt; ++i ) { if (pbleh[i].used == 0 && has_formal != -1) continue; templp->append_parameter(pbleh[i].n); // error('d',"i %d t_list: %n", i, pbleh[i].n); } // unnecessary when class and functions share owner templp->owner = 0; // restored by delete, below hbf = 1; templp->end(fn); f->fct_base = FCT_TEMPLATE; Ptempl_base fr = templp->parsed_template; // set to f_owner delete templp->save_templ; templp->save_templ = 0; Pclass cl = Ptempl(pb)->classtype(); cl->templ_friends = new cons(fr,cl->templ_friends); fr->extrapolated = 1; if (hbf == 1) // set to 2 within templ_compilation::end() fr->templ_refs = templp->friend_templ_refs; hbf = 0; // error('d',"handle_bound: cl %t is_extrap: %d templ_refs: %d", cl?cl:0, fr->is_extrapolated(), fr->templ_refs); } void check_templ_funct(Pname fn) { // error('d', "check_templ_funct( %n ): n_oper: %k",fn,fn->n_oper); Pfct f = Pfct(fn->tp); for (Pname al = f->argtype; al; al = al->n_list) { if (al->n_initializer) { error('s', "FYs do not support defaultAs"); break; } } if (f->nargs_known != 1) error('s',"ellipsis (...) inA list ofYF%n",fn); if ( fn->n_oper ) { switch (f->nargs) { case 1: case 2: break; default: if (fn->n_oper!=NEW) error("FY%n must take 1 or 2As",fn); } switch (fn->n_oper) { case CALL: case DEREF: case REF: case ASSIGN: error("YF operator%s must be aCM",keys[fn->n_oper]); return; case DELETE: error("::operator %s may not be aYF",keys[fn->n_oper]); return; default: fn->check_oper(0); break; } } if (strcmp("main",fn->string)==0) error("main() may not be aYF"); } #if 0 static void display_templ_refs() { error('d',"display_templ_refs :: templp\n"); for (Pcons p = templp->templ_refs; p; p = p->cdr) { Ptempl_inst pt = Ptempl_inst(p->car); if ( pt == 0 ) { error('d',"templ_refs: empty (%d)",p->car); continue; } error('d',"\ntempl_refs: %d->car:\n",p->car); error('d',"\ttname: %n namep: %n",pt->get_tname(),pt->get_namep()); error('d',"\tdef: %n refp: %d\n", pt->def->namep, pt->refp); } } static void display_templ_refs( basic_template *bt ) { error('d',"display_templ_refs :: basic_template\n"); for (Pcons p = bt->templ_refs; p; p = p->cdr) { Ptempl_inst pt = Ptempl_inst(p->car); if ( pt == 0 ) { error('d',"templ_refs: empty (%d)",p->car); continue; } error('d',"\ntempl_refs: %d->car:\n",p->car); error('d',"\ttname: %n namep: %n",pt->get_tname(),pt->get_namep()); error('d',"\tdef: %n refp: %d\n", pt->def->namep, pt->refp); } } #endif void templ_compilation::end(Pname p) /* The body of the template has been parsed. * Finish the definition of the template class */ { //error('d',"templ_compilation::end(%n) tp %k",p,p->tp?p->tp->base:0); // Restore the name state to that prior to // the processing of the template parameters //SYM -- need to restore tables... XXXXX ??? // since the lex_level of these paramenters was lex_level+1 // each will be hidden by restore() (n_key<==HIDDEN) // modified_tn = param_tn; // restore(); // modified_tn = 0; bool forward_definition = false; if (!p->tp) { error ("aC,F,MF or static dataM definition wasX"); return; } switch(p->tp->base) { case COBJ: if ( p->n_sto == FRIEND ) { p = p->tp->bname(); // error('d',"p %n tp %k ", p, p->tp->base ); // no break; } else { // should not happen: i.e., caught in grammar error("illegalY %n",p); return; } case CLASS: /* Create the template type to represent the parsed template, * entering it into the global table. This is achieved simply * by modifying the TNAME that was entered into ktbl to * represent the class definition. */ { // Pname namep = ktbl->look(p->string,0); // Pname namep = k_find_name(p->string,Ctbl,0);//SYM Pname namep = k_find_name(p->string,Gtbl,HIDDEN);//SYM if ( namep == 0 || namep->base==NAME ) // namep = 0; { char* s = (p->string ? p->string : ""); if (*s && (s[0] != '_' || s[1] != '_' || s[2] != 'C')) error("nestedYC %s", s); else { error("missingYN"); return; } } owner = is_template(namep); if (owner) { Pclass c = owner->classtype(); // ignore it, if it is a forward declaration // following a real definition if (owner->defined && (Pclass(p->tp)->mem_list != owner->members)) error("YC %s multiply defined", p->string); forward_definition = bool((c->defined & DEF_SEEN) && (!owner->defined)); if (forward_definition) owner->resolve_forward_decl(params, c); if (pt_opt && forward_definition) fprintf(pt_file,"t %s %s\n",p->string,curr_filename()); } // a forward declaration else introduce_class_templ(namep); if (owner->defined) { for (Pname nn=Pclass(p->tp)->mem_list; nn; nn=nn->n_list) { // look for implicit non-member template friend functions if (nn->base == NAME && nn->n_sto == FRIEND && nn->n_qualifier == 0) { switch(nn->tp->base) { case FCT: // error('d',"fct friend %s",nn->string); handle_bound_friend(owner,nn); break; case OVERLOAD: // error('d',"overload friend %s",nn->string); break; } } } } if (templ_refs) owner->templ_refs = templ_refs; templp->clear_friend_ref_templ(); break; } case FCT: { if (pt_opt && !p->fct_type()->body && !p->n_qualifier) fprintf(pt_file,"f %s %s\n",p->string,curr_filename()); Pname qual = p->n_qualifier; if (!qual) { // function template check_funct_formals(params,p); check_templ_funct(p); f_owner = is_template(p->string,FCT); if ( !f_owner ) introduce_funct_templ(p); else { Pfunt tl = f_owner; Pname fn = 0; Pfct n_fct = p->fct_type(); // is it an overloaded instance int error_cnt = 0; for (fn=tl->fn; tl; tl=tl->gen_list,fn=tl?tl->fn:0) { extern bit return_error; // set in type::check if (n_fct->check(fn->tp,PT_OVERLOAD)==0) break; if (error_cnt = return_error) break; } if (error_cnt) error("FY%n: declared twice with different returnTs",p); // error('d',"after gen_list: tl: %d", tl ); if (tl==0) // overloaded instance introduce_funct_templ(p); else { // redefinition int f1, f2, extrap = 0; Pfct o_fct = fn->fct_type(); if ((f1=n_fct->body!=0) && (f2=o_fct->body!=0)) error("two definitions ofYF%n (%t %t)",fn,o_fct,n_fct); // replace entry in template table for a forward definition // or if original definition is extrapolated friend function extrap = f_owner->is_extrapolated(); if ((forward_definition = (bool)(f1 && f2==0)) || extrap) { tl->formals = params; tl->fn = p; tl->templ_refs = templ_refs; f_owner = tl; } else if (hbf == 1) hbf = 2; } } if (f_owner->templ_refs == 0 && hbf != 2) f_owner->templ_refs = templ_refs; break; } Pbase q = Pbase(qual->tp); if (q && (q->base == COBJ)) switch (get_class_base(q)) { case UNINSTANTIATED: owner = Ptclass(q->b_name->tp)->inst->def; /* verify that the formals specified match the * template formals in name, note that the length * was already matched when the instantiation was * generated */ if (!get_template_class(q)->inst->check_qualifier(params)) error ("QrZs must match theY formalZs"); break; case CL_TEMPLATE: // the template reference was without any of the formals error("Qr%n for%n must specifyYZs",qual,p); break; default: { Pclass cl = (Pclass)q->b_name->tp; if (cl && cl->in_class && cl->in_class->class_base) error('s',"out-of-line definition ofMF ofC nestedWinYC (%t::%n)",cl,p); else error ("Qr%n for%n wasn't aYC",qual,p); return; } } if (! p->fct_type()->body) error ("QdN%n::%n inYFD",qual,p); if (owner) { Pfunt ft= owner->collect_function_member(p); ft->templ_refs = templ_refs; ft->formals = params; owner->check_formals(params); } else error("memberFY must beQd byCYN"); break; } default: { Pname qual = p->n_qualifier; if ( !qual ) { error ("%n: only static dataCMs may beZized",p); return; } // *** SDM yes, this is currently duplicated from above Pbase q = Pbase(qual->tp); if (q && (q->base == COBJ)) switch (get_class_base(q)) { case UNINSTANTIATED: owner = Ptclass(q->b_name->tp)->inst->def; if (!get_template_class(q)->inst->check_qualifier(params)) error ("%n: QrZs must match theY formalZs",p); break; case CL_TEMPLATE: // the template reference was without any of the formals error("Qr%n for%n must specifyYZs",qual,p); break; default: error ("Qr%n for%n wasn't aYC",qual,p); return; } Pdata dat = owner->collect_data_member(p); dat->templ_refs = templ_refs; dat->formals = params; owner->check_formals(params); break; } } // Note the template references from this definition clear_ref_templ(); param_end = params = 0; // Indicates the end of template processing. if (forward_definition) { /* XXX: * make this one line of code based on * owner being a basic_template pointer */ if (owner) owner->instantiate_forward_decl(); else { f_owner->instantiate_forward_decl(); } } parsed_template = owner?(Ptempl_base)owner:(Ptempl_base)f_owner; owner = 0; f_owner = 0; } void templ_compilation::clear_friend_ref_templ() { for (Pcons p = friend_templ_refs; p; p = p->cdr) Ptempl_inst(p->car)->friend_refp = false; friend_templ_refs = 0; last_friend_cons = 0; } void templ_compilation::clear_ref_templ() { /* Clear list of templates referenced during syntax analysis of a top * level definition. Note that since this list is produced during syntax * analysis, it does not recognize instantiations that may actually turn * out to be identical at instantiation after the substitution of actual * parameters. Thus, the list may be longer than it would be after the * substitution of the actual argument */ for (Pcons p = templ_refs; p; p = p->cdr) Ptempl_inst(p->car)->refp = false; templ_refs = 0; last_cons = 0; } void templ_compilation::instantiate_ref_templ() /* Instantiate the class templates that were referenced * by a non-template definition, after compleletion of * syntax analysis on that definition */ { for (Pcons p = templ_refs; p; p = p->cdr) { if (p->cdr) notinstflag=1; else notinstflag=0; Ptempl_inst(p->car)->instantiate(); } clear_ref_templ(); } static void data_copy_hook(void*, Pnode &node, node_class, tree_node_action &action, int& never_see_again) { // error('d',"data_copy_hook: %k",node->base); never_see_again = 1; switch (node->base) { case NAME: if(node == sta_name) { action = tna_stop; return; } default: action = tna_continue; return; } } Pname templ_inst::data_copy(Pdata dat, Pcons &templ_refs) { pointer_hash fcorr(*corr); // initialize with old hash table { tree_copy_info info; Pnode root = dat->dat_mem; for (Plist fformal = dat->formals, cformal = inst_formals; fformal; fformal = fformal->l, cformal = cformal->l) { fcorr[int(fformal->f)] = int(cformal->f); if (fcorr[int(fformal->f)] != int(cformal->f)) error ('i', "templ_inst::fuction_copy: hash table bug"); } info.node_hook = ::data_copy_hook; info.hook_info = this; templ_refs = ref_copy(fcorr,info,templ_refs); if (fcorr[int(def->namep)] != int(tname)) error ('i', "Y to instantiationTN correspondence is missing"); copy_tree(root,info,&fcorr); return Pname(root); } } void templ_compilation::end_of_compilation() /* * Compile all template member body instantiations. * Set in motion the compilation of the graph of * instantiation bodies. Note that compilation of * a body may in turn initiate the instantiation of * templates that had not previously been instantiated. */ { bool change = false; do { change = false; for (Ptempl p = list; p; p = p->next) change = ( change | p->instantiate_bodies() ? true : false); } while (change); } bit name::dinst_body() { if ( data_flag==0 && all_flag==0 && alltc_flag==0 && curr_inst==dummyinst && curr_inst != 0 ) return 0; if ( all_flag==0 && curr_inst!=dummyinst && curr_inst!=0 ) return 0; bit instflag=0; for (int i=0;if_inline==0 ) return 0; if ( ft_flag && fdummyinst!=fcurr_inst && fcurr_inst!=0 && fct_type()->f_inline==0 ) return 0; return 1; } bit name::inst_body() { bit inst_flag=0; for (int i=0;if_virtual==0 && ft->f_inline==0 ) return 0; if ( datainstflag && curr_inst !=0 && curr_inst==dummyinst && ft->f_virtual && all_flag==0 && alltc_flag==0 ) return 0; if ( curr_inst==dummyinst && curr_inst != 0 && ft->f_virtual && ft->f_inline==0 && data_flag==0 && all_flag==0 && alltc_flag==0 ) return 0; //error('d',"this is %n f_inline is %d ft->f_virtual is %d",this,ft->f_inline,ft->f_virtual); //error('d',"this %n curr_inst %d dummyinst %d all_flag %d",this,curr_inst,dummyinst,all_flag); if ( all_flag==0 && curr_inst!=dummyinst && curr_inst!=0 && ft->f_inline==0 && ft->f_is_inline==0 ) return 0; //error('d',"about to return 1"); return 1; } bool templ::instantiate_bodies() /* Instantiate each member function body. * It assumes that the class declaration has been instantiated. * The return value indicates whether an instantiation of bodies * actually took place. * This function is only invoked at the end of a file compilation, * after all source text has been processed. * fns: list of member function declarations for template * insts: list of instantiations for template */ { bool change = false; if (!fns && !data) return change; for (Ptempl_inst inst = insts; inst; inst = inst->next) // error('d',"instantiate_bodies: %n inst->status: %d", inst->get_tname(), inst->status ); if (!inst->forward && (inst->status==class_instantiated)) { // Set up the environment for the declaration, // and subsequent compilation of function bodies inst->status = body_instantiated; change = true; Pclass ic = inst->get_class(); { int i; // note the overriding definitions explicitly provided for (Pname fn=ic->memtbl->get_mem(i=1); fn; NEXT_NAME(ic->memtbl,fn,i)) if ((fn->base == NAME) && (fn->tp->base == FCT) && (fn->fct_type()->body)) fn->n_redefined = 1; // note overriding definitions } cc->stack(); cc->cot=0; cc->not=0; cc->tot=0; cc->c_this=0; for (Pfunt fnt=fns; fnt; fnt=fnt->next) { Pcons templ_ref_copy = fnt->templ_refs; Pname fn = inst->function_copy(fnt,templ_ref_copy); // Change qualifier to name of the instantiated, // rather than parameterized class name fn->n_qualifier = inst->namep; // cond contains type information // shares space with n_table if (fn->n_oper != TYPE ) fn->n_table = 0; fn->n_tbl_list = 0; // Note that the formals were bound to actuals // when the class decl was instantiated, // so the binding is not redone. // Modify constructor and destructor names. if (!strcmp(fn->string, namep->string)) fn->string = inst->namep->string; { inst->save_state(fn); // Instantiate the parametrized types // referenced by this template for (Pcons pc=templ_ref_copy; pc; pc=pc->cdr) Ptempl_inst(pc->car)->instantiate(); curr_inst=inst; if (((fn=fn->dcl(gtbl,EXTERN))==0) || error_count) { inst->restore_state(); continue; } curr_inst=inst; if (se_opt && fn->inst_body()==0) suppress_error++; fn->simpl(); if (se_opt && fn->inst_body()==0) { suppress_error--; } if (dtpt_opt && fn->inst_body()==0) fn->fct_type()->body=0; fn->dcl_print(0); curr_inst=0; inst->restore_state(); } } cc->unstack(); inst->status = data_instantiated; cc->stack(); cc->cot=0; cc->not=0; cc->tot=0; cc->c_this=0; for (Pdata dat=data; dat; dat=dat->next) { Pcons templ_ref_copy = dat->templ_refs; Pname dn = ic->memtbl->look(dat->dat_mem->string,0); /* do other checks here for explicit definitions */ if (!dn || dn->n_stclass != STATIC) error("%n: only static dataMs can beZized",dat->dat_mem); if (dn->n_redefined) continue; // explicitly defined Pname n = inst->data_copy(dat,templ_ref_copy); n->n_qualifier = inst->namep; n->n_table = 0; n->n_tbl_list = 0; inst->save_state(n); for (Pcons pc=templ_ref_copy; pc; pc=pc->cdr) Ptempl_inst(pc->car)->instantiate(); curr_inst=inst; if (((n=n->dcl(gtbl,EXTERN))==0) || error_count) { inst->restore_state(); continue; } n->simpl(); mk_zero_init_flag=0; if (n->n_stclass==STATIC && n->n_initializer==0 && n->n_evaluated==0) n->n_initializer=mk_zero_init(n->tp,n,n); if ( dtpt_opt && n->dinst_body()==0 ) { n->n_initializer=0; mk_zero_init_flag=1; } if (n->n_stclass != STATIC || !mk_zero_init_flag || !strict_opt) n->dcl_print(0); curr_inst=0; mk_zero_init_flag=0; n->n_dcl_printed = 2; // suppress further printing inst->restore_state(); } cc->unstack(); } return change; } Pname templ_compilation::check_tname(Pname p) /* A predicate to validate that a tname without template parameters is legit * in the scope; i.e., that it does not need actual template arguments. * Currently, a tname without parameters is ok within the class definition, * but parameters are required within the member definition. They should not * be required within the member function either to be consonance with their * use in the class definition. */ { // error('d', "templ_compilation::check_tname(%n) t_arg: %d",p,p->n_template_arg); Ptempl t = is_template(p); if (p->n_template_arg) p->n_used++; if (!t) return p; extern int in_friend; if (in_progress && in_friend == 0 && ((owner && (owner->namep == p)) || (!owner && same_class(t->classtype(),ccl)))) return p; // explicit instance extern Ptempl_inst pti; if (pti && pti->def == t) return p; error ("%n needsY instantiationAs",p); return p; } bool templ_compilation::current_template(Pname p, Pexpr actuals) /* * Determine if parameters specified to a template are * redundant, and really refer to the current template * class. Thus, * * template c c::foo * { ... }; * * has the redundant template specification c * and can simply be a reference to a "c" instead */ { // error('d',"templ_compilation::current_template(%n) in_progress: %d",p,in_progress); Ptype t; if (Pbase(p->tp) && Pbase(p->tp)->b_name) t = Pbase(p->tp)->b_name->tp; else t = 0; if (in_progress && ((owner && (owner->namep == p)) || (!owner && ((p->tp->base == COBJ) && (t == ccl))))) { // Check whether the formal and actual types are identical Pexpr actual = actuals; for (Plist formal = params; formal && actual; formal = formal->l, actual = actual->e2) { if (formal->f->n_template_arg == template_type_formal) { if ( formal->f->tp==::any_type && actual->e1->tp && actual->e1->tp->base==TYPE && Pbase(actual->e1->tp)->b_name->tp==::any_type && strcmp(Pbase(actual->e1->tp)->b_name->string,formal->f->string)==0 ) continue; else break; } else //(formal->f->n_template_arg == template_expr_formal) { if ( formal->f->base==NAME && actual->e1->base==NAME && strcmp(Pname(actual->e1)->string,formal->f->string)==0 ) continue; else break; } } if (!formal && !actual) return true; } return false; } Pfunt templ::collect_function_member(Pname fname) { // Add a new member function to list of template class functions PERM(fname); PERM(fname->tp); PERM(Pfct(fname->tp)->body); return new function_template(*this,templp->params,fname); } Pdata templ::collect_data_member(Pname dname) { // Add a new static data member to list of template class PERM(dname); PERM(dname->tp); return new data_template(*this,templp->params,dname); } void check_formals_for_dups(Plist formals) { // template class X; for (Plist fl1 = formals; fl1; fl1 = fl1->l) { Pname n1 = fl1->f; for (Plist fl2 = fl1->l; fl2; fl2 = fl2->l) { Pname n2 = fl2->f; if (strcmp(n1->string,n2->string)==0) error("YA%n redeclared inZ list",n1); } } } bit contains_formal(Pname formal, Pclass cl) { // error('d',"contains_formal formal: %n cl %t cl->class_base: %d", formal,cl,cl->class_base); if ( cl->class_base == VANILLA ) return 0; if ( cl->is_templ_instance() ) { Pexpr actuals = Ptclass(cl)->inst->actuals; for ( ; actuals; actuals = actuals->e2 ) { Pexpr e = actuals->e1; if (e->base != NAME || e->tp->base != TYPE) continue; Pname tn = e->tp->bname(); if (strcmp(formal->string,tn->string) == 0) return 1; } } else { // Queue in formal, Queue in formal signature Ptempl t = templp->is_template(cl->string); if (!t) return 0; for ( Plist formals = t->get_formals(); formals; formals=formals->l ) { Pname n = formals->f; if ( n->n_template_arg == template_type_formal ) { return 1; } } } return 0; } void check_funct_formals(Plist formals, Pname namep) { // error('d',"check_funct_formals: %n", namep); check_formals_for_dups(formals); for (Plist fl = formals; fl; fl = fl->l) { Pname fn = fl->f; if (!fn->is_template_arg()) { error("FYZ%n must be aT",fn); continue; } for (Pname a = Pfct(namep->tp)->argtype; a; a = a->n_list ) { Ptype t = a->tp; int found = 0; while (t->base == PTR || t->base == RPTR || t->base == VEC) { switch (t->base) { case PTR: if (Pptr(t)->ptname) { // T::* Pname n = Pptr(t)->ptname; if (strcmp(n->string,fn->string)==0) found = 1; t = n->tp; break; } case RPTR: t = Pptr(t)->typ; break; case VEC: t = Pvec(t)->typ; break; } } if (found) { found = 0; break; } if (t->base == COBJ ) { // min(X if (contains_formal(fn,t->classtype())) { break; } } else if (t->base != TYPE) continue; Pname tn = t->bname(); if (!tn->is_template_arg()) { t = tn->tp; if (t->base == COBJ ) { // min(X if (contains_formal(fn,t->classtype())) { break; } } continue; } if (strcmp(tn->string,fn->string)==0) break; } if ( a == 0 ) error("FYZ%n must be used in signature of %n",fn,namep); } } #if 0 templ_compilation::get_formals_count() { int cnt = 0; for ( Plist p = params; p; p=p->l, ++cnt); return cnt; } #endif int basic_template::get_formals_count() { int cnt = 0; for ( Plist f = formals; f; f=f->l, ++cnt); return cnt; } void basic_template::check_formals(Plist f2) /* Check the formals specified for a member function * or a forward definition of a class * against the formals for the class */ { for (Plist f1 = formals; f1 && f2; f1=f1->l, f2=f2->l) if (f1->f->base != f2->f->base) switch (f1->f->n_template_arg) { case template_type_formal: error("Y formalZ mismatch, %n must be aT formalZ",f2->f); break; case template_expr_formal: error("formalZ mismatch, %n must be aZ ofT %t",f2->f,f2->f->tp); break; default: error("formalZ mismatch betweenC formal: %n andM formal: %n",f1->f,f2->f); } else if (f1->f->n_template_arg == template_expr_formal) { // the types should be identical if (f1->f->tp->check(f2->f->tp, 0) == 1) error("T mismatch betweenM formal %n andC formal %n",f2->f,f1->f); } if (f1) error("insufficient formalZs,CZ%n onwards are missing",f1->f); if (f2) error ("excess formalZs,Z%n onwards not defined forC",f2->f); } static Ptype real_type (Ptype t) { // predicate to get past all the type nodes while (t && t->base == TYPE) t = Pbase(t)->b_name->tp; return t; } static int expr_match(Pexpr a1, Pexpr a2); static void vec_eval(Ptype p) { while (p && (p->base == VEC || p->base == PTR)) { if (p->base == VEC) { Pvec v = Pvec(p); if (v->dim && !v->size) { Neval = 0; v->size = (int)v->dim->eval(); if (Neval) error("cannot evaluate constantE"); DEL(v->dim); v->dim = 0; } p = v->typ; } else { p = Pptr(p)->typ; } } } bool templ::check_actual_args(Pexpr actual) { // check actual template arguments against formals for (Plist formal=formals; formal && actual; formal=formal->l, actual=actual->e2) { // error('d',"check_actual_args: formal %n %t", formal->f, formal->f->tp); switch (formal->f->n_template_arg) { case template_type_formal: { /* A "type type" parameter, any actual type that * was accepted by the parse is acceptable here, * just make sure that it is indeed a type. * If it was parsed as a type, the grammar * should have created a name node, and * attached the type to it, having marked the * name as a template_actual_arg_dummy. */ vec_eval(real_type(actual->e1->tp)); Pname n = Pname(actual->e1); if (!((n->base == NAME) && (n->n_template_arg==template_actual_arg_dummy))) { error ("Y %sA mismatch, theY formal:%n required aT actualZ", namep->string, formal->f); // recover from error with a safe expression n = new name(""); n->tp = ::any_type; actual->e1 = n; } Ptype tp = n->tp->skiptypedefs(); if (tp->base == COBJ) { Pclass cl = tp->classtype(); if (cl->local_sig) error('s', "localC%t as ZT to YC%n",cl,namep); } break; } // case template_type_formal case template_expr_formal: // in general, checking can only be done at instantiation, // however, check 0 as actual for pointer { Ptype tp = formal->f->tp->skiptypedefs();; if (actual->e1->base==ZERO && tp->base==PTR) { // ``2'' signifies user didn't set const if (tp->b_const == 2) tp->b_const = 0; error("cannot instantiate 0 to formalZ%n(%t)",formal->f,tp); } break; } default: error ('i',"badY formal") ; } } // for loop // ??????????????? If we provide for optionals, // this is where the processing should get done. if (formal) error ("too fewAs supplied forY %s",namep->string); if (actual && actual->e1) error ("too manyAs supplied forY %s", namep->string); // error('d',"check_actual_args: formal: %d actual: %d", formal, actual); return bool(~(formal || actual)); } void templ_compilation::append_friend_ref(Ptempl_inst ref) { // Append to the list of references. // error('d',"append_friend_ref: %n",ref->get_tname()); cons *p = new cons(ref,0); if (last_friend_cons) last_friend_cons->cdr = p; else friend_templ_refs = p; last_friend_cons = p; } void templ_compilation::append_ref(Ptempl_inst ref) { // Append to the list of references. // error('d',"append_ref: %n",ref->get_tname()); cons *p = new cons(ref,0); if (last_cons) last_cons->cdr = p; else templ_refs = p; last_cons = p; // display_templ_refs(); } // pretty stupid to have both, but ... static bit ref_in_friend = 0; static bit ignore_ref = 0; Ptempl_inst templ_inst::note_ref() /* Note the reference by a definition to the template, * so that the template can be instantiated before the * definition is processed. */ { // error('d',"templ_inst::note_ref: %n", tname); if ( ref_in_friend && ignore_ref == 0) { friend_refp = true; templp->append_friend_ref(this); } if ((! refp)) { refp = true; templp->append_ref(this); } return this; } bool templ_compilation::friend_template(Pexpr actuals) /* * Determine if parameters specified to a friend template * are redundant -- refer to the current template formals * if so, do not note reference: not a case for instantiation */ { // Check whether the formal and actual types are identical Pexpr actual = actuals; for (Plist formal = params; formal && actual; formal = formal->l, actual = actual->e2) { if ((formal->f->tp == actual->e1->tp) || ((actual->e1->tp && (actual->e1->tp->base == TYPE)) && (Pbase(actual->e1->tp)->b_name->tp == formal->f->tp))) continue; else break; } if (!formal && !actual) return true; return false; } extern int dont_instantiate; Ptempl_inst templ::get_inst(Pexpr actuals, Ptempl_inst exclude) /* * Get an instantiation for the template with the given set of actuals. * If one exists, return it, otherwise create a new one. */ { // error('d',"templ::get_inst: %n", namep); static int level = 0; ++level; // record recursion depth Ptempl_inst inst = get_match(actuals, exclude, false); Ptempl_inst ti; int bl; if (inst) { if ( !inst->refp && !dont_instantiate ) { if (templp->in_progress || level>1) { inst->note_ref(); } else { bl = bl_level; bl_level = 0; ti = inst->instantiate(); bl_level = bl; --level; return ti; } } --level; return inst; } inst = new templ_inst(actuals, this); if ( dont_instantiate ) { --level; return inst; } ignore_ref = templp->friend_template(actuals) == 0; // if (ref_in_friend==0 || ignore_ref) if (ref_in_friend != 2) { if (templp->in_progress || level>1) { inst->note_ref(); } else { bl = bl_level; bl_level = 0; ti = inst->instantiate(); bl_level = bl; --level; return ti; } } --level; return inst; } Ptempl_inst templ::get_match(Pexpr actuals, Ptempl_inst exclude, bool match_instantiated_only) /* Find an instantiation that has the same set of actuals, * exclude: don't match this one passed in from the set of candidates * match_instantiated_only: only instantiated templates match */ { for (Ptempl_inst p = insts ; p ; p = p->next) { if ((p != exclude) && (p->actuals_match(actuals)) && (match_instantiated_only ? (Pclass(Pbase(p->tname->tp)->b_name->tp)->class_base == INSTANTIATED) : true)) return (p->forward ? p->forward : p); } return 0; } int template_hier; // interim solution: permit Y to match X Pfunct_inst function_template::get_match(Pexpr actuals, Pfunct_inst exclude, bool match_instantiate_only) { for (Pfunct_inst p = insts; p; p = p->next) { if ( (p != exclude) && (p->actuals_match(actuals)) && (match_instantiate_only ? Ptfct(p->tname->tp)->fct_base == INSTANTIATED : true)) return p; } return 0; } static void check_actuals( Pexpr actuals, Pname fn ) { // error('d',"check_actuals: fn: %n",fn); for (Pexpr ae=actuals; ae; ae = ae->e2) { Ptype t = ae->e1->tp->skiptypedefs(); if ( t->base == COBJ ) t = t->classtype(); if ( t->base != CLASS ) continue; if ( strncmp("__C",Pclass(t)->string,3) == 0 ) error("YF%n instantiatedW unnamedC", fn); if ( t->lex_level && t->local_sig ) error('s',"YF%n instantiatedW localC %s", fn,Pclass(t)->string); } } Pfunct_inst function_template::get_inst(Pexpr actuals, Pfunct_inst exclude) { // error('d',"%n->get_inst()",fn); Pfunct_inst inst = get_match(actuals,exclude,false); if (inst == 0) { check_actuals(actuals,fn); inst = new funct_inst(actuals,this); } return inst; } #if 0 Pbase templ::inst_basetype(Pexpr actuals) { // provides the basetype created for a given set of actuals. return (check_actual_args(actuals) ? Pbase(get_inst(actuals)->tname->tp) : basep); } #endif Pname parametrized_typename(Pname p, Pexpr actuals, bit in_friend) /* Validate that the name denotes a parametrized type, * and produce a TNAME for the instantiation. */ { // error('d',"parametrized_typename(%n (%d),%k) in_friend:%d",p,p,actuals->base,in_friend); Ptempl t = templp->is_template(p); // A template instantiation with redundant actuals // identical to the formals of the current template if (templp->current_template(p, actuals) && in_friend == 0) return p; if (t) { ref_in_friend = in_friend; Pname tname = t->typename(actuals); ref_in_friend = 0; return (tname ? tname : p); } error ("%n hasTAs but is not aZizedC", p); return p; } Pname templ::typename(Pexpr actuals) { // obtain typename associated with an instantiation return (check_actual_args(actuals) ? get_inst(actuals)->tname : 0); } // Check whether the actuals provided match the actuals for this instantiation. // The actuals match the formals, iff they are same type or parametrized type. bool templ_inst::actuals_match(Pexpr check_actuals) { Pexpr ae1, ae2 ; // the cons cells Plist formal = def->formals ; // The lists should be the same length, since check_actuals will have taken // appropriate action. for (ae1=actuals, ae2=check_actuals ; formal && ae1 && ae2 ; ae1=ae1->e2, ae2=ae2->e2, formal = formal->l) switch (formal->f->n_template_arg) { case template_type_formal: { Ptype t1 = ae1->e1->tp, t2 = ae2->e1->tp ; if (t1->check(t2,255) == 1) return false ; break ; } case template_expr_formal: if (! expr_match(ae1->e1, ae2->e1)) return false ; break ; default: error ('i', "bad template formal") ; } return true ; } Ptype non_template_arg_type(Pbase); bool funct_inst::actuals_match(Pexpr check_actuals) /* Try to match the actual arguments in a function call expression with the formal arguments of the template function pointed to by `this'. */ { // error('d',"%n->actuals_match()",tname); Pname n = Ptfct(tname->tp)->argtype; // formal arguments Pexpr e = check_actuals; // actual arguments for ( ; n && e; n=n->n_list, e=e->e2) { Ptype t1 = n->tp; Ptype t2 = e->e1->tp; // error('d',"actuals_match: t1: %t t2: %t", t1,t2); while ( t2->base == TYPE ) { Pname nnn = t2->bname(); // instantiation inside instantiated function if (nnn->is_template_arg()) t2 = t2->bname_type(); else t2 = nnn->tp; } // first try to match with trivial conversions only int t_h = template_hier; template_hier = 0; // turn off derived-->base conversions if (exact1(n, t2)) { template_hier = t_h; continue; } template_hier = t_h; // for class C, allow for C -> C& if (t2->base==COBJ && t1->base==RPTR) { Ptype t = Pptr(t1)->typ->skiptypedefs(); if (t->base==COBJ) t2 = new ptr(RPTR,t2); // for T[n], convert T[n] -> T* } else if (t2->base==VEC) { t2 = new ptr(PTR,Pvec(t2)->typ); if (t1->base==RPTR) t2 = new ptr(RPTR,t2); } // `template_hier' was set to 1 in `has_templ_instance()' // to allow for Derived --> Base conversions if (t1->check(t2,PT_OVERLOAD)) { // provided that const-ness is not violated, we are // matching with an allowed Derived --> Base conversion if (!const_problem || Pptr(t2)->typ->b_const) return false; else continue; } } return true; } extern char emode; /* stradd: set of overloaded fuctions used to * accumulate name strings */ void stradd(char *&target, char *source, int numeric = 0) { // error('d',"stradd: target: %s\t source: %s\tnumeric: %d",target,source,numeric); while (*target = *source) { if ( *target == '-' && numeric ) *target = 'n'; target++; source++; } } void stradd(char *&p, long i) { char s[64]; char t[64]; if (!emode) { *p++ = 'L'; } sprintf(s,"%ld", i); if (!emode) { int len = strlen(s); if (len >= 10) sprintf(t,"%ld_%s", len,s); else sprintf(t,"%ld%s", len,s); stradd(p,t,1); } else stradd(p,s,0); } void stradd(char *&p, Pname n) { if (!emode){ char s[1024]; sprintf(s,"%d", strlen(n->string)); stradd(p,s); } stradd(p, n->string); } char* mangled_expr(char *p, Pexpr e, bool mangle_for_address = false) /* produce a unique string suitable for use within a name; * if in emode, i.e., printing in the context of an error, * print a pretty name instead. */ { static int mangle_address = 0; if (e == 0) return p; // error('d',"mangled_expr: p %s e: %k", p, e?e->base:0); switch (e->base) { case ADDROF: case G_ADDROF: // relies on unary using e2 ++mangle_address; if (emode) { stradd(p, "&"); } p = mangled_expr(p,e->e2); --mangle_address; break; case NAME: if (mangle_address || mangle_for_address) { Pname n = Pname(e); if (n->n_scope == PUBLIC || n->n_scope == 0) { // static data or function members of some class Ptype t = n->tp; if ((n->n_stclass == STATIC && n->n_table->t_name) || (t->base == FCT && Pfct(t)->f_static)) { // &X::i ==> 5i__1X char xx[1024]; char s[1024]; char *st = t->base!=FCT ? n->n_table->t_name->string : Pfct(t)->memof->string; if (emode) { sprintf(s,"%s::%s",st,n->string); } else { sprintf(xx,"%s__%d%s",n->string,strlen(st),st); sprintf(s,"%d",strlen(xx)); strcat(s,xx); } //error('d',"mangled_expr: %s", s ); stradd(p,s); } else stradd(p,Pname(e)); } else stradd(p,Pname(e)); } else if (Pname(e)->n_evaluated) stradd(p,Pname(e)->n_val); else if (Pname(e)->n_initializer) { // see ``suitable_const_expr'' Pexpr ee = Pname(e)->n_initializer; if (ee->base == NAME && Pname(ee)->n_evaluated) // const p = mangled_expr(p,ee); else p = mangled_expr(p,ee,true); } else if (Pname(e)->tp->base == VEC || Pname(e)->tp->base == FCT) stradd(p,Pname(e)); else error('i',"mangled_expr: couldn't mangle actualYE %n",e); break ; case IVAL: stradd(p, ((ival *)e)->i1); break; case CAST: case G_CAST: { // an IVAL || ICON hiding below the cast? // see ``suitable_const_expr'' if (e->e1->base == IVAL) { stradd(p, ((ival *)e->e1)->i1); break; } else if (e->e1->base == ZERO) { *p++ = '0'; break; } else if (e->e1->base != ICON && e->e1->base != CCON) { if (emode) { Neval=0; long e_eval = e->eval(); if (!Neval) { stradd(p,e_eval); break; } } error ('i', "mangled_expr: unexpected cast in YE"); break; } else e = e->e1; // no break! -- compute ICON } // case FCON: /* already filtered out */ case ICON: case CCON: { if (!emode) { *p++ = 'L'; } int len = strlen(e->string); char s[64]; if (!emode) { if (len >= 10) sprintf(s,"%ld_%s", len,e->string); else sprintf(s,"%ld%s", len,e->string); } else { sprintf(s,"%s",e->string); } strcpy(p,s); // ??? must there be a better encoding scheme ??? if (!emode) while (*p) if (! (isalnum(*p))) switch(*p) { case '+': *p++ = 'p'; break; case '-': *p++ = 'n'; break; case '.': *p++ = 'D'; break; case 'e': *p++ = 'E'; break; default: error ('i', "bad character in constant"); break; } else p++; break ; } case ZERO: *p++ = '0'; break; case ILIST: // pointer to member constant { // literal encoding of pointer to member :: LM if (emode) { stradd(p, "&class::member"); break; } Pexpr i1 = e->e1->e1; // delta Pexpr i2 = e->e1->e2; // index Pexpr i3 = (e->e2->base==CAST|| e->e2->base==G_CAST)?e->e2->e1:e->e2; // function address or 0 *p++ = 'L'; *p++ = 'M'; p = mangled_expr(p,i1); *p++ = '_'; p = mangled_expr(p,i2); *p++ = '_'; if (i3->base != NAME) // p = mangled_expr(p,i3); // virtual function else p = mangled_expr(p,i3,true); // address of name } break; case DOT: case REF: if (mangle_address) { while (e->base == DOT || e->base == REF) e = Pexpr(e->mem); p = mangled_expr(p,e->e2); break; } // no break default: if (emode) { Neval=0; long e_eval = e->eval(); if (!Neval) { stradd(p,e_eval); break; } } error ('i', "can't mangle %k", e->base); } // error('d',"mangled_expr: %s", p); return p; } #if 0 char* mangled_expr(char *p, Pname n) { // this function is invoked once at the top level if (n->n_evaluated) { stradd(p, n->n_val); // error('d',"mangled_expr using stradd: %s", p); return p; } return mangled_expr(p, n->n_initializer, (real_type(n->tp)->base == PTR ? true : false)); } #endif void templ_inst::print_pretty_name() { /* Generate a template class instantiation name suitable for printing * when it is presented to the user. * this depends on using a %t and passing it a classdef type -- * passing it the %n, Pname pair will have it print out with encoding */ extern char emode; /* 3.1/4.0: until next release when defs are merged */ Pname n = Ptempl_inst(this)->def->namep; error('c', " %s<", (n ? n->string : "?")); Plist formal = inst_formals; emode++; for ( Pexpr ae1=actuals; formal && ae1; ae1=ae1->e2, formal = formal->l ) { if (ae1->e1->base == 0) break; // we are in an error mode in any case switch (formal->f->n_template_arg) { case template_type_formal: ae1->e1->tp->dcl_print(0); break; case template_expr_formal: { char buff[ 1024 ]; memset(buff,0,1024); mangled_expr(buff, ae1->e1); error('c',"%s", buff); break; } default: error('i',"unexpected formalZ"); } // this comma is unfortunately misplaced, // since it follows a space printed out by dcl_print if (formal->l) error('c',", "); } error('c', ">"); emode --; } void funct_inst::print_pretty_name() /* Generate a template function instantiation name suitable for printing when it is presented to the user. */ { extern char emode; emode++; Pname n = Pfunct_inst(this)->def->fn; error('c', " %s(", n ? n->string : "?"); n = Ptfct(tname->tp)->argtype; // formal arguments for ( ; n; n=n->n_list) { Ptype t = n->tp; if (!t || t->base == 0) continue; // in error mode in any case t->dcl_print(0); if (n->n_list) error('c', ", "); } error('c', ")"); emode --; } static int check_expr(TOK b) { switch (b) { default: return 0; case ICON: case IVAL: case ZERO: case CCON: case CAST: case G_CAST: case EOBJ: case SIZEOF: case COMPL: case UMINUS: case UPLUS: case NOT: case PLUS: case MINUS: case MUL: case LS: case RS: case NE: case LT: case GT: case GE: case LE: case AND: case OR: case ER: case DIV: case MOD: case EQ: case ANDAND: case OROR: case CM: case G_CM: case QUEST: return 1; } } int check_for_const(Pexpr a1, Pexpr a2) { // there are different representations for ICON based upon // whether it has been evaluated. // error('d',"check_for_const a1: %k a2: %k",a1->base,a2->base); // error('d',"check_for_const a1(tp): %t a2(tp): %t",a1->tp,a2->tp); Neval = 0 ; if (a1->base != NAME && a2->base != NAME) { if (a1->tp && a2->tp && a1->tp->check(a2->tp, 255)) return false; while (a1->base == CAST || a1->base == G_CAST) { TOK b1 = a1->tp2->skiptypedefs()->base; if (b1 == CHAR || b1 == SHORT || b1 == INT || b1 == LONG || b1 == ENUM || b1 == EOBJ) break; a1 = a1->e1; } while (a2->base == CAST || a2->base == G_CAST) { TOK b2 = a2->tp2->skiptypedefs()->base; if (b2 == CHAR || b2 == SHORT || b2 == INT || b2 == LONG || b2 == ENUM || b2 == EOBJ) break; a2 = a2->e1; } long a1_eval = a1->eval(); long a2_eval = a2->eval(); if (Neval) return false; return a1_eval==a2_eval; } if (a1->base == NAME && check_expr(a2->base)) { Pname n = Pname(a1); long a2_eval = a2->eval(); return (!Neval && n->n_evaluated && (n->n_val == a2_eval)); } else if (a2->base == NAME && check_expr(a1->base)) { Pname n = Pname(a2); long a1_eval = a1->eval(); return (!Neval && n->n_evaluated && (n->n_val == a1_eval)); } if (a1->base == NAME && (a2->base == ADDROF || a2->base == G_ADDROF) && a1 == a2->e2) return true; if (a2->base == NAME && (a1->base == ADDROF || a1->base == G_ADDROF) && a2 == a1->e2) return true; if (a1->base == NAME && a2->base == ILIST && a1->tp && a2->tp) return !a1->tp->check(a2->tp, 255) ? true : false; if (a2->base == NAME && a1->base == ILIST && a1->tp && a2->tp) return !a2->tp->check(a1->tp, 255) ? true : false; return false; } // get past the template parameter names to get the the real expression static Pexpr real_expression(Pexpr e) { while (e && (e->base == NAME) && (Pname(e)->n_template_arg == template_expr_formal) && (! Pname(e)->n_evaluated) && Pname(e)->n_initializer) e = Pname(e)->n_initializer ; return e ; } // determine whether two expressions supplied as actual arguments to // a "template_expr_formal" formal argument match. static int expr_match(Pexpr a1, Pexpr a2) { static int addr_of = 0 ; a1 = (addr_of ? a1 : real_expression(a1)) ; a2 = (addr_of ? a2 : real_expression(a2)) ; if (a1 == a2) return true; // error('d',"expr_match: a1 %k a2 %k",a1->base,a2->base); // if (a1->base != a2->base) return check_for_const(a1, a2) ; Neval = 0; switch (a1->base) { case QUEST: // a ternary operator return ( expr_match(a1->cond, a2->cond) && expr_match(a1->e1, a2->e1) && expr_match(a1->e2, a2->e2)) ; case PLUS: case MINUS: case MUL: case DIV: case MOD: case ER: case OR: case ANDAND: case OROR: case LS: case RS: case EQ: case NE: case LT: case LE: case GT: case GE: // binary operator return (expr_match(a1->e1, a2->e1) && expr_match(a1->e2, a2->e2)) ; case UMINUS: case UPLUS: case NOT: case COMPL: return (expr_match(a1->e2, a2->e2)) ; case ADDROF: case G_ADDROF: { // unary using e2 addr_of ++ ; int result = (expr_match(a1->e2, a2->e2)) ; addr_of -- ; return result ; } case CAST: case G_CAST: { TOK b1 = a1->e1->base; TOK b2 = a2->e1->base; if ((b1 == ICON || b1 == IVAL || b1 == CCON || b1 == ZERO) && (b2 == ICON || b2 == IVAL || b2 == CCON || b2 == ZERO)) return check_for_const(a1, a2); else return (expr_match(a1->e1, a2->e1)) ; } case NAME: { Pname n1 = Pname(a1), n2 = Pname(a2) ; if (! addr_of) { if ((n1->n_evaluated) && n2->n_evaluated && (n1->n_val == n2->n_val)) return true ; if (n1->n_initializer && (! n2->n_initializer)) return check_for_const(n2, n1->n_initializer) ; if (n2->n_initializer && (! n1->n_initializer)) return check_for_const(n1, n2->n_initializer) ; if (n1->n_initializer && n2->n_initializer) return expr_match(n1->n_initializer, n2->n_initializer) ; } if (templp->formals_in_progress) return strcmp(n1->string,n2->string)==0; return false; } case DOT: return (expr_match(a1->e1, a2->e1)) ; case REF: return (expr_match(a1->e1, a2->e1)) ; case IVAL: return (ival *)a1->i1 == (ival *)a2->i1 ; case ICON: case CCON: { int i; i = a1->eval() == a2->eval(); if (Neval) error("cannot evaluate constantE"); return i; } case FCON: case STRING: return (strcmp (a1->string, a2->string) == 0) ; case ZERO: return true ; case SIZEOF: { long l1 = a1->eval(), l2 = a2->eval() ; if (Neval) return false; return (l1 == l2) ; } case ILIST: if (a1->tp && a2->tp && !a1->tp->check(a2->tp, 255)) return true; else return false; } return false ; } static char* non_type_argument_signature(Pexpr e, char *p) { // template ... p = e->tp->signature(p); return mangled_expr(p,e); } /* check for excessive template class nesting levels */ static int check_nest(char* s, int max) { char* t; char* t2; int len; int n; int maxn; t = s; maxn = 0; while (*t && t < s + max) { if (t[0] == '_' && t[1] == '_' && t[2] == 'p' && t[3] == 't' && t[4] == '_' && t[5] == '_') { t2 = t + 6; len = 0; while (*t2 >= '0' && *t2 <= '9') { len = len * 10 + *t2 - '0'; t2++; } n = 1 + check_nest(t2, len); if (n > maxn) maxn = n; } t++; } return maxn; } // Generate a mangled template instantiation name. The syntax of as template // mangled class name is of the form: // original_name____unique_id // Each non-type argument is replaced by a unique id. char* templ_inst::mangled_name(char *ip) { // error('d',"templ_inst::mangled_name(%s)", ip); char *start = ip ; ip = start ; strcpy(ip, (def->namep ? def->namep->string : "?")) ; ip= start + strlen(start) ; stradd(ip, "__pt__") ; { char a [max_string_size], *p = a ; Plist formal = inst_formals ; for (Pexpr ae1=actuals ; ae1 ; ae1=ae1->e2, formal = formal->l) switch (formal->f->n_template_arg) { case template_expr_formal: *p++ = 'X' ; // the formal must have been bound p = non_type_argument_signature(formal->f, p) ; break ; case template_type_formal: p = ae1->e1->tp->signature(p,1) ; break ; default: error ("bad template formal:%d", formal->f->base) ; break ; } *p = 0 ; sprintf(ip, "%d_", strlen(a)+1) ; ip = start + strlen(start) ; strcpy(ip,a) ; } // error('d',"templ_inst::mangled_name(%s)", start); if (check_nest(start, 9999) > MAX_INST_DEPTH) { if (!error_count) error("infinite template instantiation sequence"); else error('i', "infinite template instantiation sequence"); } return start ; } char* funct_inst::mangled_name(char *ip) { /* generate mangled template instantiation name of the form * __pt__F_[_]... */ char *start = ip; ip = start; strcpy(ip, (def->fn ? def->fn->string : "?")); ip = start + strlen(start); stradd(ip, "__pt__"); char buf[max_string_size], *p = buf; Ptype t; if (Ptfct(tname->tp) && Ptfct(tname->tp)->argtype) { Pname n = Ptfct(tname->tp)->argtype; for ( ; n; n=n->n_list) { t = n->tp; if (t->base == ZTYPE) t = int_type; p = t->signature(p); *p++ = '_'; } } else { for (Pexpr ae = actuals; ae; ae=ae->e2) { t = ae->e1->tp; if (t->base == ZTYPE) t = int_type; p = t->signature(p); *p++ = '_'; } } if (actuals) --p; *p = 0; sprintf(ip, "F%d_", strlen(buf)+1); ip = start + strlen(start); strcpy(ip,buf); // error('d',"funct_inst::mangled_name: %n (%s)",def->fn,start); return start; } const char leader[]= "\t"; void basic_inst::print_error_loc(int newline) { /* Explain the location of an instantiation in greater detail, * since it may be far removed from it's textual definition. */ if (! basic_inst::head) return; // No active instantiations extern void print_loc(); state current_state; char buffer[max_string_size]; for (int i = 0; inext_active) { // A more compact message for a single level of instantiation context.restore(); print_loc(); error('c', "%sis the site of the instantiation\n", leader); } else { // The instantiation chain is longer than one print_loc(); error('c',"%sthe instantiation path was:\n", leader); for (Pbase_inst p = basic_inst::head ; p ; p = p->next_active) { p->context.restore(); print_loc(); error('c',"%s template:", leader); p->print_pretty_name(); putch('\n'); } } current_state.restore(); } char* basic_inst::instantiation_string() { /* generates name for class or function template */ char inst_name[max_string_size]; for (int i=0; imodify_inst_names(%s)",this,s); char *old = string; string = s; // Change the class name if ( lex_level && (in_class==0 || lex_level!=in_class->lex_level) ) { error('i',"localCY"); //Pktab tb = k_tbl->k_next; //while ( tb && tb->k_id!=BLOCK ) tb = tb->k_next; //if ( tb == 0 ) error('i',"missing block scope for localCY%t",this); //local_sig = make_local_name(this,tb->k_name); } else c_strlen = ::strlen(s); // Change the constructor names for (Pname p=mem_list; p; p=p->n_list) if (p->tp && (p->tp->base==FCT) && (!strcmp(old, p->string))) p->string = s; } Ptype non_template_arg_type(Pbase t) { // Get past the fake template argument name typename types // error('d',"non_template_arg_type: %t", t ); if ((t->base == TYPE) && (t->b_name->n_template_arg == template_type_formal)) return non_template_arg_type(Pbase(t->b_name->tp)); else return t; } // follow the chain until we hit a non void non_template_arg_non_type(Pname n) { Pexpr i = n->n_initializer; while (i && (i->base == NAME) && (Pname(i)->n_template_arg == template_expr_formal)) { if (Pname(i)->n_initializer) { n->n_initializer = Pname(i)->n_initializer ; i = n->n_initializer ; continue ; } if (Pname(i)->n_evaluated) { n->n_evaluated = 1 ; n->n_val = Pname(i)->n_val ; return ; } } return ; } // Now that the actuals are truly resolved, ie. semantics is complete, and the // template is about to be instantiated. void forward_template_arg_types(Plist formal, Pexpr actuals) { for (Pexpr actual = actuals ; formal && actual ; formal = formal->l, actual = actual->e2) switch(formal->f->n_template_arg){ case template_type_formal: actual->e1->tp = non_template_arg_type(Pbase(actual->e1->tp)) ; break ; case template_expr_formal: break ; default: error ('i', "bad template formal") ; } } static int suitable_const_expr(Pname n) /* * determine whether the supplied expression is suitable * All expressions must be of the form constant integer expression, * or the address of a variable, or an array, or function */ { // error('d',"suitable_const_expr %n",n); if (n->n_evaluated) return 1; Pexpr ee = n->n_initializer; if (ee == 0) return 0; // error('d',"init: %k e1 %k e2 %k",ee->base,ee->e1?ee->e1->base:0,ee->e2?ee->e2->base:0); switch (ee->base) { case CAST: case G_CAST: { Pexpr e = ee->e1; // if it is a cast of an integer value, it's fine. if (e->base == IVAL || e->base == ICON || e->base == CCON || e->base == ZERO) return 1; return -2; // to permit explicit sorry } // case FCON: /* should be caught in bind_formals() */ case ZERO: return 1; case ADDROF: case G_ADDROF: { bit mbr = 0; Pexpr e = ee->e2; while (e->base == DOT || e->base == REF) { // &x.i, &px->i if (e->e1->base == CALL || e->e1->base == G_CALL) return 0; // a sorry requested by directed instantiation if ( e->mem->base != DOT && e->mem->base != REF ) error('s',"address of boundM (&%n%k%n) as actualYA",e->e1,e->base,e->mem); e = Pexpr(e->mem); ++mbr; } Pname an = Pname(e); // error('d',"an: %k %n %t",an->base,an->base==NAME?an:0,an->tp); if (an->base != NAME) return 0; if (an->n_sto == STATIC) return -3; // explicit static if (an->n_stclass == STATIC || mbr) return 1; if (an->tp->base == FCT && Pfct(an->tp)->f_static && Pfct(an->tp)->memof ) return 1; // T (x::*)() return 0; } case NAME: { Pname an = Pname(n->n_initializer); if (an->n_stclass == STATIC && (an->tp->base == VEC || (an->tp->b_const && an->n_evaluated))) // const return 1; return 0; } case ILIST: // constant pointer to member: int (X::*)() if (ee->e2) return 1; return 0; case STRING: return -1; // simplest way to add the explicit sorry default: return 0; } } static int const_formal_hack(Pname n) { /* templ_inst::bind_formals sets b_const in expr parameters. * this break type-checking for binding of formals to insts. * simply not setting it, though, is no answer since the * instantiated formal should be handled as a const * hence, this ``elegant'' hack ... */ // error('d',"const_formal_hack: %n %k %t const_problem: %d",n,n->tp->base,n->tp,const_problem); if (!const_problem) return 0; switch (n->tp->base) { // case W_CHAR: // case W_STRING: case RPTR: case VEC: case FLOAT: case DOUBLE: case LDOUBLE: return 0; // should not occur case ZTYPE: case CHAR: case SHORT: case INT: case LONG: case FIELD: case EOBJ: case COBJ: case TYPE: case ANY: { // a basetype node Pbase b = Pbase(n->tp); if (b->b_const == 2) return 1; return 0; } case PTR: { Pptr b = Pptr(n->tp); if (b->b_const == 2) return 1; return 0; } } return 0; } char * make_formal_name(char *fns, char *ins) {// create formal parameter name: ``fns__ins'' char s[1024]; char t[6]; strcpy(s,fns); // formal name string sprintf(t,"__%d",strlen(ins)); // instantiation name string strcat(s,t); strcat(s,ins); // error('d',"make_formal_name: %s",s); char *result = new char[ strlen(s)+1 ]; strcpy(result,s); return result; } void templ_inst::bind_formals() /* Bind the formals to the types passed in as the actuals, for the * instantiations, bind the non-type names to their expressions. */ { Pexpr actual; Plist formal; for (formal = inst_formals, actual = actuals; formal && actual; formal = formal->l, actual = actual->e2) { switch (formal->f->n_template_arg) { case template_type_formal: { Ptype t = non_template_arg_type(Pbase(actual->e1->tp)); if (t->base == VEC) { // handle X Pvec v = Pvec(t); if(v->dim == 0 && v->size == 0) error("actual vectorZ%t must include dimension",actual->e1->tp); } formal->f->tp = t; PERM(formal->f->tp); break; } case template_expr_formal: { actual->e1 = actual->e1->typ(gtbl); // insure that T* == T[] Ptype t = actual->e1->tp; if (t->base == VEC && formal->f->tp->base == PTR) t=new ptr(PTR,Pvec(t)->typ); if (formal->f->tp->check(t,0)) { // const shouldn't be a problem for objects if (const_problem && t->is_ptr_or_ref()==0 && formal->f->tp->is_ptr_or_ref()==0) ; else if (!const_formal_hack(formal->f)) { error("YA mismatch:X %t for formal %n, not %t", formal->f->tp, formal->f, actual->e1->tp); error('i',"cannot recover from previous errors"); } } // hide the global name around decl processing of the formal name Pname g = gtbl->look(formal->f->string, 0); if (g) g->n_key = HIDDEN; // bind the non type arguments to their expressions // parameters that are bound at syntax analysis, these parameters are // bound during dcl processing, so ensure that they can be found. formal->f->n_initializer = actual->e1; formal->f->simpl(); formal->f = formal->f->dcl(gtbl, STATIC); formal->f->n_key = HIDDEN; formal->f->n_sto = STATIC; PERM(formal->f); non_template_arg_non_type(formal->f); int sorry = suitable_const_expr(formal->f); // yes, should have constants defined for case labels switch ( sorry ) { case 0: error("YA for formal: %sis not a suitable constant.", formal->f->string); break; case -1: // string literal error('s',"actualZE ofT string literal"); error('i',"cannot recover from previous errors"); break; case -2: // cast of non-integer constant error('s',"cast of non-integer constant"); break; case -3: // address of explicit static name error("YA for formal %s: address of static identifier",formal->f->string); break; } if (g) g->n_key = 0; break; } default: error ('i', "badY formal"); } } // now that the formals are bound, compute the instantiation string tname->string = instantiation_string(); for (formal=inst_formals; formal; formal=formal->l) if (formal->f->n_template_arg_string) error('i', "attempt to bind aYZ multiple times"); else formal->f->n_template_arg_string = make_formal_name(formal->f->string,tname->string); } void funct_inst::bind_formals() { /* bind formal parameters to the actual type arguments * generate the mangled name for this instantiation */ // error('d',"funct_inst::bind_formals: %n status: %d", tname,status ); int count = def->get_formals_count(); for (Plist formal=inst_formals; formal; formal=formal->l) { /* 3.1/4.0: redo is-ftempl_match to set binding in order of formals */ /* this will make `bind_formal' faster ... */ // error('d',"\tfunct_inst::bind_formals: formal: %n", formal->f); Pbinding p = binding; for (int i=0; iparam ); if (strcmp(formal->f->string,p->param->string)==0) { // error('d',"\tfunct_inst::bind_formals: match !! tp: %t\n", p->typ); Ptype t = p->typ; if (t->base == ZTYPE) t = int_type; if (t->b_const != 0) { if (t->base==PTR || t->base==RPTR) { Pptr pt = new ptr(t->base,0); *pt = *(Pptr(t)); pt->b_const = 0; t = pt; } else { Pbase bt = new basetype(t->base,0); *bt = *(Pbase(t)); bt->b_const = 0; t = bt; } } formal->f->tp = t; PERM(formal->f->tp); } } } // now compute the instantiation string tname->string = instantiation_string(); // error('d',"bind_formals: instantiation_string: %s", inst_name); for (formal=inst_formals; formal; formal=formal->l) { if (formal->f->n_template_arg_string) error('i',"attempt to bind aYP multiple times"); else formal->f->n_template_arg_string = make_formal_name(formal->f->string,tname->string); } } void templ_inst::explicit_inst() { // copy the formals :: need to instantiate string name_list dummy_formal(0,0); Plist last = &dummy_formal; for (Plist formal=def->formals; formal; formal=formal->l) { Pname copy_name = new name(""); *copy_name = *formal->f; copy_name->n_tbl_list = 0; last = last->l = new name_list(copy_name,0); } inst_formals = dummy_formal.l; bind_formals(); Pclass cl = tname->tp->classtype(); cl->class_base = INSTANTIATED; cl->templ_base = CL_TEMPLATE; // indicates specialized Pbase(tname->tp)->b_name->string = tname->string; // namep = insert_type(tname,Ctbl,TYPE);//SYM Pktab tb = cl->k_tbl->k_next; if (tb == 0) tb = Gtbl; else if ( tb->k_id == TEMPLATE ) tb = tb->k_next; namep = insert_type(tname,tb,cl->csu);//SYM namep->tp = tname->tp; cl->modify_inst_names(tname->string); cl->k_tbl->k_name = tname; // don't understand why this is necessary ... if (cl->k_tbl->k_name->n_ktable == 0) cl->k_tbl->k_name->n_ktable = namep->n_ktable; } // Expose the non-type parameter names so that they are visible during decl // processing. Conflicting global names are hidden, so that they are not // found. void basic_inst::expose_parameter_names() { if (hidden_globals) error ('i', "an expose without a hide of global names") ; for (Plist formal = inst_formals ; formal ; formal = formal->l) if (formal->f->n_template_arg == template_expr_formal) { // Hide any visible globals Pname gname = gtbl->look(formal->f->string, 0) ; if (gname) { // an existing global name, hide it gname->n_key = HIDDEN ; // note them for future restoration hidden_globals = new name_list(gname,hidden_globals); } formal->f->n_key = 0 ; // bring it out of hiding if (formal->f != gtbl->look(formal->f->string,0)) error('i', "parameter could not be located in the global table") ; } } // Hide the non-type parameter names after an instantiation, and restore any // globals that may have been hidden during the process. void basic_inst::hide_parameter_names() { // error('d',"hide_parameter_names()"); for (Plist formal = inst_formals ; formal ; formal = formal->l) if (formal->f->n_template_arg == template_expr_formal) { formal->f->n_key = HIDDEN ; } for (; hidden_globals; hidden_globals= hidden_globals->l) hidden_globals->f->n_key= 0 ; hidden_globals = 0 ; } // Primitives for saving and restoring the compilation state around a template // instantiation. It also maintains the stack of template instantiations. void basic_inst::save_state(Pname p) { if (next_active) error ('i', "circular instantiation of a template") ; context.save() ; if (basic_inst::head) basic_inst::head->hide_parameter_names() ; next_active = basic_inst::head; basic_inst::head = this; context.init() ; Cdcl = p; Cstmt = NULL; curr_file = (Cdcl) ? Cdcl->where.file : 0; expose_parameter_names() ; } void basic_inst::restore_state() { context.restore() ; hide_parameter_names() ; basic_inst::head = next_active ; next_active = NULL ; if (basic_inst::head) basic_inst::head->expose_parameter_names() ; } // Copy over the class definition subtree starting from COBJ down to the // CLASSDEF node. This minimal subtree has to exist during syntax analysis, // and already contains pointers into it. void templ_inst::kludge_copy(Pbase pbc) { // copy just the COBJ ->b_name NAME ->tp CLASS path for now, note that the // preceding path of the tree is pre-allocated, since syntax analysis needs // to generate pointers to these objects. Pbase pb = Pbase(tname->tp) ; Pname save_b_name = pb->b_name ; Ptype save_tp = pb->b_name->tp ; if ((pb->base != COBJ) || (pbc->base != COBJ)) error('i',"templ_inst::kludge_copy:(pb %k,pbc %k) cobjX",pb->base,pbc->base); *pb = *pbc ; pb->b_name = save_b_name ; *pb->b_name = *pbc->b_name ; pb->b_name->tp = save_tp ; *Pclass(pb->b_name->tp) = *Pclass(pbc->b_name->tp) ; Pclass(pb->b_name->tp)->class_base = INSTANTIATED ; // ::error('d',"kludge_copy: %n", pb->b_name); } // these statics probably belong in templ_inst and shouldn't be dangling around static Pbase cobj_node ; static Pname cname_node ; static Pclass class_node ; static Pfct fct_node; static void syntax_tree_copy_hook(void *, Pnode &, node_class, tree_node_action &action, int& never_see_again) { never_see_again = 1; action = tna_continue; return; } // create a copy of the expression tree static Pnode copy_syntax_tree(Pnode n, int no_types = 0) { pointer_hash cht(default_copy_hash_size) ; tree_copy_info info ; if(no_types) info.node_hook = syntax_tree_copy_hook; copy_tree (n, info, &cht); return n ; } bool templ_inst::copy_hook(Pnode &node) { // hook to perform the copying of the pre-allocated class subtree switch (node->base) { case COBJ: if (node == cobj_node) return false; if (node == def->namep->tp) { *cobj_node = *Pbase(node); node = cobj_node; } break; case NAME: if (node == cname_node) return false; if (node == sta_name) return false; if (node == Pbase(def->namep->tp)->b_name) { *cname_node= *Pname(node); node = cname_node; } break; case CLASS: if (node == class_node) return false; if (node == Pbase(def->namep->tp)->b_name->tp) { if (class_node==0) return false; *class_node = *Pclass(node); node = class_node; } break; } return true; } /* This hook function used during a class copy. */ static void copy_hook(void /* Ptempl_inst */ *p, Pnode &node, node_class, tree_node_action &action, int& never_see_again) { action = (Ptempl_inst(p)->copy_hook(node) ? tna_continue : tna_stop ) ; never_see_again = (action != tna_stop); return; } static void f_copy_hook(void *p, Pnode &node, node_class, tree_node_action &action, int& never_see_again) { // 3.1/4.0: should be merged with global copy_hook // error('d',"f_copy_hook"); action = (Pfunct_inst(p)->f_copy_hook(node) ? tna_continue : tna_stop); never_see_again = (action != tna_stop); return; } // hook to perform the copying of the pre-allocated class subtree bool funct_inst::f_copy_hook(Pnode &node) { // error('d',"funct_inst::f_copy_hook: %k %d", node->base , node); switch (node->base) { case NAME: if(node == sta_name) return false; break; case FCT: if (node == fct_node) return false; if (node == def->fn->tp) { *fct_node = *Pfct(node); node = fct_node; } break; } return true; } void establish_class_subtree_correspondence(pointer_hash &h, Pname key_tname, Pname value_tname) { h[int(key_tname)] = int(value_tname) ; h[int(key_tname->tp)] = int(value_tname->tp) ; h[int(Pbase(key_tname->tp)->b_name)] = int(Pbase(value_tname->tp)->b_name) ; h[int(Pbase(key_tname->tp)->b_name->tp)] = int(Pbase(value_tname->tp)->b_name->tp) ; } Pcons make_ref_copy(pointer_hash &h, tree_copy_info &info, Pcons old_templ_refs) { cons dummy(0,0), *last = &dummy ; for (Pcons pc = old_templ_refs ; pc ; pc = pc->cdr) { Ptempl_inst t = Ptempl_inst(pc->car) ; Pexpr dummy = new expr(ELIST, 0, 0); elist list(dummy); // error('d',"make_ref_copy: %n", t->tname); // copy the trees corresponding to the actuals for (Pexpr actual = t->actuals ; actual ; actual = actual->e2) { Pnode root = actual->e1 ; copy_tree (root, info, &h); // make sure that references to enclosing formals are resolved root = Pexpr(root)->typ(gtbl); list.add(new expr(ELIST, Pexpr(root), 0)) ; } Pexpr new_actuals = list.head->e2 ; // get one if it exists, create one otherwise. Ptempl_inst treal = t->def->get_inst(new_actuals, t) ; Pname new_tname = treal->tname ; last = last->cdr = new cons(treal,0); establish_class_subtree_correspondence(h, t->tname, new_tname) ; } return dummy.cdr ; } /* Remap the template references from within the body of the template. This action is similar to the normal tree copy operation; it would normally have been done during the syntax phase, that produced the tree, but since there isn't one, for the instantiated body, it must be done here. */ Pcons basic_inst::ref_copy(pointer_hash &h, tree_copy_info &info, Pcons old_templ_refs) { expose_parameter_names() ; Pcons new_refs = make_ref_copy(h,info,old_templ_refs) ; hide_parameter_names() ; return new_refs ; } #if 0 static bool is_forward_instantiation(Pbase b_base, Pbase f_base) { return bool(b_base->b_name->tp->defined && f_base->b_name->tp->defined) ; } #endif /***************************************************************************** * * * If the template instantiation is found to be unique after the decl * * processing of the actuals, create a copy of the post syntax graph for the * * class. The edges of the graph are determined by "type nodes" that have * * already been defined, and TNAME nodes that are in the global keyword * * table. Special care is also taken to avoid copying nodes whose identity * * must be maintained, since cfront uses them for fast type checks, these * * nodes always have the "defined" flag turned on and so are never copied. * * * * Copying of the pre-allocated class sub-tree for the template: COBJ * * ->b_name NAME ->tp CLASS * * * * is handled by the class_copy hook above, that is invoked during the course * * of the copy. * * * * Template references from within the class need special handling, since * * each instantiation of the class, results in a potentially new template * * instantiation. * * * *****************************************************************************/ Ptempl_inst templ_inst::class_copy(Pcons &templ_refs, bool recopy) { // associate the formals with their types, and their expressions if (recopy) { // remove the class def node from the table, so that it's attributes are // copied. corr->del(int(Pbase(def->namep->tp)->b_name->tp)) ; corr->del(int(Pbase(def->namep->tp)->b_name)) ; corr->del(int(def->namep->tp)) ; corr->del(int(tname->tp)) ; corr->del(int(Pbase(tname->tp)->b_name)) ; corr->del(int(Pbase(tname->tp)->b_name->tp)) ; }else corr = new pointer_hash(default_copy_hash_size) ; { // copy the formals & install them in the correspondence table name_list dummy_formal(0,0) ; Plist last = &dummy_formal ; for (Plist formal = def->formals ; formal ; formal = formal->l) { Pname copy_name = new name("") ; *copy_name = *formal->f ; copy_name->n_tbl_list = 0 ; last = last->l = new name_list(copy_name, 0) ; (*corr)[int(formal->f)] = (int)copy_name ; } inst_formals = dummy_formal.l ; } bind_formals() ; if ( ! recopy ) { // Pname nnn = k_find_name(tname->string,Ctbl,0); Pname nnn = k_find_name(tname->string,Gtbl,HIDDEN); if ( nnn && nnn->base==TNAME ) { // formal binding may result in detecting identical instantiations Ptempl_inst ti = def->get_match(actuals, this, true) ; if (ti) return ti ; error('i', "generated template instantiation name %swas not unique", tname->string) ; } } { tree_copy_info info ; info.node_hook = ::copy_hook ; info.hook_info = this ; (*corr)[int(def->namep)] = int(tname) ; // make the tnames correspond templ_refs = ref_copy(*corr, info, templ_refs) ; Pnode root = def->basep ; // start the copy at the cobj node // deal with these nodes differently during the copy, ie. the nodes // themselves are not copied, but their attributes are. cobj_node = (Pbase)tname->tp ; cname_node = Pbase(tname->tp)->b_name ; class_node = Pclass(Pbase( tname->tp)->b_name->tp) ; copy_tree (root, info, corr); } // Perform name modifications for the class, so that it is an // instantiation-specific name. cname_node->string = tname->string ; if (!recopy) { // namep = ktbl->insert(tname, 0) ; // namep = insert_type(tname,Ctbl,TYPE) ;//SYM Pktab tb = class_node->k_tbl->k_next; if (tb == 0) tb = Gtbl; else if ( tb->k_id == TEMPLATE ) tb = tb->k_next; namep = insert_type(tname,tb,class_node->csu);//SYM if (reinstat) class_node->k_tbl->k_name = tname; } else class_node->defined &= ~(DEFINED|SIMPLIFIED) ; namep->tp = cobj_node; class_node->modify_inst_names(cname_node->string) ; // don't understand why this is necessary ... if (class_node->k_tbl->k_name->n_ktable == 0) { // error('d',"change k_name: %n",tname); class_node->k_tbl->k_name->n_ktable = namep->n_ktable; } return 0 ; } /* This hook function is responsible for the replacement of references to expression when copying function bodies */ static void function_copy_hook(void *current_templ_inst, Pnode &node, node_class, tree_node_action &action, int& never_see_again) { never_see_again = 1; switch (node->base) { case NAME: { if(node == sta_name) { action = tna_stop; return; } char *s = Pname(node)->string ; Pname f = 0 ; if (s && (*s == '$') && (f = Ptempl_inst(current_templ_inst)->get_parameter(s+1))) { if(Pname(node)->n_list) error ('i', "n_list set in tree template formal."); node = copy_syntax_tree(Pname(f)->n_initializer) ; action = tna_stop ; never_see_again = 0; }else action = tna_continue ; return ; } default: action = tna_continue ; return ; } } Pname templ_inst::function_copy(Pfunt fnt, Pcons &templ_refs) /* * Create a copy of a function member, as part * of the instantiation of a function body. * The correspondence table is first initialized * with the contents of the correspondence table * used to instantiate the class. * Copying is initiated in this context */ { pointer_hash fcorr(*corr); // initialize with old hash table { tree_copy_info info; Pnode root = fnt->fn; /* establish a correspondence between the formals used * for the class template, and the formals used for the * function template, all references to the function * template formals will be replaced by references to * the instantiated class template formals after the * copy has been completed */ for (Plist fformal = fnt->formals, cformal = inst_formals; fformal; fformal = fformal->l, cformal = cformal->l) { fcorr[int(fformal->f)] = int(cformal->f) ; if (fcorr[int(fformal->f)] != int(cformal->f)) error ('i', "templ_inst::fuction_copy: hash table bug"); } info.node_hook = ::function_copy_hook; info.hook_info = this; templ_refs = ref_copy(fcorr,info,templ_refs); if (fcorr[int(def->namep)] != int(tname)) error ('i', "Y to instantiationTN correspondence is missing"); copy_tree(root,info,&fcorr); return Pname(root); } } /***************************************************************************** * * * A matching template was found at instantiation time, which was not * * detected at syntax analysis time. This can happen, when an instantiation * * has as its arguments not real types but template arguments, so that * * matches cannot be detected until the templates are bound. Note that it is * * also possible to match a template that is in the process of being * * instantiated further up the instantiation call chain. In such cases, the * * kludge_copy operation will copy over an incomplete class subtree, which * * will be recopied with the completed one after the instantiation is * * completed, in templ_inst::instantiate. * * * *****************************************************************************/ Pclass current_instantiation = 0 ; void templ_inst::instantiate_match(Ptempl_inst match) { Pbase pb = Pbase(match->tname->tp) ; kludge_copy(pb) ; forward = match ; // Note that template was matched } void print_nested_typedef(Pname n, Pclass cl) { /* need to print a nested typedef now because it serves as a parameter * to a class being instantiated -- mark it as printed within its * enclosing class so it is not subsequently printed a second time */ for (Pname nn = cl->mem_list; nn; nn = nn->n_list) { if ( nn->base != TNAME ) continue; if ( strcmp(nn->string,n->string) == 0 ) { if (nn->n_dcl_printed == 0) { nn->dcl_print(0); nn->n_dcl_printed = 2; } break; } } } Ptempl_inst templ_inst::instantiate(bool reinstantiate) { // ::error('d', "templ_inst::instantiate(%d) tname: %n namep: %n", reinstantiate, tname, namep); if ( (dtpt_opt && curloc.file==first_file && // dummyinst==0 && notinstflag==0 && (tname==righttname || tname && righttname && !strcmp(tname->string, righttname->string))) /*|| matchflag==1*/ ) { dummyinst=this; // matchflag=0; } Pcons templ_refs = def->templ_refs; Templ_type ct = Ptclass(Pbase(tname->tp)->b_name->tp)->class_base; if (! reinstantiate) { switch (ct) { case INSTANTIATED: return this; case UNINSTANTIATED: break; case VANILLA: case CL_TEMPLATE: // the canonical template class case FCT_TEMPLATE: // the canonical template class case BOUND_TEMPLATE: default: error ('i',"attempt to instantiate a non-YC %n",namep); } status = class_instantiated; // Check whether the template has already // been instantiated. if so, use it. forward_template_arg_types(def->formals, actuals); Ptempl_inst match = def->get_match(actuals, this, true); if (match || (match = class_copy(templ_refs, false))) { // if (dummyinst==this) matchflag=1; instantiate_match(match); return this; } } else class_copy(templ_refs, true); Pbase pb = Pbase(tname->tp); if (ansi_opt) { TOK csu = Ptclass(Pbase(tname->tp)->b_name->tp)->csu; fprintf(out_file, "%s %s;\n", csu == UNION || csu == ANON ? "union" : "struct", tname->string); } // Save the state around decl processing { save_state(def->namep); if (def->open_instantiations++ > MAX_INST_DEPTH) { error ("an infinite instantiation sequence was initiated"); def->open_instantiations--; return this; } // Mark the class as instantiated to avoid circular instantiations. Pclass(pb->b_name->tp)->class_base = INSTANTIATED; // if it is a forward reference, rely on the usual compilation to // provide an error message, if indeed it is an error, and not a // benign forward reference such as: friend class foo if (def->basep->b_name->tp->defined) { // need to reset the `where' of the instantiation to that of definitionf pb->b_name->where = def->basep->b_name->where; /* Put out the typedefs for the template parameters * do this before the call to name::dcl below, * since dcl processing will emit c declarations * that make use of the type */ for (Plist formal=inst_formals; formal; formal=formal->l) { if (formal->f->tp->base == TYPE) { Pclass cl; Pname n = Pbase(formal->f->tp)->b_name; if (n->base == TNAME && n->tpdef && (cl = n->tpdef->in_class) && cl->c_body == 1) print_nested_typedef(n,cl); } if (formal->f->n_template_arg == template_expr_formal) { formal->f->dcl_print(0); } } // Instantiate parameterized types referenced by this template for (Pcons pc = templ_refs ; pc ; pc = pc->cdr) Ptempl_inst(pc->car)->instantiate(); tempdcl = 1; cc->stack(); cc->cot=0; cc->not=0; cc->tot=0; cc->c_this=0; curr_inst=this; if (!((pb->b_name->dcl(gtbl,EXTERN) == 0) || error_count)) { pb->b_name->simpl(); Ptype pt = pb->b_name->tp; if (pt->base != CLASS) error('i',"templ_inst::instantiate(%k),CX",pt->base); Pclass cl = Pclass(pt); current_instantiation = cl; pb->b_name->dcl_print(0); if (cl->c_body == 3) cl->print_all_vtbls(cl); if (!(pt->defined & DEFINED)) error ('i', "templ_inst::instantiate: dclC%t is not yet defined", pt); current_instantiation = 0; } curr_inst=0; cc->unstack(); tempdcl = 0; } // bash every template instantiation class that has been forwarded to // it, with the decl processed version. for (Ptempl_inst clone = def->insts ; clone ; clone = clone->next) if (clone != this) { if (clone->forward == this) clone->kludge_copy(Pbase(tname->tp)) ; else { #if 0 // resolve references to forward declarations if (this == def->get_match(clone->actuals, clone, true)) { clone->kludge_copy(Pbase(tname->tp)) ; clone->forward = this ; } #endif } } /* this does not fit in with cfront's lazy print strategy // dcl_print the member functions, so that they can be referenced int i = 0 ; for (Pname fn= Pclass(pb->b_name->tp)->memtbl->get_mem(i=1); fn; NEXT_NAME(Pclass(pb->b_name->tp)->memtbl,fn,i)) if ((fn->base == NAME) && (fn->tp->base == FCT)) fn->dcl_print(0) ; */ restore_state() ; def->open_instantiations-- ; } return this; } /* Template Constructors */ templ_state::templ_state() { // error('d',"templ_state::templ_state"); param_end = templp->param_end; params = templp->params; templ_refs = templp->templ_refs; friend_templ_refs = templp->friend_templ_refs; last_cons = templp->last_cons; owner = templp->owner; // has_expr_formals = templp->has_expr_formals; } templ_state::~templ_state() { // error('d',"templ_state::~templ_state"); templp->param_end = param_end; templp->params = params; templp->templ_refs = templ_refs; templp->friend_templ_refs = friend_templ_refs; templp->last_cons = last_cons; templp->owner = owner; // templp->has_expr_formals = has_expr_formals; } templ::templ(Plist parms, Pname p) // template class X {T t; public: T foo() {return t;}}; { // ::error('d',"templ::templ(%d %n)", parms, p ); formals = parms; // 'T' namep = p; // 'X' basep = Pbase(namep->tp) ; if (basep->base != COBJ) error("YC%n --%n already declared asTdef (%t) ",p,p,p->tp); Ptype t = basep->b_name->tp; Pclass cl = Pclass(t); cl->class_base = CL_TEMPLATE; defined = ((t->defined & DEF_SEEN) ? true : false); if (defined) members = cl->mem_list; PERM(namep); PERM(namep->tp); // Chain on to the list of templates for the compilation. next = templp->list; templp->list = this; } templ_inst::templ_inst (Pexpr act, Ptempl owner) /* * Set up the basetype for the class, so that nodes that * need to point to it during syntax processing can do so. * These objects are merely place-holders during syntax * analysis, and are actually filled in during * the copy phase of instantiation. */ { // error('d',"templ_inst: %n", owner->namep); isa = CLASS; def = owner; tname = new name(def->namep->string); tname->base = TNAME; tname->tp = new basetype(COBJ,new name(def->namep->string)); Pclass c = new templ_classdef(this); Pclass cl = owner->namep->tp->classtype(); // SYM if (cl->k_tbl) c->k_tbl = cl->k_tbl; // SYM Pbase(tname->tp)->b_name->tp = c; PERM(tname); PERM(tname->tp); PERM(Pbase(tname->tp)->b_name); PERM(Pbase(tname->tp)->b_name->tp); // initialize member list so set_scope can do the right thing // SYM archaic c->mem_list = def->classtype()->mem_list; actuals = act; next = owner->insts; owner->insts = this; } templ_inst::templ_inst (Pexpr act, Ptempl owner, TOK csu) { // explicit template class instance of already defined template // error('d',"templ_inst: %n csu: %k", owner->namep, csu); isa = CLASS; def = owner; tname = new name(owner->namep->string); tname->base = TNAME; tname->tp = new basetype(COBJ,new name(owner->namep->string)); Pclass c = new templ_classdef(this,csu); // XXXXX : need to supply some name here for k_name c->k_tbl = new ktable(0,0,tname); c->k_tbl->k_id = CLASS; Pbase(tname->tp)->b_name->tp = c; PERM(tname); PERM(tname->tp); PERM(Pbase(tname->tp)->b_name); PERM(Pbase(tname->tp)->b_name->tp); // initialize member list so set_scope can do the right thing // SYM archaic // ??????????????? XXXXX // c->mem_list = def->classtype()->mem_list; actuals = act; next = owner->insts; owner->insts = this; } funct_inst::funct_inst (Pexpr act, Pfunt owner) /* * mumble */ { // error('d',"funct_inst( act: %d owner: %n", act, owner->fn); isa = FCT; def = owner; tname = new name(def->fn->string); tname->tp = new templ_fct(this); actuals = act; next = owner->insts; owner->insts = this; PERM(tname); PERM(tname->tp); } templ_classdef::templ_classdef(Ptempl_inst i) : classdef(CLASS) { inst = i; class_base = UNINSTANTIATED; string = unparametrized_tname()->string; // error('d',"templ_classdef::templ_classdef: %s", string ); } templ_classdef::templ_classdef(Ptempl_inst i, TOK csu) : classdef(csu) { inst = i; class_base = INSTANTIATED; templ_base = CL_TEMPLATE; // error('d',"templ_classdef::templ_classdef: %s csu %k", string,csu ); } templ_fct::templ_fct(Pfunct_inst i) : fct(0,0,0) { // error('d',"templ_fct::templ_fct: %n",i->tname); inst = i; fct_base = UNINSTANTIATED; } data_template::data_template(templ &owner,Plist params,Pname n) { // Create a new data template. // ::error('d',"data_template(%n,%n)", owner.namep,n); if (owner.data_end) owner.data_end->next = this; else owner.data = this; owner.data_end = this; formals = params; dat_mem = n; PERM(n); PERM(n->tp); } function_template::function_template(templ &owner,Plist params,Pname n) { // Create a new function template. // ::error('d',"function_template(%n,%n)", owner.namep,n); if (owner.fns_end) owner.fns_end->next = this; else owner.fns = this; owner.fns_end = this; formals = params; fn = n; PERM(n); PERM(n->tp); } function_template::function_template(Plist params,Pname n) { // Create a new function template. // ::error('d',"function_template(%n)", n); formals = params; fn = n; Pfct(n->tp)->fct_base = FCT_TEMPLATE; next = templp->f_list; templp->f_list = this; templ_refs = 0; PERM(n); PERM(n->tp); } Pname templ_inst::get_parameter(char *s) { for (Plist formal=inst_formals; formal; formal=formal->l) if (strcmp(formal->f->string,s)== 0) return formal->f; return 0; } Pfunct_inst funct_inst::tfct_copy(Pcons &templ_refs, bool recopy) { // error('d',"%n->tfct_copy(recopy: %d)",tname,recopy); // associate the formals with their types, and their expressions if (recopy) { // remove the function def node from the table, // so that it's attributes are copied. corr->del(int(def->fn)); corr->del(int(tname)); } else corr = new pointer_hash(default_copy_hash_size) ; // copy the formals & install them in the correspondence table name_list dummy_formal(0,0); Plist last = &dummy_formal; for (Plist formal=def->formals; formal; formal=formal->l) { Pname copy_name = new name(""); *copy_name = *formal->f; copy_name->n_tbl_list = 0; last = last->l = new name_list(copy_name, 0); (*corr)[int(formal->f)] = (int)copy_name; } inst_formals = dummy_formal.l; bind_formals(); if ( !recopy && gtbl->look(tname->string, 0)) { // formal binding may result in detecting identical instantiations Pfunct_inst ti = def->get_match(actuals, this, true); if (ti) return ti; error('i',"generatedY instanceN %s not unique",tname->string); } tree_copy_info info; info.node_hook = ::f_copy_hook; info.hook_info = this; Pnode root = def->fn->tp; fct_node = Pfct(tname->tp); // make the tnames correspond ??? (*corr)[int(def->fn)] = int(tname); templ_refs = ref_copy(*corr,info,templ_refs); copy_tree(root,info,corr); return 0; } Pfct current_fct_instantiation; Pfunct_inst fct_instantiation; void funct_inst::instantiate(bool reinstantiate) { // ::error('d', "funct_inst::instantiate(%d) tname: %n namep: %n",reinstantiate,tname,namep); Pcons templ_refs = def->templ_refs; Templ_type ft = Ptfct(tname->tp)->fct_base; if (!reinstantiate) { switch (ft) { case INSTANTIATED: if ( dtpt_opt && fdummyinst==0 && curloc.file==first_file && tempdcl==0 ) { fdummyinst=this; fcurr_inst=this; current_fct_instantiation=tname->fct_type(); if (tname->finst_body()) tname->dcl_print(0); current_fct_instantiation=0; } return; case UNINSTANTIATED: break; case VANILLA: case CL_TEMPLATE: case FCT_TEMPLATE: case BOUND_TEMPLATE: default: error ('i',"attempt to instantiate a non-YF %n",namep); } status = function_instantiated; if (dtpt_opt && fdummyinst==0 && curloc.file==first_file && tempdcl==0) fdummyinst=this; // class template calls forward_template_arg_type() // functions however only take ``type type'' for (Pexpr e = actuals; e; e = e->e2) e->e1->tp = non_template_arg_type(Pbase(e->e1->tp)); Pfunct_inst dup; if (dup=tfct_copy(templ_refs, false)) { // don't believe it should happen -- let's check error('i',"FT %n already instantiated", namep); return; } } else tfct_copy(templ_refs, true); // need to propagate certain fields of the pure template instance // tname's ``where'' is at point of instantiation but the // statements of the function are at the point of definition tname->n_oper = def->fn->n_oper; tname->where = def->fn->where; save_state(def->fn); // save state around decl processing if (def->open_instantiations++ > MAX_INST_DEPTH) { error ("%n: an infinite instantiation sequence was initiated",namep); def->open_instantiations--; return; } // Mark the function as instantiated to avoid circular instantiations. Ptfct ptf = Ptfct(tname->tp); ptf->fct_base = INSTANTIATED; // Instantiate parameterized types referenced by this template for (Pcons pc=templ_refs; pc; pc=pc->cdr) { Ptempl_inst(pc->car)->instantiate();; } if (reinstantiate && tname->n_table) tname->n_table = 0; if (fct_instantiation == 0) fct_instantiation = this; fcurr_inst=this; cc->stack(); cc->cot = 0; cc->not = 0; cc->tot = 0; cc->c_this = 0; if (!((tname = tname->dcl(gtbl, EXTERN)) == 0) || error_count) { fcurr_inst=this; if (se_opt && tname->finst_body()==0) suppress_error++; tname->simpl(); Ptype pt = tname->tp; if (pt->base != FCT) error('i',"funct_inst::instantiate(%k),FX",pt->base); if (fct_instantiation != this && fct_instantiation->namep == namep) { // error('d',"fct_instantiation: %n",fct_instantiation->tname); Pfct f = fct_instantiation->tname->fct_type(); Pblock b = f->body; f->body = 0; current_fct_instantiation = f; Pblock b1; if (dtpt_opt && tname->finst_body()==0) { b1=tname->fct_type()->body; tname->fct_type()->body=0; } fct_instantiation->tname->dcl_print(0); if (dtpt_opt && tname->finst_body()==0) tname->fct_type()->body=b1; current_fct_instantiation = 0; f->body = b; } current_fct_instantiation = Pfct(pt); Pblock b; if (se_opt && tname->finst_body()==0) suppress_error--; if (dtpt_opt && tname->finst_body()==0) { b=tname->fct_type()->body; tname->fct_type()->body=0; } tname->dcl_print(0); if (dtpt_opt && tname->finst_body()==0) tname->fct_type()->body=b; fcurr_inst=0; current_fct_instantiation = 0; } fcurr_inst=0; cc->unstack(); restore_state() ; def->open_instantiations--; if (fct_instantiation == this) fct_instantiation = 0; } static int has_templ_arg( Pclass cl1, Ptclass cl2, Pbinding p, int& ni) { /* should be able to meld these two instances of * has_templ_arg -- this is expediency for users * cl1: min(X), cl2: min(X) * need to bind T1 == int, T2 == double */ Ptempl t = templp->is_template(cl1->string); if (t == 0) error('i',"has_templ_arg: unable to retrieveCY%t",cl1); Plist formals = t->get_formals(); Pexpr actuals = cl2->inst->actuals; for (; formals && actuals; formals=formals->l, actuals=actuals->e2) { // get the associated actual type Ptype at = actuals->e1->tp; while (at->base == TYPE) at = at->bname_type(); // if not bound yet, bind Type to type // if already bound, check for consistency int i = -1; while ( ++i < ni ) { if (p[i].param == formals->f) break; } if ( i < ni ) { // previously bound if(p[i].typ->check(at,0)) { if (!const_problem) return 0; } continue; } p[ni].param = formals->f; p[ni++].typ = at; } return 1; } static int has_templ_arg( Ptclass cl1, Ptclass cl2, Pbinding p, int& ni) { /* cl1: min(X), cl2: min(X) * need to bind T1 == int, T2 == double */ Pexpr formals = cl1->inst->actuals; Pexpr actuals = cl2->inst->actuals; for (; formals && actuals; formals=formals->e2, actuals=actuals->e2) { // find any formal Type Pexpr fe = formals->e1; if (fe->base != NAME || fe->tp->base != TYPE) continue; Pname ftn = fe->tp->bname(); if (!ftn->is_template_arg()) continue; // get the associated actual type Ptype at = actuals->e1->tp; while (at->base == TYPE) at = at->bname_type(); // if not bound yet, bind Type to type // if already bound, check for consistency int i = -1; while ( ++i < ni ) { if (p[i].param == ftn) break; } if ( i < ni ) { // previously bound if(p[i].typ->check(at,0)) { if (!const_problem) return 0; } continue; } p[ni].param = ftn; p[ni++].typ = at; } // end: for (formals && actuals) return 1; } static bit formal_not_const(Pname nn) /* called by is_ftempl-match if type::check sets const_problem: * ok if problem is that formal is const * yes: could be made recursive if arg is nn->tp ... */ { // error('d',"formal_not_const %n %t", nn, nn->tp); Ptype t = nn->tp; bit cnst = t->tconst(); loop: if (cnst == 0) // parallels type::check's behavior if (t->base == PTR || t->base == RPTR) { t = Pptr(t)->typ; cnst = t->tconst(); goto loop; } return cnst == 0; } static void check_valid_formal_type(Ptype et) { et = et->skiptypedefs(); switch( et->base ) { case FIELD: error('s',"formalYZ of type bit field" ); break; case VOID: error("formalYZ of type void illegal"); break; } } Pslot* is_ftempl_match(Pexpr actuals, Pfunt ft) { // error('d',"is_ftempl_match: %n", ft->fn); int count = ft->get_formals_count(); Pslot* parray = new Pslot[count]; int ni=0; Pfct f = ft->fn->fct_type(); Pexpr e = actuals; Pname nn = f->argtype; // turn on special handling of base/derived // error('d',"template_hier : %d fn: %n", template_hier, ft->fn); for( ; e; e=e->e2, nn=nn->n_list) { if (nn == 0) { if (f->nargs_known==ELLIPSIS) {return parray;} delete parray; return 0; } Pexpr a = e->e1; Ptype et = a->tp; check_valid_formal_type(et); Ptype t = nn->tp; if (t->is_ref()) { t = Pptr(t->skiptypedefs())->typ; if (et->is_ref()) et = Pptr(et->skiptypedefs())->typ; } if (t->check(et,OVERLOAD)) { if (const_problem) { // ok: f(T,int); const int actual; // ok: f(const T*,const int*); int *actual; if ( t->is_ptr_or_ref() != 0 && et->is_ptr_or_ref() != 0 && formal_not_const(nn)) { delete parray; return 0; } } else { delete parray; return 0; } } // handle things like formal: Type& actual: char* // *** this should be recursive int ptr_count = 0, ref_count = 0; Ptype nt = nn->tp; Pname ptm = 0; if (nt->is_ref()) { nt = Pptr(nt->skiptypedefs())->typ; } while(t=nt->is_ptr()) { ++ptr_count; Pptr p = Pptr(t); if (p->ptname) { ptm = p->ptname; break; } nt = Pptr(t)->typ; } while(ptr_count-- && (t=et->is_ptr())) { Pptr p = Pptr(t); if (p->memof) { et = p->memof; break; } et = Pptr(t)->typ; } Pname bn = ptm; if (bn == 0) { while(t=nt->is_ref()) { ++ref_count; nt = Pptr(t)->typ; } while(ref_count-- && (t=et->is_ref())) et = Pptr(t)->typ; if(nt->base != TYPE) continue; while (nt->base == TYPE) { bn = nt->bname(); if (bn->is_template_arg()) break; else bn = 0; nt = nt->bname_type(); } } while ( et->base == TYPE ) et = et->bname_type(); if (bn == 0) { if (nt->base == COBJ && et->base == COBJ) { // declaration: min(X), call: min(X) Pclass c1 = nt->classtype(); Pclass c2 = et->classtype(); // formal argument is not a template class -- skip it if ( !c1->class_base ) continue; // are the two different states of the same class // or is the second class a public base of the first? // no? then skip it.... if ( c1->class_base == CL_TEMPLATE ) { if (same_class(c1,c2,1) == 0 && c2->is_base(c1->string) == 0 ) continue; } else if (same_class_templ(c1,c2) == 0 && (c2=c2->is_base(c1->string)) == 0 ) continue; // two possibilities -- formal argument is either // an abstract template or uninstantiated ... int bound_formals = 0; if (c1->class_base == CL_TEMPLATE) bound_formals = has_templ_arg(c1,Ptclass(c2),parray,ni); else bound_formals= has_templ_arg(Ptclass(c1),Ptclass(c2),parray,ni); if ( !bound_formals ) { delete parray; return 0; } continue; } else continue; } Pptr p = 0; if (et->base==PTR && Pptr(et)->typ->base == FCT) { Pfct f = Pfct(Pptr(et)->typ); if (f->fct_base == FCT_TEMPLATE) { error("actual argument toFY%n is an uninstantiatedFY",ft->fn); delete parray; return 0; } if (f->body) { Pfct ff = new fct(0,0,0); *ff = *f; ff->body = 0; p = new ptr(PTR,ff); p->memof = ((Pptr)et)->memof; p->ptname = ((Pptr)et)->ptname; } } if (et->base==VEC) p = new ptr(PTR,Pvec(et)->typ); int i = -1; while(++i < ni) { if(parray[i].param == bn) break; } if(i < ni) { if(parray[i].typ->check(p==0?et:p,0)) { if (const_problem) continue; delete parray; return 0; } continue; } if ( et->base == OVERLOAD ) { delete parray; return 0; } parray[ni].param = bn; parray[ni++].typ = p == 0 ? et : p; } if (nn && !nn->n_initializer) { delete parray; return 0; } if (ni < count) return 0; return parray; } Pname has_templ_instance(Pname fn, Pexpr arg, bit no_err) { /* * invoked by a use of this function, fn: expr::call_fct, ptof * args: the actual arguments of the use * if matches template function, return instantiated function */ // error('d',"has_templ_instance(%n)", fn); if ( fn->is_template_fct() == 0 ) return 0; Pfunt ft = templp->is_template(fn->string,FCT); if (ft==0) error('i',"%n flagged asYF but not entered inY table",fn); if (ft->gen_list && fn->tp->base != OVERLOAD) error('i',"%n gtbl: non-overloaded,Y table: overloaded",fn); Pbinding pb = 0; Pfunt instance = 0; // account for derived->base conversions when matching fnc. templates bit used_conv = 0; // did matching template use conversion ? int conv_reqd = 0; // number of matches requiring conversion int no_conv_reqd = 0; // number of exact matches for (Pfunt p=ft; p; p=p->gen_list) { // find matching template Pbinding b; template_hier = 1; Nvirt = 0; // set by classdef::is_base() if conversion used // error('d',"template_hier : %d fn: %n", template_hier, ft->fn); if (b = is_ftempl_match(arg,p)) { if (!instance) { pb = b; instance = p; used_conv = Nvirt==0 ? 0 : 1; } // earlier match req'd conversion, this one doesn't else if (used_conv && Nvirt==0) { pb = b; instance = p; used_conv = 0; } Nvirt==0 ? no_conv_reqd++ : conv_reqd++; } } if (!instance) { if (fn->tp->base == FCT && !no_err) { error("use ofYF%n does not match any of itsY definitions", fn); } template_hier = 0; return 0; } else if ((no_conv_reqd>1) || (no_conv_reqd==0 && conv_reqd>1)) { error("use ofYF%n matches multiple instances",fn); // use `instance' for rest of compilation } // get (or generate) correct instantiation of template // error('d',"has_templ_instance: instance: %n",instance->fn); template_hier = conv_reqd; Pfunct_inst fctmpl = instance->get_inst(arg); template_hier = 0; fctmpl->binding = pb; fctmpl->instantiate(); Pname fct_inst = fctmpl->get_tname(); // error('d',"has_templ_instance: instantiated function %n", fct_inst); return fct_inst; } void basic_template::dummy() { abort(); } void basic_inst::dummy() { abort(); }