/** * \file template.cpp */ INTERFACE: #include #include #include #include "symbol_table.h" #include "class.h" class Template_thing { std::vector arg_names; Ptree* tree; }; class Template_spec; class Template_class_symbol : public Symbol { Template_thing* definition; // a class definition that can be // handed to parse_class std::vector members; std::vector special; Abstract_scope* defined_in_scope; }; IMPLEMENTATION: #include #include "except.h" #include "typedef.h" #include "paranoid_visitor.h" #include "annotator.h" #include "symbol_name.h" #include "decl_read.h" /***************************** Template_thing ****************************/ PUBLIC Template_thing::Template_thing() : tree(0) { } PUBLIC void Template_thing::add_args_from_ptree(Ptree* list, Abstract_scope* scope) { for (Ptree* p = list; p != 0; (p = p->Cdr()) && (p = p->Cdr())) { Ptree* ele = p->Car(); if (ele->Car()->IsLeaf() && (ele->Car()->Eq("class") || ele->Car()->Eq("typename"))) { /* [class NAME] */ assert(ele->Second()); assert(ele->Second()->IsLeaf()); add_type_arg(ele->Second()->ToString()); } else { compile_error("invalid template argument"); } } } PUBLIC void Template_thing::add_type_arg(std::string name) { arg_names.push_back(name); } PUBLIC int Template_thing::get_num_args() const { return arg_names.size(); } PUBLIC std::string Template_thing::get_type_arg(int i) const { assert(i >= 0 && i < arg_names.size()); return arg_names[i]; } PUBLIC void Template_thing::set_tree(Ptree* tree) { this->tree = tree; } PUBLIC Ptree* Template_thing::get_tree() const { return tree; } /***************************** Template_spec *****************************/ class Template_spec { Type_vector args; Class_symbol* defn; Ptree* code; }; PUBLIC Template_spec::Template_spec(Type_vector args, Class_symbol* defn) : args(args), defn(defn), code(0) { } PUBLIC Template_spec::~Template_spec() { } PUBLIC void Template_spec::add_code(Ptree* tree) { if (!tree) return; assert(!tree->IsLeaf()); code = Ptree::Append(code, tree); } PUBLIC bool Template_spec::matches(const Type_vector& rhs) const { if (args.size() != rhs.size()) return false; for (Type_vector::size_type i = 0; i < args.size(); ++i) if (rhs[i] != args[i]) return false; return true; } PUBLIC inline Class_symbol* Template_spec::get_class() const { return defn; } PUBLIC inline Ptree* Template_spec::get_code() const { return code; } /************************** Template_defn_scope **************************/ /** Dummy scope for template definitions. This is just a dummy class which is never directly handed to Annotator; hence we can get away with this many dummy functions. Its only raison d'etre is that template args can be found. */ class Template_defn_scope : public Abstract_scope { std::map things; }; PUBLIC Template_defn_scope::Template_defn_scope(Abstract_scope* parent, const Template_thing* arg_names, const Type_vector* arg_types, std::string class_name, std::string tpl_name) : Abstract_scope(parent) { assert(arg_names); assert(arg_types); assert(arg_types->size() == arg_names->get_num_args()); for (unsigned i = 0; i < arg_types->size(); ++i) things[arg_names->get_type_arg(i)] = new Typedef_symbol((*arg_types)[i]); } PUBLIC Template_defn_scope::~Template_defn_scope() { } PUBLIC Symbol_pair Template_defn_scope::lookup_here(std::string name, bool for_decl) { Typedef_symbol* s = things[name]; if (s && s->get_type().is_class_type()) return Symbol_pair(s, s->get_type().get_type_symbol()); else return Symbol_pair(s, 0); } PUBLIC Variable_symbol* Template_defn_scope::add_variable(Storage_class_specifier s, Type t, Ptree* n, Ptree* i, Ptree* b) { assert(!"add_variable"); } PUBLIC Function_signature* Template_defn_scope::add_function_decl(Storage_class_specifier s, Function_specifier_set f, Type t, const Symbol_name& n) { return get_parent()->add_function_decl(s, f, t, n); } PUBLIC void Template_defn_scope::add_function_implementation(Function_signature* sig, Block_scope* sco, Ptree* b, Ptree* i) { get_parent()->add_function_implementation(sig, sco, b, i); } PUBLIC void Template_defn_scope::add_symbol(std::string name, Symbol* s) { assert(!"add_symbol"); } PUBLIC std::string Template_defn_scope::get_unique_name(std::string name) { assert(!"get_unique_name"); } PUBLIC Type Template_defn_scope::get_this_type() const { assert(!"get_this_type"); } /************************** Template_class_adder *************************/ class Template_class_adder : public Class_adder { std::string mangled_name; std::string real_name; }; PUBLIC Template_class_adder::Template_class_adder(std::string mangled_name, std::string real_name) : mangled_name(mangled_name), real_name(real_name) { } PUBLIC Class_symbol* Template_class_adder::add_class(Abstract_scope* scope, std::string name, Symbol::Kind k) { assert(dynamic_cast(scope)); Class_symbol* csym = new Class_symbol(k, scope, real_name); csym->set_status(Symbol::st_Declared); scope->get_parent()->add_symbol(mangled_name, csym); return csym; } /************************* Template_class_symbol *************************/ PUBLIC Template_class_symbol::Template_class_symbol(Abstract_scope* scope) : definition(0), defined_in_scope(scope) { set_status(st_Declared); } PUBLIC Template_class_symbol::~Template_class_symbol() { } PUBLIC Symbol::Kind Template_class_symbol::get_kind() const { return k_ClassTemplate; } PUBLIC void Template_class_symbol::set_definition(Template_thing* def) { assert(!is_defined()); definition = def; set_status(st_Defined); } PUBLIC void Template_class_symbol::add_member(Template_thing* def) { members.push_back(def); } PUBLIC Class_symbol* Template_class_symbol::get_specialisation(const Type_vector& types) { for (unsigned i = 0; i < special.size(); ++i) if (special[i]->matches(types)) return special[i]->get_class(); if (!definition) compile_error("Requesting specialisation of undefined template class"); std::string mangle = Symbol_name::get_mangled_template_name(get_basename(), types); /* We have to make a new specialisation. Let's boogie. */ Template_defn_scope* tds = new Template_defn_scope(defined_in_scope, definition, &types, get_basename(), mangle); Template_class_adder adder(mangle, get_basename()); Class_symbol* csym = parse_class(definition->get_tree(), tds, 0, false, adder); Template_spec* spec = new Template_spec(types, csym); special.push_back(spec); // std::cout << " ... done class\n"; /* process all members we have so far */ Ptree* code = 0; for (unsigned i = 0; i < members.size(); ++i) { tds = new Template_defn_scope(defined_in_scope, members[i], &types, get_basename(), mangle); Annotator anno(&Source::instance(), tds); anno.visit_and_catch(members[i]->get_tree()); spec->add_code(anno.get_output()); } return csym; } PUBLIC Class_symbol* Template_class_symbol::get_specialisation_from_ptree(Ptree* tree, Abstract_scope* sco) { Type_vector tv; parse_template_arg_list(tree, sco, &tv); return get_specialisation(tv); } /**************************** Template_visitor ***************************/ typedef int Tpl_RT; class Template_visitor : public Paranoid_visitor { Abstract_scope* current_scope; Template_thing args; }; PUBLIC Template_visitor::Template_visitor(Source* s, Abstract_scope* scope, const Template_thing& args) : Paranoid_visitor(s), Ptree_visitor(s), current_scope(scope), args(args) { } PUBLIC Tpl_RT Template_visitor::visit_type_declaration(Ptree* storagespec, Ptree* typespec) { if (storagespec && (storagespec->Eq("friend") || storagespec->Car()->Eq("friend"))) { // FIXME? I think we can ignore friends because we don't do protection return 0; } if (storagespec) bogus_ptree_error("type declarations may not have storage specifiers", storagespec); if (!typespec) { compile_warning("neat idea: template `;'", storagespec); } else { /* "template class X */ declare_class(typespec); } return 0; } /** Name declaration. \param storagespec list of static/mutable/friend/virtual/inline/extern \param typespec list of type specifiers and cv-qualifiers, or type definition \param decllist list of declarators, separated by comma */ PUBLIC Tpl_RT Template_visitor::visit_name_declaration(Ptree* storagespec, Ptree* typespec, Ptree* decllist) { /* "template<...> friend" */ if (storagespec && (storagespec->Eq("friend") || storagespec->Car()->Eq("friend"))) { // FIXME? I think we can ignore friends because we don't do protection return 0; } // HACK around OpenC++ limitations: HACK_EXPLICIT(typespec, storagespec); /* template<...> TYPE DECLARATOR... */ for (Ptree* p = decllist; p != 0; (p = p->Cdr()) && (p = p->Cdr())) { Ptree* decl = p->Car(); Ptree* declared_name = get_name_from_declarator(decl, current_scope); if (!declared_name) compile_error("unnamed template?"); declare_name(declared_name, new PtreeDeclaration(storagespec, Ptree::List(typespec, decl))); } return 0; } PUBLIC Tpl_RT Template_visitor::visit_function(Ptree* storagespec, Ptree* typespec, PtreeDeclarator* decl, PtreeBlock* block) { HACK_EXPLICIT(typespec, storagespec); /* template<...> TYPE DECLARATOR BLOCK */ Ptree* declared_name = get_name_from_declarator(decl, current_scope); if (!declared_name) compile_error("unnamed template?"); // FIXME? Michael dissects it and I build it again declare_name(declared_name, new PtreeDeclaration(storagespec, Ptree::List(typespec, decl, block))); return 0; } PRIVATE void Template_visitor::declare_name(Ptree* p, Ptree* tree) { /* Figure out which template this goes into */ /* FIXME? Code duplication from Symbol_name */ if (!Symbol_name::is_qualified_name(p)) { // "template void foo()" compile_error("unqualified name in template definition?"); } bool look_global = true; Abstract_scope* scope = current_scope; if (p->Car()->Eq("::")) { scope = scope->get_global_scope(); look_global = false; p = p->Cdr(); } while (p && p->Cdr()) { Ptree* ele = p->Car(); if (ele->IsLeaf()) { /* class or namespace name */ if (look_global) { Abstract_scope* s; while (scope && !(s = Symbol_name::lookup_symbol_in_scope(ele, scope, scope))) scope = scope->get_parent(); if (!scope) compile_error("no such scope " + std::string(ele->ToString())); scope = s; } else { scope = Symbol_name::lookup_symbol_in_scope(ele, scope, scope); if (!scope) compile_error("no such scope " + std::string(ele->ToString())); } look_global = false; p = p->Cdr(); expect_ptree(p->Car(), "::"); p = p->Cdr(); } else { /* template name */ std::string tpl_name = ele->First()->ToString(); Template_class_symbol* tcs; Symbol_pair sp; if (look_global) sp = scope->lookup_unqualified(tpl_name); else sp = scope->lookup_here(tpl_name, false); if (!sp || !(tcs = dynamic_cast(sp.tag))) compile_error("no such template " + tpl_name); /* we know that it's a template. Add it to the class. */ Template_thing* thing = new Template_thing(args); thing->set_tree(tree); tcs->add_member(thing); break; } } if (!p) // happens for template functions compile_error("template without a template expression in name"); } PRIVATE void Template_visitor::declare_class(Ptree* class_tree) { Ptree* name = class_tree->Second(); if (!name || (!name->IsLeaf() && !name->First())) compile_error("nameless template not allowed"); Symbol_name sym_name(name, current_scope, false); Symbol_pair p = (sym_name.is_qualified() ? sym_name.lookup_for_decl() : current_scope->lookup_here(sym_name.get_name(), true)); Template_class_symbol* tcs; if (sym_name.is_qualified()) compile_error("template specialisation not supported"); if (!p) { if (sym_name.is_qualified()) compile_error("can't define scoped name"); tcs = new Template_class_symbol(current_scope); current_scope->add_symbol(sym_name.get_name(), tcs); } else { tcs = dynamic_cast(p.tag); if (!tcs) compile_error(sym_name.get_name() + " is not a class template"); } if (class_tree->Length() > 2) { if (tcs->is_defined()) compile_error(tcs->get_name() + " already has a definition"); Template_thing* tt = new Template_thing(args); tt->set_tree(class_tree); tcs->set_definition(tt); } } void visit_template(Ptree* arglist, Ptree* content, Abstract_scope* scope) { Template_thing tt; tt.add_args_from_ptree(arglist, scope); Template_visitor tvis(&Source::instance(), scope, tt); tvis.visit(content); } void parse_template_arg_list(Ptree* arglist, Abstract_scope* scope, Type_vector* output) { expect_ptree(arglist->Car(), '<'); expect_ptree(arglist->Third(), '>'); for (Ptree* p = arglist->Second(); p != 0; (p = p->Cdr()) && (p = p->Cdr())) { Ptree* typ = p->Car(); Type t = parse_type(typ->Car(), scope, 0, false); if (!parse_abstract_declarator(typ->Second(), scope, &t)) compile_error("invalid template parameter"); output->push_back(t); } }