/** * \file function_body.cpp * \brief Function body handling * \author Stefan Reuther * * This module provides logics for handling function bodies. It also * allows to queue up some bodies for later processing which is * required to support the rules for class members defined in-line. */ INTERFACE: #include "ptree_nodes.h" class Function_body_node; class Function_signature; class Block_scope; class Function_body_queue { Function_body_node *first, *last; }; IMPLEMENTATION: #include #include #include "function.h" #include "type_rep.h" #include "block.h" #include "annotator.h" #include "annotation.h" #include "class.h" #include "typedef.h" #include "init_handler.h" /************************** Function_body_queue **************************/ struct Function_body_node { Function_body_node* next; Function_signature* fsig; Block_scope* scope; Ptree* tree; Ptree* init; public: Function_body_node(Function_signature* fsig, Block_scope* scope, Ptree* tree, Ptree* init) : next(0), fsig(fsig), scope(scope), tree(tree), init(init) { } }; /** Create a function body queue. */ PUBLIC Function_body_queue::Function_body_queue() : first(0), last(0) { } /** Add function definition. \param fsig the function signature we're dealing with \param scope the relevant block scope (including parameter types) \param tree the function body \param init member initializer */ PUBLIC void Function_body_queue::add_function(Function_signature* fsig, Block_scope* scope, Ptree* tree, Ptree* init) { assert(fsig); assert(scope); assert(tree); assert(!init || fsig->get_function()->get_function_kind() == Symbol_name::k_Constructor); Function_body_node* p = new Function_body_node(fsig, scope, tree, init); if (first) last->next = p; else first = p; last = p; } PRIVATE Function_body_node* Function_body_queue::pop() { Function_body_node* rv = first; if (first) { first = first->next; if (!first) last = 0; } return rv; } /** Process all enqueued function bodies. */ PUBLIC void Function_body_queue::process() { while (Function_body_node* p = pop()) { process_function_body(p->fsig, p->scope, p->tree, p->init); delete p; } } /******************************* Functions *******************************/ /** Process an initializer list. \param fsig the function signature \param scope relevant scope \param tree input tree. Either null or [: init , init , ...] (whatever we get from the parser). \return list containing an initializer for every relevant element, in correct order. */ static Ptree* process_initializers(Function_signature* fsig, Block_scope* scope, Ptree* tree) { /* cut off the ":" */ if (tree) { expect_ptree(tree->Car(), ':'); tree = tree->Cdr(); } /* the scopes we work in */ Abstract_scope* class_scope = fsig->get_function()->get_declared_scope(); Class_symbol* class_sym = downcast(fsig->get_this_type().get_type_symbol()); /* what we do: we parse the initializer into an array. Then, we gather them up in the order in which they'll be executed and null them out. If some remain, we have a bug. */ std::vector > inits; for (Ptree* p = tree; p; (p = p->Cdr()) && (p = p->Cdr())) { Ptree* ele = p->Car(); expect_ptree(ele->Second(), '('); expect_ptree(ele->Nth(3), ')'); // FIXME: 12.6.2p2 says first look into class_scope, then // the scope in which the ctor is defined Symbol_pair sp = Symbol_name(ele->First(), class_scope, false).lookup_for_use(false); Symbol* the_sym = 0; if (Class_symbol* csym = dynamic_cast(sp.tag)) the_sym = csym; else if (Variable_symbol* vsym = dynamic_cast(sp.untag)) the_sym = vsym; else if (Typedef_symbol* tdsym = dynamic_cast(sp.untag)) { Type t = tdsym->get_type(); if (t.is_class_type()) the_sym = t.get_type_symbol(); } if (!the_sym) compile_error("don't know what to do with name `" + std::string(ele->First()->ToString()) + "` in an initializer"); inits.push_back(std::make_pair(the_sym, ele->Cdr())); } /* Now we have all the initializers. What do we want to initialize? */ std::vector want; for (Class_symbol::bases_t::const_iterator i = class_sym->vbc_begin(); i != class_sym->vbc_end(); ++i) want.push_back(*i); for (Class_symbol::bases_t::const_iterator i = class_sym->base_begin(); i != class_sym->base_end(); ++i) want.push_back(*i); for (Class_symbol::members_t::const_iterator i = class_sym->mem_begin(); i != class_sym->mem_end(); ++i) if ((*i)->is_member_variable()) want.push_back(*i); /* Let's boogie. */ Ptree* output_list = 0; unsigned last_used = 0; for (unsigned i = 0; i < want.size(); ++i) { /* first, look up the corresponding initializer and strike it out. */ Ptree* the_init = 0; for (unsigned j = 0; j < inits.size(); ++j) { if (inits[j].first == want[i]) { if (the_init) { compile_warning("multiple initializers for `" + want[i]->get_name() + "'", the_init); } else { if (last_used > j) compile_warning("initializers will be re-ordered to match initialisation rules", the_init); the_init = inits[j].second; last_used = j; } inits[j].first = 0; } } /* do it. */ Ptree *output_init, *output_name; if (Class_symbol* csym = dynamic_cast(want[i])) { output_init = Init_handler(scope, the_init, false).process_initializer(csym->get_type()); output_name = make_name(csym->get_name(), csym->get_type(), csym); } else if (Variable_symbol* vsym = dynamic_cast(want[i])) { output_init = Init_handler(scope, the_init, false).process_initializer(vsym->get_type()); if (!output_init) compile_warning("member `" + vsym->get_name() + "' not initialized", tree); output_name = make_name(vsym); } else { assert(!"game over"); } /* build output tree */ output_list = Ptree::Snoc(output_list, Ptree::List(output_name, make_static_leaf("="), output_init)); } /* everything done? */ for (unsigned i = 0; i < inits.size(); ++i) if (inits[i].first) compile_error("unexpected initializer for `" + inits[i].first->get_name() + "'"); return output_list; } /** Process function body. \param fsig function signature \param scope scope containing the parameters \param tree body \param init member initializer tree, for constructors only. */ void process_function_body(Function_signature* fsig, Block_scope* scope, Ptree* tree, Ptree* init) { if (fsig->get_storage_specifier() == s_Member) scope->set_this_type(fsig->get_this_type()); else scope->set_this_type(Type()); // maybe it's a constructor if (fsig->get_function()->get_function_kind() == Symbol_name::k_Constructor) init = process_initializers(fsig, scope, init); else assert(!init); // this assumes that the tree is a PtreeBlock or a simple statement Annotator anno(&Source::instance(), scope); anno.visit_stmt_or_block(tree); fsig->set_body(anno.get_output(), init); }