/* ident "@(#)ctrans:src/template.c 1.3" */ /******************************************************************* * template.c * * This file contains most of the implementation for a subset of * parametrized types as defined by the 1989 Stroustrup JOOP paper. * The subset chosen here was the subset relevant to the * implementation of aggregates in ObjectStore. * * The following is a list of features supported by the * implementation. The list of features parallels the * description of templates in the JOOP paper. * * a) Class templates supported. * * b) Member function templates supported. * Type-specific member functions as described at the * end of the "Outline of an implementation" section * are also supported. * * Non-member function templates are not supported. * * c) Template arguments may be of type "type" or * simple integral, real, double and pointer types * that are compile-time constants. * * Default arguments are not suported. * * * Restrictions: * * a) template definitions may not be nested. * * b) enums, or class definitions may not be * nested within a template class definition. * * This file also supports an internal template facility * to facilitate the implementation of ObjectStore data * model features. The internal template facility is * only used by compiler implementors, and is not user visible. * ************************************************************/ /*********************************************************** * * TBD * * 1) Error recovery, a never ending task, could stand improvement. * * 2) 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. * * 3) Clean up $name processing, it needs to be remodularized * so that class templates and tree templates share the code. * * 4) Permit parametrized name default names within member * functions. * ****************************************************************/ #include "tree_copy.h" #include "cfront.h" #include <string.h> #include "tree_dump.h" #include "template.h" #include <stdlib.h> #include <ctype.h> #include "hash.h" extern int bound ; // is not mentioned in the header file const int max_string_size = 1024 ; const int default_copy_hash_size = 1000 ; // 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 ; no_of_badcall = ::no_of_badcall; no_of_undcl = ::no_of_undcl ; badcall = ::badcall ; undcl = ::undcl ; } ; 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 ; ::no_of_badcall = no_of_badcall; ::no_of_undcl = no_of_undcl ; ::badcall = badcall ; ::undcl = undcl ; } ; void state::init() { ::bound = 0 ; ::inline_restr = 0 ; ::no_of_badcall = ::no_of_undcl = 0 ; ::undcl = ::badcall = NULL ; // lastline needs to be initialized probaly via a call to putline } bit basetype::parametrized_class() { return ((base == COBJ) && Ptclass(Pbase(this)->b_name->tp)->class_base == uninstantiated_template_class) ; } class_type_enum get_class_base (Pbase b) { if (b->base != COBJ) error('i', "badA top ::get_class_type") ; return Ptclass(Pbase(b)->b_name->tp)->class_base ; } Ptclass get_template_class (Pbase b) { class_type_enum t = get_class_base(b) ; if (! ((t == instantiated_template_class) || (t == uninstantiated_template_class))) error ('i', "C is not aYC") ; return Ptclass(Pbase(b)->b_name->tp) ; } Ptempl_inst get_templ_inst(Pbase b) { return (get_template_class(b))->inst ; } bit classdef::parametrized_class() { return (class_base == uninstantiated_template_class) ; } // 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. bit classdef::same_class(Pclass pc) { if (this == pc) return true ; // An intermediate test to compensate for the fact that instantiations do // not cause a copy of the syntax tree to be generated. This kludge should // not be necessary once the template implementation is complete, and tree // copying is implemented. // Later, Sam: tree copying is now implemented, i need to remove the // following two statements and rerun the test suite. if ((this->class_base == template_class) && (pc->class_base == instantiated_template_class) && (Ptclass(pc)->inst->def_basetype()->b_name->tp == this)) return true ; // The inverse symmetric test if ((pc->class_base == template_class) && (this->class_base == instantiated_template_class) && (Ptclass(this)->inst->def_basetype()->b_name->tp == pc)) return true ; // Check whether the templates were determined to be identical after // instantiation. if ((pc->class_base == instantiated_template_class) && (this->class_base == instantiated_template_class) && (Ptclass(this)->inst->same(Ptclass(pc)->inst))) return true ; return false ; } // determine whether two instantiations are identical; the test asumes that // the templates have been instantiated. bool templ_inst::same(Ptempl_inst t) { return ((forward && (forward == t->forward)) || (forward == t) || (t->forward == this)) ? true : false ; } /* Template parsing support */ // The canonical template compilation instance. templ_compilation *templp ; templ_compilation::templ_compilation() { templates = new table(128, NULL, NULL) ; any_type = new basetype(ANY, NULL); PERM(any_type) ; } // determine whether the string corresponds to a tree formal parameter Pname templ_compilation::tree_parameter(char *s) { for (Plist formal = params ; formal ; formal = formal->l) if (strcmp(formal->f->string, s) == 0) { formal->f->n_used++ ; return formal->f ; } return 0 ; } // Determine whether the name refers to the canonical template class during // syntax analysis. Ptempl templ_compilation::is_template(Pname p) { if (p->tp && (p->tp->base == COBJ) && (get_class_base(Pbase(p->tp)) == template_class)) { Pname n = templates->look(p->string, 0) ; return (n ? Ptempl(n->tp) : 0) ; } return 0 ; } // determine whether the string names a template Ptempl templ_compilation::is_template(char *s) { Pname n = templates->look(s,0) ; return (n ? Ptempl(n->tp) : 0) ; } // Set up the environment for parsing a template. This involves setting up a // new nesting level into which the "type type" parameters of the template can // be entered, so that the lexer can find them as TNAMES. The scope is // deallocated by end(). void templ_compilation::start() { templp->in_progress = true ; // Reinitialize the state. params = param_end = NULL ; owner = NULL ; modified_tn = 0 ; // Initialize it here, since ::collect adds new types } // Collect each parameter as it is parsed, and add it to the list of parms. // Validate each parameter to make sure that it is one of the acceptable // types. void templ_compilation::collect(TOK parm_type, Pname n) { switch (parm_type) { case CLASS: // A "type type" parameter, give it the "ANY" type normally used as a // wildcard match internally by the compiler in cases of error. n->tp = new basetype(ANY, 0); n = n->tdef() ; // Set it up to be a typedef. n->lex_level = bl_level + 1 ; // Inner scope, so restore() can hack it n->n_template_arg = template_type_formal ; PERM(n) ; PERM(n->tp) ; break ; case STATEMENT: case EXPRESSION: // the argument is a post-syntax expression tree n->n_template_arg = ((parm_type == EXPRESSION) ? template_expr_tree_formal : template_stmt_tree_formal) ; // canonical any_type is ok here n->tp = any_type ; PERM(n) ; break ; default: error("theZT for%n must be CLASS, not %k", n,parm_type); } append_parameter(n) ; } // append the "non-type" parameter to the end of the list void templ_compilation::append_parameter(Pname n) { if (params){ param_end->l = new name_list(n, NULL) ; param_end = param_end->l ; }else params = param_end = new name_list(n, NULL) ; PERM(n) ; PERM(n->tp) ; } // collect non "type type" parameters. The tp field of the name // indicates the type of the formal parameter. void templ_compilation::collect(Pname n) { // The grammar alone should be sufficient to protect against undesirable // types. Any additional checks go here. n->n_template_arg = template_expr_formal ; append_parameter(n) ; } // validate the type for a non-type formal, and make it a const. static void check_non_type_formal(Pname n) { switch (n->tp->base) { case ZTYPE: case CHAR: case SHORT: case INT: case LONG: case FLOAT: case DOUBLE: 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); Pbase b = new basetype(0, 0) ; *b= *Pbase(n->tp) ; b->b_const = 1 ; n->tp = b ; break ; } case PTR: { Pptr b = new ptr(0,0) ; *b = *Pptr(n->tp) ; b->rdo = 1; n->tp = b ; break ; } case RPTR: case VEC: break; // constant by definition default: error ("badZT %t for formalZ %n", n->tp, n); } return ; } // 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. void templ_compilation::enter_parameters() { for (Plist list = params ; list ; list = list->l) { Pname n = list->f ; switch(n->n_template_arg) { case template_type_formal: // Set them up for restoration modified_tn = new name_list(n,modified_tn); // 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 ; case template_expr_tree_formal: case template_stmt_tree_formal: // simply note it, the guts of the processing takes place when the // copy of the syntax tree is generated. break ; default: error ('i', "badY formal" ) ; } } // Save away the list of modified_tn, since the body processing will clobber // it. param_tn = modified_tn ; modified_tn = 0 ; } // 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. void templ::resolve_forward_decl(Plist params, Pclass c) { check_formals(params) ; formals = params ; defined = true ; definition_number = ++ definition_tick ; members = c->mem_list ; } 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_template_class && ! i->forward) { // reinstantiate it i->instantiate(true) ; } } // verify thet the qualifier used to declare the member function matches the // template arguments in name, ie. // template <class P, class Q, ..> c<P,Q,..>::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 ; case template_expr_tree_formal: case template_stmt_tree_formal: default: error ('i',"badY formal") ; } return true ; } // make the class template visible when compiling the template class // defintion, so that it can be refernced while compiling the class body. void templ_compilation::introduce_class_templ(Pname namep) { owner = is_template(namep) ; // create a template definition if one did not already exist, due to a // forward declaration if (!owner){ owner = new templ(params, namep) ; Pname lookup_name = templp->templates->insert(new name(namep->string), 0); lookup_name->tp = Ptype(owner) ; // lie, to permit use of the table } } // The body of the template has been parsed. Finish the definition of the // template class. void templ_compilation::end(Pname p) { bool forward_definition = false ; // Restore the name environment to the state before the template parameters // were processed. modified_tn = param_tn ; restore() ; modified_tn = 0 ; if (curr_tree_template) { // create an expression template new tree_template(curr_tree_template, p->string, params, p->n_initializer, templ_refs) ; }else { if (!p->tp) { error ("aC, orMF definition wasX") ; return ; } switch(p->tp->base){ case CLASS: // Create the template type to represent the parsed template, and enter 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) ; // check for nested definitions, they aren't supported currently. 2.1 is // a good time to start supporting them, since they are nested for real. for (Pname nn = Pclass(p->tp)->mem_list ; nn ; nn = nn->n_list) switch (nn->base) { case NAME: switch(nn->tp->base) { case CLASS: error("CD %s not permitted within a ZizedC", nn->string) ; break ; case ENUM: error("enumD %s not permitted within a ZizedC", nn->string) ; break ; } break ; case TNAME: error("typedef %s not permitted within a ZizedC", nn->string) ; break ; } owner = is_template(namep); if (owner) { Pclass c = Pclass(owner->basetype()->b_name->tp) ; // 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) ; }else // a forward declaration introduce_class_templ(namep) ; if (templ_refs) owner->templ_refs = templ_refs ; break ; case FCT: { Pname qual = p->n_qualifier ; // the function must belong to a declared template class if (! qual) { error('s', "onlyYMFs may beZized currently") ; return ; } if (qual->tp && (qual->tp->base == COBJ)) switch (get_class_base(Pbase(qual->tp))) { case uninstantiated_template_class: owner = Ptclass(Pbase(qual->tp)->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 (Pbase(qual->tp))->inst->check_qualifier(params)) error ("QrZs must match theY formals") ; break ; case template_class: // the template reference was without any of the formals owner = is_template(qual) ; if (! owner->has_tree_expr_formals()) error('w',"Qr %n for %n must specifyYZs", qual, p) ; break ; default: error ("Qr %n for %n wasn't aYC", qual, p) ; return ; } Pfunt ft= owner->collect_function_member(p) ; if (! Pfct(p->tp)->body) error ("theYFM %n must have a body", p) ; ft->templ_refs = templ_refs ; ft->formals = params ; owner->check_formals(params) ; break ; } default: error ("C, or MF definitionX.") ; } } // Note the template references from this definition clear_ref_templ() ; param_end = params = 0; // Indicates the end of template processing. curr_tree_template = 0 ; if (forward_definition) owner->instantiate_forward_decl() ; owner = 0 ; } // Clear the list of templates referenced during the syntax analysis of a top // level definition. Note that since this list is produced during syntax // analysis, it does not recognize instantiations that may actualy turn out to // be identical at instantiation after the substitution of actual parameters. // Thus, the list may be longer than it would be after actual argument // substitution. void templ_compilation::clear_ref_templ() { for (Pcons p = templ_refs ; p ; p = p->cdr) Ptempl_inst(p->car)->refp = false ; templ_refs = 0 ; last_cons = 0 ; } // Instantiate templates that were referenced by a non-template definition, // after syntax analysis has been completed on it. void templ_compilation::instantiate_ref_templ() { for (Pcons p = templ_refs ; p ; p = p->cdr) Ptempl_inst(p->car)->instantiate() ; clear_ref_templ() ; } // 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. void templ_compilation::end_of_compilation() { bool change = false ; do { change = false ; for (Ptempl p = list ; p ; p = p->next) change = ( change | p->instantiate_bodies() ? true : false); } while (change) ; } // A predicate to validate that a tname without template parameters is legit // in the scope, ie. 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. Pname templ_compilation::check_tname(Pname p) { Ptempl t = is_template(p) ; if (p->n_template_arg) p->n_used++ ; if (!t) return p ; if (in_progress && ((owner && (owner->namep == p)) || (!owner && t->basetype()->b_name->tp == ccl))) return p ; error ("%n needs Y instantiationAs.", p) ; return p ; } // This function determine when the parameters specified to a template are // redundant, and really refer to the current template class. Thus, // // template c<class p1, class p2> c<p1,p2>::foo { ... } ; // has the redundant template specification c<p1, p2> and can simply be a // reference to a "c" instead, bool templ_compilation::current_template(Pname p, Pexpr actuals) { if (in_progress && ((owner && (owner->namep == p)) || (!owner && ((p->tp->base == COBJ) && (Pbase(p->tp)->b_name->tp == 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->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 ; } // Add a new member function to the list of functions for the template class. Pfunt templ::collect_function_member(Pname fname) { PERM(fname) ; PERM(fname->tp) ; PERM(Pfct(fname->tp)->body) ; return new function_template (*this, templp->params, fname) ; } // Check the formals specified for a member function or a forward definition // of a class, against the formals for the class. void basic_template::check_formals(Plist f2) { 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 a type formal parameter.", f2->f) ; break ; case template_expr_formal: error ("formalZ mismatch, %n must be aZ ofT %t", f2->f, f2->f->tp) ; break ; case template_expr_tree_formal: case template_stmt_tree_formal: error ("formalZ mismatch, %n must be anE formalZ", f2->f) ; 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 ("type mismatch betweenM formal %n, andC formal %n", f2->f, f1->f) ; } if (f1) error ("insufficient formalZs,CZ parameter %n onwards are missing", f1->f) ; if (f2) error ("excess formalZs,Z %n onwards not defined forC", f2->f) ; } // predicate to determine whether the template has expression tree formals bool templ::has_tree_expr_formals() { for (Plist formal= formals; formal ; formal = formal->l) if (formal->f->n_template_arg == template_expr_tree_formal) return true ; return false ; } // Check that those formals that require class actual arguments, due to their // use in member function bodies get them. bool basic_template::check_constraints(Pexpr actual) { bool ret = true ; for (Plist formal = formals ; formal && actual ; formal = formal->l, actual = actual->e2) if ((formal->f->n_template_arg == template_type_formal) && formal->f->n_template_formal_must_be_class) { Pname n = Pname(actual->e1) ; if (n && n->tp && n->tp->is_cl_obj()) continue ; error("expected aCT actual, not %t, for the \"TT\" formal %s", n->tp, formal->f->string); ret = false ; } return ret ; } // Check actual template arguments, against the formals. bool templ::check_actual_args(Pexpr actual) { for (Plist formal = formals ; formal && actual ; formal = formal->l, actual = actual->e2) 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. Pname n = Pname(actual->e1) ; if (!((n->base == NAME) && (n->n_template_arg == template_actual_arg_dummy))) { error ("Y %s A mismatch, the Y formal:%n\ required aT actualZ.", namep->string, formal->f) ; // recover from the error with a safe expression n = new name("") ; n->tp = any_type ; actual->e1 = n ; } if ((formal->f->n_template_formal_must_be_class) && !(n->tp && n->tp->is_cl_obj())) error("expected aCT actual, not %t, for the \"TT\" formal %s", n->tp, formal->f->string) ; break ; } case template_expr_formal: // checking can only be done at instantiation, so postpone it break ; case template_expr_tree_formal: case template_stmt_tree_formal: // anything is acceptable, it is a case of "implementor beware". Any // illegalities will only be detected when dcl processing takes place. break ; default: error ('i',"badY formal") ; } // 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) ; } return bool(~(formal || actual)) ; } // Append to the list of references. void templ_compilation::append_ref(Ptempl_inst ref) { cons *p = new cons(ref,0) ; if (last_cons) last_cons->cdr = p ; else templ_refs = p ; last_cons = p ; } ; // Note the reference by a definition to the template, so that the template // can be instantiated before the definition is processed. Ptempl_inst templ_inst::note_ref() { if ((! refp)) { refp = true ; templp->append_ref(this) ; } return this ; } // Get an instantiation for the template with the given set of actuals. If one // exists, return it, otherwise create a new one. Ptempl_inst templ::get_inst(Pexpr actuals, Ptempl_inst exclude) { Ptempl_inst inst = get_match(actuals, exclude, false) ; return (inst ? inst : new templ_inst(actuals, this))->note_ref() ; } // Find an instantiation that has the same set of actuals, exclude the // instantaition passed in from the set of candidates Ptempl_inst templ::get_match(Pexpr actuals, Ptempl_inst exclude, // don't match this one // Only instantiated templates match bool match_instantiated_only) { 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_template_class) : true)) return (p->forward ? p->forward : p) ; return NULL ; } // provides the basetype created for a given set of actuals. Pbase templ::inst_basetype(Pexpr actuals) { return (check_actual_args(actuals) ? Pbase(get_inst(actuals)->tname->tp) : basep) ; } // Validate that the name denotes a parametrized type, and prodouce a TNAME // for the instantiation. Pname parametrized_typename (Pname p, Pexpr actuals) { 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)) return p ; if (t) { Pname tname = t->typename(actuals) ; return (tname ? tname : p) ; } error ("%n hasTAs but is not aZizedC", p) ; return p ; } Pbase parametrized_basetype (Pname p, Pexpr actuals) { 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)) return Pbase(p->tp) ; if (t) return t->inst_basetype(actuals) ; error ("%n is not aZizedC", p) ; return new basetype(TYPE, p); } // Similar primitive for obtaining the typename associated with an // instantiation. Pname templ::typename(Pexpr actuals) { return (check_actual_args(actuals) ? get_inst(actuals)->tname : 0) ; } // predicate to get past all the type nodes static Ptype real_type (Ptype t) { while (t->base == TYPE) t = Pbase(t)->b_name->tp ; return t ; } static int expr_match(Pexpr a1, Pexpr a2) ; // Check whether the actuals provided match the actuals for this instantiation. // The actuals match the formals, iff they are the 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 ; case template_expr_tree_formal: case template_stmt_tree_formal: // these are internal instantiations and consequently never match return false ; default: error ('i', "bad template formal") ; } return true ; } extern char emode ; // This set of overloaded fuctions are used to accumulate name strings void stradd(char *&target, char *source) { while (*target++ = *source++) ; target-- ; } void stradd(char *&p, long i) { if (!emode) { *p++ = 'L' ; } char s[16] ; sprintf(s,"%ld", i) ; stradd(p,s) ; } void stradd(char *&p, Pname n) { if (!emode){ char s[256] ; sprintf(s,"%d", strlen(n->string)) ; stradd(p,s) ; } stradd(p, n->string) ; } // produce a unique string suitable for use within a name; if in emode ie. // printing in the context of an error, print a pretty name instead. char * mangled_expr(char *p, Pexpr e, bool mangle_for_address = false) { static int mangle_address = 0 ; if (e == 0) return p; switch (e->base) { case ADDROF: case G_ADDROF: // unary using e2 // rely on the mangle_address++ ; p = mangled_expr(p, e->e2) ; mangle_address -- ; break ; case NAME: if (mangle_address || mangle_for_address) stradd(p, Pname(e)) ; else if (Pname(e)->n_evaluated) stradd(p, Pname(e)->n_val) ; else if (Pname(e)->n_initializer) p = mangled_expr(p, Pname(e)->n_initializer, mangle_for_address) ; else error ('i', "couldn't mangle %n", e) ; break ; case IVAL: stradd(p, ((ival *)e)->i1) ; break ; case CAST: { // an IVAL hiding below the cast ? if (e->e1->base == IVAL) stradd(p, ((ival *)e->e1)->i1) ; else error ('i', "unexpected cast") ; break ; } case ICON: case CCON: case FCON: *p++ = 'L' ; strcpy(p, e->string) ; // Sam: there needs to be a better encoding scheme, but it can wait. if (!emode) while (*p) if (! (isalnum(*p))) switch(*p) { case '+': *p++ = 'p' ; break ; case '-': *p++ = 'm' ; 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 ; default: error ('i', "can't mangle %k", e->base) ; } return p ; } // this function is invoked once at the top level char *mangled_expr(char *p, Pname n) { if (n->n_evaluated) { stradd(p, n->n_val) ; return p ; } return mangled_expr(p, n->n_initializer, (real_type(n->tp)->base == PTR ? true : false)) ; } // Generate a template instantiation name suitable for printing when it is // presented to the user. void templ_inst::print_pretty_name() { extern char emode ; fprintf(out_file, " %s<", (def->namep ? def->namep->string : "?")) ; Plist formal = inst_formals ; emode ++ ; // so that dcl_print generates c++ rather than c names for (Pexpr ae1=actuals; formal && ae1 ; ae1=ae1->e2, formal = formal->l) { switch (formal->f->n_template_arg) { case template_type_formal: ae1->e1->tp->dcl_print(0) ; break ; case template_expr_formal: { char buff[256] ; mangled_expr(buff, ae1->e1) ; fprintf(out_file, "%s", buff) ; break ; } case template_expr_tree_formal: case template_stmt_tree_formal: // doesn't participate in the name generation break ; default: error ('i', "unexpected formal") ; } // this comma is unfortunately misplaced, since it follows a space printed // out by dcl_print if (formal->l) fprintf(out_file, ", ") ; } fprintf(out_file, ">") ; emode -- ; } // there are different representations for ICON based upon whether it has been // evaluated. int check_for_const(Pexpr a1, Pexpr a2) { Neval = 0 ; if ((a1->base == NAME) && ((a2->base == ICON) || (a2->base == IVAL) || (a2->base == ZERO))) { Pname n = Pname(a1) ; return (n->n_evaluated && (n->n_val == a2->eval())) ; }else if (((a1->base == ICON) || (a1->base == IVAL) || (a1->base == ZERO)) && (a2->base == NAME)) { Pname n = Pname(a2) ; return (n->n_evaluated && (n->n_val == a1->eval())) ; } 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; // if (a1->base != a2->base) return check_for_const(a1, a2) ; 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: { // unary using e2 addr_of ++ ; int result = (expr_match(a1->e2, a2->e2)) ; addr_of -- ; return result ; } case CAST: return (expr_match(a1->e1, a2->e1)) ; case NAME: if (! addr_of) { Pname n1 = Pname(a1), n2 = Pname(a2) ; 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) ; } 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: case FCON: case STRING: return (strcmp (a1->string, a2->string) == 0) ; case ZERO: return true ; case SIZEOF: { long l1 = a1->eval(), l2 = a2->eval() ; return (l1 == l2) ; } } return false ; } static char * non_type_argument_signature(Pexpr e, char *p) { p = e->tp->signature(p) ; return mangled_expr(p, e) ; } // generate array suffix information for an array signature. cfront does not // normally generate this as part of the type signature, since it only deals // with argument signatures, and arrays can't be arguments. static void add_array_type_suffix(char *&s, Ptype t) { t = real_type(t) ; if (t->base == VEC) { Pvec v = Pvec(t); int dim; char a[32] ; Neval = 0 ; if(v->dim == 0) dim = v->size / v->typ->tsizeof(); else dim = (int)v->dim->eval(); sprintf(a, "A%d", dim) ; stradd(s,a) ; add_array_type_suffix(s, Pvec(t)->typ); } } // Generate a mangled template instantiation name. The syntax of as template // mangled class name is of the form: // original_name__<argument type signatures>__unique_id // Each non-type argument is replaced by a unique id. char *templ_inst::mangled_name(char *ip) { static tree_formal_id = 0 ; 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) ; add_array_type_suffix(p,ae1->e1->tp) ; break ; case template_expr_tree_formal: case template_stmt_tree_formal: *p++ = 'Y' ; stradd(p, tree_formal_id++) ; 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) ; } return start ; } // The C compiler barfs when it is passed on through C++ as an automatic // variable const char leader[]= "\t" ; // Explain the location of an instantiation in greater detail, since it may be // far removed from it's textual definition. void templ_inst::print_error_loc() { if (! head) return ; // No active instantiations extern void print_loc() ; state current_state ; char buffer[max_string_size] ; for (int i = 0 ; i < max_string_size; i++) buffer[i] = 0 ; current_state.save() ; fprintf (out_file, "%sanomaly detected during the instantiation of", leader) ; print_pretty_name() ; fprintf(out_file, "\n") ; if (!head->next_active) { // A more compact message for a single level of instantiation context.restore() ; fprintf (out_file, leader) ; print_loc() ; fprintf (out_file, "was the site of the instantiation\n") ; }else { // The instantiation chain is longer than one fprintf (out_file, "%sthe instantiation path was:\n", leader) ; for (Ptempl_inst p = head ; p ; p = p->next_active) { p->context.restore() ; print_loc() ; fprintf (out_file, " template:") ; p->print_pretty_name() ; fprintf(out_file, "\n") ; } } current_state.restore() ; } // Generate a class name for the instantiated class. It is constructed in a // manner similar to the names used in the construction of overloaded functions char *templ_inst::instantiation_string() { char inst_name[max_string_size] ; for (int i = 0 ; i < max_string_size; i++) inst_name[i] = 0 ; mangled_name(inst_name) ; return strdup(inst_name) ; } // Change the names for the class, constructors, and destructors to reflect // the new class instantiation name. void classdef::modify_inst_names(char *s) { char *old = string ; string = s ; // Change the class name // 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 ; } // Get past the fake template argument name typename types Ptype non_template_arg_type(Pbase 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 ; case template_expr_tree_formal: case template_stmt_tree_formal: break ; default: error ('i', "bad template formal") ; } } // determine whether the expression supplied as an actual argument to // atemplate formal of type "template_expr_formal" is suitable. We are nore // restrictive than we need to be, simply so that the debugger can have an // easier time. All expressions must be of the form constant integer // expression, a float or double literal, or the address of a variable, or an // array, or function static int suitable_const_expr(Pname n) { if (n->n_evaluated) return 1 ; if (!n->n_initializer) return 0 ; switch (n->n_initializer->base) { case CAST: { // if it is a cast of an integer value, it's fine. if (n->n_initializer->e1->base == IVAL) return 1 ; else return 0 ; } case FCON: case ZERO: return 1 ; case ADDROF: case G_ADDROF: { Pname an = Pname(n->n_initializer->e2) ; if (an->base != NAME) return 0 ; if (an->n_stclass == STATIC) return 1 ; return 0 ; } case NAME: { Pname an = Pname(n->n_initializer) ; if ((an->n_stclass == STATIC) && (an->tp->base == VEC)) // assumes that decl processing won, so that the formal could only // have been apointer return 1 ; return 0 ; } default: return 0 ; } } static bool is_stmt_node(Pnode p) { if (!p) return false ; switch(p->base) { case BREAK: case CONTINUE: case DEFAULT: case SM: case WHILE: case DO: case SWITCH: case RETURN: case CASE: case FOR: case IF: case BLOCK: case PAIR: // ? return true ; } return false ; } static void bind_tree_expr_formal(Pname f, Pexpr actual) { if (actual->e1->base == TNAME) error ('i', "a TNAME is not a valid argument for the formal %n", f) ; if (is_stmt_node(actual->e1)) { error ("an expression node was expected for the expression formal parameter %n", f) ; actual->e1 = zero ; } // bind the formal to the expression, it will be accessed from here by // the hook function during the copy f->n_initializer = actual->e1 ; } // Bind the formals to the types passed in as the actuals, for the // instantiation, bind the non-type names to their expressions. void templ_inst::bind_formals() { 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: { formal->f->tp = non_template_arg_type(Pbase(actual->e1->tp)) ; PERM(formal->f->tp) ; break ; } case template_expr_formal: { actual->e1 = actual->e1->typ(gtbl); if (formal->f->tp->check(actual->e1->tp,ASSIGN) == 1) error("template argument mismatch, expected %t for formal %n, not %t", formal->f->tp, formal->f, actual->e1->tp) ; // 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 ; PERM(formal->f) ; non_template_arg_non_type(formal->f) ; if (!suitable_const_expr(formal->f)) error("template argument for formal:%s, is not a suitable constant.", formal->f->string) ; if (g) g->n_key = 0 ; break ; } case template_expr_tree_formal: { bind_tree_expr_formal(formal->f, actual) ; break ; } case template_stmt_tree_formal: { error("a statement tree may not be specified for a class template") ; break ; } default: error ('i', "bad template formal") ; } // now that the formals are bound, compute the instantiation string char *inst_name = 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 a template parameter multiple times") ; else formal->f->n_template_arg_string = strcat(strcpy(calloc(strlen(formal->f->string)+strlen(inst_name)+1,1), formal->f->string), inst_name) ; } // 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 templ_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 templ_inst::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 templ_inst::save_state(Pname p) { if (next_active) error ('i', "circular instantiation of a template") ; context.save() ; if (templ_inst::head) templ_inst::head->hide_parameter_names() ; next_active = templ_inst::head ; templ_inst::head = this ; context.init() ; Cdcl = p ; Cstmt = NULL ; curr_file = (Cdcl) ? Cdcl->where.file : 0; expose_parameter_names() ; } void templ_inst::restore_state() { context.restore() ; hide_parameter_names() ; templ_inst::head = next_active ; next_active = NULL ; if (templ_inst::head) templ_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', "cobj nodes were expected here") ; *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_template_class ; } // 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 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 ; } // hook to perform the copying of the pre-allocated class subtree bool templ_inst::copy_hook(Pnode &node) { 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 ; }else { // check for tree expression formals char *s = Pname(node)->string ; Pname f = 0 ; if (s && (*s == '$') && (f = 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) ; return false ; } } break ; case CLASS: if (node == class_node) return false ; if (node == Pbase(def->namep->tp)->b_name->tp) { *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 ; } 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) ; // +**** this doesn't work without class instances of new and delete // +**** dtor places this onto free_list: ugh // +**** want to restore this once new/delete isntances are restored // expr dummy(ELIST, 0, 0) ; // elist list(&dummy) ; Pexpr dummy = new expr(ELIST, 0, 0); elist list(dummy); // 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 templ_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 ; } 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) ; } /***************************************************************************** * * * 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) { bool forward = false ; // a forward class is instantiated twice // 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 && ktbl->look(tname->string, 0)) { // 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); { // dump the tree if the flag is set extern int dump_tree ; extern dcn_arg dump_tree_arg; if (dump_tree) display_cfront_node (dump_tree_arg, root); } } // 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) ; else class_node->defined &= ~(DEFINED|SIMPLIFIED) ; namep->tp = cobj_node; class_node->modify_inst_names(cname_node->string) ; 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 SM_PARAM: error ('i', "statement parameters not permitted for member functions") ; break ; 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 ; } } /* 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. */ Pname templ_inst::function_copy(Pfunt fnt, Pcons &templ_refs) { pointer_hash fcorr(*corr) ; // initialize it with the old hash table { tree_copy_info info ; Pnode root = fnt->fn ; // establish a correspondence between the formals used for the class // template, andthe 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', "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', "template to instantiation typename correspondence is missing") ; copy_tree (root, info, &fcorr); { // dump the tree if the flag is set extern int dump_tree ; extern dcn_arg dump_tree_arg; if (dump_tree) display_cfront_node (dump_tree_arg, root); } 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. * * * *****************************************************************************/ void templ_inst::instantiate_match(Ptempl_inst match) { Pbase pb = Pbase(match->tname->tp) ; kludge_copy(pb) ; forward = match ; // Note the fact that this template was matched } Pclass current_instantiation = 0 ; // Do the class declaration instantiation. void templ_inst::instantiate(bool reinstantiate) { Pcons templ_refs = def->templ_refs ; if (! reinstantiate) { switch (Ptclass(Pbase(tname->tp)->b_name->tp)->class_base) { case instantiated_template_class: return ; case uninstantiated_template_class: break ; case vanilla_class: case template_class: // the canonical template class default: error ('i', "attempt to instantiate a non-template class") ; } 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))){ instantiate_match(match) ; return ; } }else class_copy(templ_refs, true) ; Pbase pb = Pbase(tname->tp) ; // Save the state around decl processing { save_state(def->namep) ; if (def->open_instantiations++ > 1) { error ("an infinite instantiation sequence was initiated") ; def->open_instantiations-- ; return ; } // Mark the class as instantiated so that there are no circular // instantiations. Pclass(pb->b_name->tp)->class_base = instantiated_template_class ; // 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<X,Y> if (def->basep->b_name->tp->defined) { // 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) switch(formal->f->n_template_arg) { case template_expr_tree_formal: case template_stmt_tree_formal: break ; default: formal->f->dcl_print(0) ; } // Instantiate the parametrized types referenced by this template for (Pcons pc = templ_refs ; pc ; pc = pc->cdr) Ptempl_inst(pc->car)->instantiate() ; if (!((pb->b_name->dcl(gtbl, EXTERN) == 0 ) || error_count)) { pb->b_name->simpl() ; if (pb->b_name->tp->base != CLASS) error('i', "a classdef was expected in templ_inst::instantiate") ; current_instantiation = Pclass(pb->b_name->tp) ; pb->b_name->dcl_print(0) ; if (! (pb->b_name->tp->defined & DEFINED)) error ('i', "dcl class is not yet defined") ; current_instantiation = 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 { // resolve references to forward declarations if (this == def->get_match(clone->actuals, clone, true)) { clone->kludge_copy(Pbase(tname->tp)) ; clone->forward = this ; } } } Pclass result = Pclass(pb->b_name->tp); // 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; fn=Pclass(pb->b_name->tp)->memtbl->get_mem(++i)) if ((fn->base == NAME) && (fn->tp->base == FCT)) fn->dcl_print(0) ; restore_state() ; def->open_instantiations-- ; } } /* Template Constructors */ templ::templ(Plist parms, Pname p) { namep = p ; formals = parms ; if (!formals) error ("a parametrized type must have parameters !") ; basep = Pbase(namep->tp) ; Pclass(basep->b_name->tp)->class_base = template_class ; defined = ((basep->b_name->tp->defined & DEF_SEEN) ? true : false) ; if (defined) members = Pclass(basep->b_name->tp)->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. Theese objects are merely // place-holders during syntax analysis, and are actually filled in during // the copy phase of instantiation. Pclass c ; def = owner ; tname = new name(def->namep->string) ; tname->base = TNAME ; tname->tp = new basetype(COBJ, new name(def->namep->string)) ; Pbase(tname->tp)->b_name->tp = c = new templ_classdef(this) ; PERM(tname) ; PERM(tname->tp) ; PERM(Pbase(tname->tp)->b_name) ; PERM(Pbase(tname->tp)->b_name->tp) ; // initialize the member list so that set_scope can do the right thing c->mem_list = Pclass(def->basetype()->b_name->tp)->mem_list ; actuals = act ; next = owner->insts ; owner->insts = this ; } templ_classdef::templ_classdef(Ptempl_inst i): classdef(CLASS) { inst = i ; class_base = uninstantiated_template_class ; string = unparametrized_tname()->string ; } // Create a new function template. function_template::function_template (templ &owner, Plist params, Pname n) { definition_number = ++definition_tick ; 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) ; } // create the tree template tree_template::tree_template(TOK tree_kind, char *s, Plist params, Pnode tree, Pcons references) { string = s ; formals = params; e = tree ; if (!e) error('w', "the internal template %s does not have an associated body", s); templ_refs = references ; kind = tree_kind ; // add the expression template too the list of known templates if (get(s)) { error ("duplicate definition of the internal template %s", s) ; return ; } // link it in next = templ_compilation::tree_templates ; templ_compilation::tree_templates = this ; // check that formals weren't misspelt for (Plist formal = formals ; formal ; formal = formal->l) if (! formal->f->n_used) error('w', "the formal parameter %n was not referenced within the template body",formal->f) ; } // lookup an internal expression template Ptreet tree_template::get(char *s) { for (Ptreet o = templ_compilation::tree_templates ; o ; o = o->next) if (strcmp(o->string, s) == 0) return o ; return 0 ; } Pname tree_template::get_parameter(char *s) { for (Plist formal = formals ; formal ; formal = formal->l) { if (strcmp(formal->f->string, s)== 0) return formal->f ; } return 0 ; } // Sam: some remodularization seems to be in order here, so that this method // can be shared 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 ; } Pexpr tree_template::expand(Pexpr) { error('i', "no support for tree templates") ; return 0 ; } // Instantiate each function member 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. bool templ::instantiate_bodies(){ bool change = false ; // Perform the instantiation of the member function bodies. if (!fns) return change ; for (Ptempl_inst inst = insts ; inst ; inst = inst->next) if (!inst->forward && (inst->status == class_instantiated)) { // Set up the environment for the declaration, and subsequent compilation // of the function bodies. inst->status = body_instantiated ; change = true ; { Pclass ic = inst->get_class() ; int i ; // note all the overriding definitions explicitly provided by the user for (Pname fn= ic->memtbl->get_mem(i=1); fn; fn=ic->memtbl->get_mem(++i)) if ((fn->base == NAME) && (fn->tp->base == FCT) && Pfct(fn->tp)->body) fn->n_redefined = 1 ; } 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 the qualifier to be the name of the instantiated, rather than // the parametrized class name fn->n_qualifier = inst->namep ; if (fn->n_oper != TYPE ) // cond contains type information fn->n_table = 0; fn->n_tbl_list = 0 ; // Note that the formals were bound to the 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() ; // ensure that "type type" formals constrained to be class definitions are if (!fnt->check_constraints(inst->actuals))continue ; if ( ((fn = fn->dcl(gtbl, EXTERN)) == 0) || error_count) { inst->restore_state() ; continue ; } fn->simpl() ; fn->dcl_print(0) ; inst->restore_state() ; } } } return change ; }