INTERFACE: #include #include "type_rep.h" #include "symbol_table.h" #include "symbol_name.h" #include "scope.h" class Variable_symbol; class Function_symbol; class Class_symbol; class Class_scope : public Abstract_scope { Class_symbol* sym; std::string prefix; public: }; ///! Symbol for a class or union type. class Class_symbol : public Type_symbol { public: typedef std::vector bases_t; typedef std::vector members_t; typedef std::vector memfuns_t; private: Kind k; Abstract_scope* in_scope; std::string real_name; /* Base classes in left-to-right order */ bases_t base_classes, virtual_base_classes; members_t members; memfuns_t member_functions; bool pod, aggregate; friend class Class_lookup_helper; friend class Class_scope; enum Assignment_operator { no_assignment_operator, const_assignment_operator, nonconst_assignment_operator }; }; class Class_lookup_helper { protected: typedef std::vector classes_t; classes_t result_set; classes_t hidden_vbcs; virtual bool predicate(Class_symbol* sym) = 0; }; class Class_adder { public: virtual Class_symbol* add_class(Abstract_scope* scope, std::string name, Symbol::Kind k) = 0; virtual ~Class_adder() { } }; class Default_class_adder : public Class_adder { }; IMPLEMENTATION: #include #include "downcast.h" #include "token.h" #include "except.h" #include "variable.h" #include "annotator.h" #include "function.h" #include "typedef.h" #include "init_handler.h" using namespace Opencxx; Class_symbol* Default_class_adder::add_class(Abstract_scope* scope, std::string name, Symbol::Kind k) { Class_symbol* sym = new Class_symbol(k, scope, name); sym->set_status(Symbol::st_Declared); scope->add_symbol(name, sym); return sym; } /****************************** Class_symbol *****************************/ PUBLIC Class_symbol::Class_symbol(Kind k, Abstract_scope* in_scope, std::string real_name) : k(k), in_scope(in_scope), real_name(real_name), pod(true), aggregate(true) { } PUBLIC Class_symbol::~Class_symbol() { } PUBLIC Symbol::Kind Class_symbol::get_kind() const { return k; } PUBLIC inline bool Class_symbol::is_pod() const { return pod; } PUBLIC inline bool Class_symbol::is_aggregate() const { return aggregate; } PUBLIC inline const std::string& Class_symbol::get_real_name() const { return real_name; } PUBLIC bool Class_symbol::is_base_class_of(Class_symbol* other) const { bases_t::iterator i; for (i = other->base_classes.begin(); i != other->base_classes.end(); ++i) if (*i == this || is_base_class_of(*i)) return true; for (i = other->virtual_base_classes.begin(); i != other->virtual_base_classes.end(); ++i) if (*i == this || is_base_class_of(*i)) return true; return false; } PUBLIC bool Class_symbol::is_unique_base_class_of(Class_symbol* other) const { // FIXME return is_base_class_of(other); } PUBLIC void Class_symbol::enumerate_base_classes(bool with_virtuals, bases_t* output) { bases_t::iterator i; for (i = base_classes.begin(); i != base_classes.end(); ++i) { output->push_back(*i); (*i)->enumerate_base_classes(false, output); } if (with_virtuals) { for (i = virtual_base_classes.begin(); i != virtual_base_classes.end(); ++i) { output->push_back(*i); (*i)->enumerate_base_classes(false, output); } } } /** Add virtual base class, if it is not already there. */ PRIVATE void Class_symbol::maybe_add_vbc(Class_symbol* sym) { if (std::find(base_classes.begin(), base_classes.end(), sym) != base_classes.end()) compile_error("`" + sym->get_name() + "' is already a nonvirtual base class"); if (std::find(virtual_base_classes.begin(), virtual_base_classes.end(), sym) == virtual_base_classes.end()) { virtual_base_classes.push_back(sym); for (bases_t::iterator i = sym->virtual_base_classes.begin(); i != sym->virtual_base_classes.end(); ++i) maybe_add_vbc(*i); } } /** Add base class. This will also check conditions (class must be defined, class must not already be a base class, ...). FIXME: This does not reject ': virtual A, virtual A'. */ PUBLIC void Class_symbol::add_base_class(Class_symbol* sym, bool is_virt) { if (!sym || !sym->is_defined()) compile_error("base class `" + sym->get_name() + "' is not defined"); if (sym->get_kind() != k_ClassOrStruct) compile_error("base class must be class or struct"); if (is_virt) { maybe_add_vbc(sym); } else { if (std::find(virtual_base_classes.begin(), virtual_base_classes.end(), sym) != virtual_base_classes.end()) compile_error("`" + sym->get_name() + "' is already a virtual base class"); if (std::find(base_classes.begin(), base_classes.end(), sym) != base_classes.end()) compile_error("`" + sym->get_name() + "' is already a nonvirtual base class"); base_classes.push_back(sym); for (bases_t::iterator i = sym->virtual_base_classes.begin(); i != sym->virtual_base_classes.end(); ++i) maybe_add_vbc(*i); } pod = aggregate = false; } PUBLIC void Class_symbol::dump(std::ostream& os) { Type_symbol::dump(os); if (!base_classes.empty()) { os << "\n + base classes:"; for (unsigned i = 0; i < base_classes.size(); ++i) os << " " << base_classes[i]->get_name(); } if (!virtual_base_classes.empty()) { os << "\n + virtual base classes:"; for (unsigned i = 0; i < virtual_base_classes.size(); ++i) os << " " << virtual_base_classes[i]->get_name(); } } PUBLIC inline Symbol_pair Class_symbol::lookup_helper(std::string name) const { // return Symbol_table::get_instance().get_symbol(get_name() + "::" + name); return Symbol_table::get_instance(). get_symbol(Symbol_name::get_mangled_symbol_name(Symbol_name::get_mangled_scope_from_symbol(get_name()), name)); } PUBLIC Class_scope* Class_symbol::get_scope() { return new Class_scope(this); } PUBLIC inline Class_symbol::members_t::const_iterator Class_symbol::mem_begin() const { return members.begin(); } PUBLIC inline Class_symbol::members_t::const_iterator Class_symbol::mem_end() const { return members.end(); } PUBLIC inline Class_symbol::bases_t::const_iterator Class_symbol::vbc_begin() const { return virtual_base_classes.begin(); } PUBLIC inline Class_symbol::bases_t::const_iterator Class_symbol::vbc_end() const { return virtual_base_classes.end(); } PUBLIC inline Class_symbol::bases_t::const_iterator Class_symbol::base_begin() const { return base_classes.begin(); } PUBLIC inline Class_symbol::bases_t::const_iterator Class_symbol::base_end() const { return base_classes.end(); } /** Start class definition. */ PUBLIC void Class_symbol::start_definition(std::string basename) { if (basename.length()) { Class_scope scope(this); Typedef_symbol* tdsym = new Typedef_symbol(get_type()); scope.add_symbol(basename, tdsym); Symbol_table::get_instance().set_peer(tdsym, this); } } /** Finish class definition. */ PUBLIC void Class_symbol::finish_definition() { /* aggregate? */ Function_symbol* fsym = dynamic_cast(lookup_helper(Symbol_name::CONSTRUCTOR_NAME).untag); if (fsym) { pod = aggregate = false; } else { /* no default constructor */ Class_scope* cs = get_scope(); fsym = new Function_symbol(cs, Symbol_name::k_Constructor); cs->add_symbol(Symbol_name::CONSTRUCTOR_NAME, fsym); } /* add copy constructor */ if (!get_copy_ctor(fsym, true)) { Type t = get_type(); if (implicit_copy_ctor_is_const()) t.add_qualifier(Type::q_Const); Function_signature* fsig = fsym->add_signature(make_unary_function_type(t.make_reference_type(), ctor_type), get_type(), s_Member, f_Inline, Function_symbol::must_be_new); fsig->set_generated(); } /* assignment operator */ fsym = dynamic_cast(lookup_helper(Symbol_name::ASSIGNMENT_OPERATOR_NAME).untag); if (!get_copy_ctor(fsym, false)) { Assignment_operator aso = implicit_assignment_operator_style(); if (aso != no_assignment_operator) { if (!fsym) { Class_scope* cs = get_scope(); fsym = new Function_symbol(cs, Symbol_name::k_Operator); cs->add_symbol(Symbol_name::ASSIGNMENT_OPERATOR_NAME, fsym); } Type t = get_type(); if (aso == const_assignment_operator) t.add_qualifier(Type::q_Const); Function_signature* fsig = fsym->add_signature(make_unary_function_type(t.make_reference_type(), get_type().make_reference_type()), get_type(), s_Member, f_Inline, Function_symbol::must_be_new); fsig->set_generated(); } } /* fill in member function names */ for (memfuns_t::iterator i = member_functions.begin(); i != member_functions.end(); ++i) (*i)->fill_in_mangled_names(true); } /** Given a function signature, return the copy constructor or assignment operator (i.e. the signature which takes a "cv-T&" as parameter). */ PRIVATE Function_signature* Class_symbol::get_copy_ctor(Function_symbol* fsym, bool must_be_ref) const { if (!fsym) return 0; for (Function_symbol::Sig_it i = fsym->sig_begin(); i != fsym->sig_end(); ++i) { Type t = (*i)->get_proto_type(); if (t.get_num_function_args() == 1 && (!must_be_ref || t.get_function_arg(0).get_kind() == Type::k_Reference) && t.get_function_arg(0).sans_reference().is_same_unqualified_type(get_type())) return *i; } return 0; } PRIVATE bool Class_symbol::implicit_copy_ctor_is_const() const { /* The question we're asking is: does our copy ctor take a "const" arg or not? */ for (bases_t::const_iterator i = base_classes.begin(); i != base_classes.end(); ++i) if (Function_signature* fsig = (*i)->get_copy_ctor(dynamic_cast((*i)->lookup_helper(Symbol_name::CONSTRUCTOR_NAME).untag), true)) if (!fsig->get_proto_type().get_function_arg(0).get_basis_type().is_qualified(Type::q_Const)) return false; for (bases_t::const_iterator i = virtual_base_classes.begin(); i != virtual_base_classes.end(); ++i) if (Function_signature* fsig = (*i)->get_copy_ctor(dynamic_cast((*i)->lookup_helper(Symbol_name::CONSTRUCTOR_NAME).untag), true)) if (!fsig->get_proto_type().get_function_arg(0).get_basis_type().is_qualified(Type::q_Const)) return false; for (members_t::const_iterator i = mem_begin(); i != mem_end(); ++i) { if ((*i)->is_member_variable()) { Type t = (*i)->get_type().sans_array(); if (t.is_class_type()) { Class_symbol* csym = downcast(t.get_type_symbol()); if (Function_signature* fsig = csym->get_copy_ctor(dynamic_cast(csym->lookup_helper(Symbol_name::CONSTRUCTOR_NAME).untag), true)) if (!fsig->get_proto_type().get_function_arg(0).get_basis_type().is_qualified(Type::q_Const)) return false; } } } return true; } PRIVATE Class_symbol::Assignment_operator Class_symbol::implicit_assignment_operator_style() const { /* if one of our members or bases has no assignment operator, we don't have one either. If one of our members or bases has a nonconst assignment operator, ours is nonconst, too. If we have reference or const members, we can't have an assignment operator. */ Assignment_operator rv = const_assignment_operator; for (bases_t::const_iterator i = base_classes.begin(); i != base_classes.end(); ++i) { if (Function_signature* fsig = (*i)->get_copy_ctor(dynamic_cast((*i)->lookup_helper(Symbol_name::ASSIGNMENT_OPERATOR_NAME).untag), false)) { if (!fsig->get_proto_type().get_function_arg(0).get_basis_type().is_qualified(Type::q_Const)) rv = nonconst_assignment_operator; } else { return no_assignment_operator; } } for (bases_t::const_iterator i = virtual_base_classes.begin(); i != virtual_base_classes.end(); ++i) { if (Function_signature* fsig = (*i)->get_copy_ctor(dynamic_cast((*i)->lookup_helper(Symbol_name::ASSIGNMENT_OPERATOR_NAME).untag), false)) { if (!fsig->get_proto_type().get_function_arg(0).get_basis_type().is_qualified(Type::q_Const)) rv = nonconst_assignment_operator; } else { return no_assignment_operator; } } for (members_t::const_iterator i = mem_begin(); i != mem_end(); ++i) { if ((*i)->is_member_variable()) { Type t = (*i)->get_type().sans_array(); if (t.is_qualified(Type::q_Const) || t.get_kind() == Type::k_Reference) return no_assignment_operator; if (t.is_class_type()) { Class_symbol* csym = downcast(t.get_type_symbol()); if (Function_signature* fsig = csym->get_copy_ctor(dynamic_cast(csym->lookup_helper(Symbol_name::ASSIGNMENT_OPERATOR_NAME).untag), false)) { if (!fsig->get_proto_type().get_function_arg(0).get_basis_type().is_qualified(Type::q_Const)) rv = nonconst_assignment_operator; } else { return no_assignment_operator; } } } } return rv; } /************************************************************************/ static void process_base_classes(Class_symbol* sym, Ptree* tree, Abstract_scope* scope) { expect_ptree(tree->First(), ':'); for (Ptree* p = tree->Cdr(); p != 0; (p = p->Cdr()) && (p = p->Cdr())) { Ptree* base = p->Car(); assert(!base->IsLeaf()); /* parse it */ bool is_virt = false; while (1) { switch (base->Car()->What()) { case VIRTUAL: is_virt = true; break; case PUBLIC: case PRIVATE: case PROTECTED: break; default: goto out; } base = base->Cdr(); assert(base); } out: assert(!base->Cdr()); Symbol_pair p = Symbol_name(base->Car(), scope, false).lookup_for_use(false); if (!p.tag || p.tag->get_kind() != Symbol::k_ClassOrStruct) compile_error("invalid base class specified"); sym->add_base_class(downcast(p.tag), is_virt); } } /* content is: [kind name] [kind name bases content] [kind [nil nil] content] (!) */ Class_symbol* parse_class(Ptree* tree, Abstract_scope* scope, Ptree* name_for_anon, bool is_type_declaration, Class_adder& adder) { /* Figure out kind */ Ptree* rw = tree->First(); Symbol::Kind k; if (rw->Eq("class") || rw->Eq("struct")) k = Symbol::k_ClassOrStruct; else if (rw->Eq("union")) k = Symbol::k_Union; else bogus_ptree_error("expected struct/class/union", tree->First()); if (tree->Length() == 2) { /* Declaration / reference to existing */ Ptree* name = tree->Second(); if (!name) bogus_ptree_error("stray struct/class/union in program", tree); Symbol_name sym_name(name, scope, false); Symbol_pair p = sym_name.lookup_for_decl(); if (p.tag) { /* already defined. */ if (p.tag->get_kind() != k) compile_error("symbol `" + std::string(name->ToString()) + "' already is a different kind of symbol"); Class_symbol* csym = dynamic_cast(p.tag); if (!csym) compile_error("symbol `" + std::string(name->ToString()) + "' is not a class"); if (!csym->is_declared()) csym->set_status(Symbol::st_Declared); return csym; } else { /* not defined yet. Declare it. If this is a type declaration, declare it here. Otherwise, declare it in smallest enclosing non-class, non-prototype scope. */ if (sym_name.is_qualified()) compile_error("can't declare scoped identifier"); if (sym_name.is_template()) compile_error("reference to undefined template"); if (!is_type_declaration) { // prototype scope should also be handled here, but we // don't do prototype scope. while (scope && dynamic_cast(scope)) scope = scope->get_parent(); // there's always a namespace scope outside assert(scope); } return adder.add_class(scope, sym_name.get_name(), k); } } /* When we're here, it is a class definition */ Class_symbol* csym; Ptree* name = tree->Second(); Ptree* body; Ptree* bases; /* bummer. Named objects are "[key name base defn]", unnamed ones are "[key [nil nil] defn]". */ if (name && !name->IsLeaf() && !name->Car()) { name = bases = 0; body = tree->Third(); } else { bases = tree->Third(); body = tree->Nth(3); } if (!name) name = name_for_anon; Symbol_name sym_name(name, scope, false); Symbol_pair p = sym_name.lookup_for_decl(); if (p.tag) { if (p.tag->get_kind() != k) compile_error("symbol `" + std::string(name->ToString()) + "' already is a different kind of symbol"); csym = downcast(p.tag); } else { if (sym_name.is_qualified()) compile_error("can't declare scoped identifier"); if (sym_name.is_template()) compile_error("definition of template specialisation not supported"); csym = adder.add_class(scope, sym_name.get_name(), k); } if (csym->is_defined()) compile_error("We already have a perfectly good definition for `" + csym->get_name() + "'"); if (!csym->is_declared()) csym->set_status(Symbol::st_Declared); /* read definition... */ if (bases) process_base_classes(csym, bases, scope); if (body) { /* [{ [a b c d] }] */ csym->start_definition(sym_name.get_name()); expect_ptree(body->First(), '{'); expect_ptree(body->Third(), '}'); Annotator ann(&Source::instance(), csym->get_scope()); for (Ptree* p = body->Second(); p; p = p->Cdr()) ann.visit_and_catch(p->Car()); csym->finish_definition(); } /* finish it up */ csym->set_status(Symbol::st_Defined); return csym; } /************************** Class_lookup_helper **************************/ /** Helper class to implement lookup of class members. 10.2: First, every declaration for the name in the class and in each of its base class sub-objects is considered. A member name f in one sub-object B hides a member name f in a sub-object A if A is a base class sub-object of B. Any declarations that are so hidden are eliminated from consideration. Each of these declarations that was introduced by a using-declaration is considered to be from each sub-object of C that is of the type containing the declara-tion designated by the using-declaration.96) If the resulting set of declarations are not all from sub-objects of the same type, or the set has a nonstatic member and includes members from distinct sub-objects, there is an ambiguity and the program is ill-formed. Otherwise that set is the result of the lookup. FIXME: g++ accepts the following: struct A { struct stat {}; }; struct B { static void stat(); }; struct C : A, B { }; struct C::stat x; and rejects void x() { C::stat(); } We currently reject both. */ PROTECTED Class_lookup_helper::Class_lookup_helper() { } PUBLIC virtual Class_lookup_helper::~Class_lookup_helper() { } /** Add a base class subobject to the result set. */ PUBLIC void Class_lookup_helper::add_class(Class_symbol* sym) { typedef Class_symbol::bases_t::iterator iter_t; if (predicate(sym)) { /* this class defines the symbol. All its base classes are hidden. Hence, we need not process the non-virtual base class sub-objects. Add the VBCs to the set of hidden base classes. */ result_set.push_back(sym); for (iter_t i = sym->virtual_base_classes.begin(); i != sym->virtual_base_classes.end(); ++i) if (find(hidden_vbcs.begin(), hidden_vbcs.end(), *i) == hidden_vbcs.end()) hidden_vbcs.push_back(*i); } else { /* symbol not defined here. Maybe in one of the bases? */ for (iter_t i = sym->base_classes.begin(); i != sym->base_classes.end(); ++i) add_class(*i); /* VBCs are processed later */ } } /** Finish up the result set. This adds the non-hidden base classes to the set. */ PUBLIC void Class_lookup_helper::finish(Class_symbol* sym) { typedef Class_symbol::bases_t::iterator iter_t; for (iter_t i = sym->virtual_base_classes.begin(); i != sym->virtual_base_classes.end(); ++i) if (predicate(*i) && find(hidden_vbcs.begin(), hidden_vbcs.end(), *i) == hidden_vbcs.end()) result_set.push_back(*i); } /************************ Class_name_lookup_helper ***********************/ class Class_name_lookup_helper : public Class_lookup_helper { std::string name; }; PUBLIC Class_name_lookup_helper::Class_name_lookup_helper(std::string name) : name(name) { } PUBLIC Class_name_lookup_helper::~Class_name_lookup_helper() { } PUBLIC bool Class_name_lookup_helper::predicate(Class_symbol* sym) { return sym->lookup_helper(name); } /** Get result. This checks whether there is a unique result. */ PUBLIC Symbol_pair Class_name_lookup_helper::get_result() { typedef classes_t::iterator iter_t; Symbol_pair result; Class_symbol *untag_class = 0, *tag_class = 0; for (iter_t i = result_set.begin(); i != result_set.end(); ++i) { Symbol_pair p = (*i)->lookup_helper(name); /* check "normal" member */ assert(p.untag); if (untag_class) { if (*i != untag_class || !is_static(p.untag)) compile_error("ambiguous: " + name); } else { untag_class = *i; result.untag = p.untag; } /* check "tag" member */ if (p.tag) { if (tag_class) { if (*i != tag_class || !is_static(p.tag)) compile_error("ambiguous: class/enum " + name); } else { tag_class = *i; result.tag = p.tag; } } } return result; } /****************************** Class_scope ******************************/ PUBLIC Class_scope::Class_scope(Class_symbol* sym) : Abstract_scope(sym->in_scope), sym(sym), prefix(Symbol_name::get_mangled_scope_from_symbol(sym->get_name())) { } PUBLIC Class_scope::~Class_scope() { } PUBLIC inline Class_symbol* Class_scope::get_class_symbol() const { return sym; } PUBLIC Type Class_scope::get_this_type() const { return Type(); } PUBLIC std::string Class_scope::get_unique_name(std::string name) { return Symbol_name::get_mangled_symbol_name(prefix, name); } /** True iff symbol /sym/ is a static entity, i.e. no matter through what object we access it, we get the same thing. */ static bool is_static(Symbol* sym) { /* allow: static variables, enumerators, nested types */ // FIXME? I think this doesn't apply to member functions. switch(sym->get_kind()) { case Symbol::k_Enum: case Symbol::k_ClassOrStruct: case Symbol::k_Union: case Symbol::k_Typedef: case Symbol::k_ClassTemplate: return true; case Symbol::k_Variable: { Variable_symbol* vsym = downcast(sym); return vsym->get_storage_class() == s_Static || !vsym->has_address(); } case Symbol::k_Namespace: case Symbol::k_Function: ; } return false; } /** Look up name here. */ PUBLIC Symbol_pair Class_scope::lookup_here(std::string name, bool for_decl) { if (Symbol_pair p = sym->lookup_helper(name)) return p; if (for_decl) return Symbol_pair(); /* not a declaration. search base classes. */ Class_name_lookup_helper h(name); h.add_class(sym); h.finish(sym); return h.get_result(); } PUBLIC void Class_scope::add_symbol(std::string name, Symbol* sym) { // Symbol_table::get_instance().add_symbol(prefix + name, name.length(), sym); Symbol_table::get_instance().add_symbol(get_unique_name(name), name.length(), sym); } /** Add a variable at class scope. */ PUBLIC Variable_symbol* Class_scope::add_variable(Storage_class_specifier storage, Type type, Ptree* name, Ptree* init, Ptree* bitsize) { assert(type.get_kind() != Type::k_Function); Symbol_name sym_name(name, this, false); if (sym_name.is_qualified()) compile_error("can't define structured names inside a class"); if (sym_name.is_template()) compile_error("can't define template specialisation"); if (storage == s_None) storage = s_Member; if (storage != s_Member && storage != s_Mutable && storage != s_Static) compile_error("invalid storage class for member variable"); if (storage == s_Mutable && type.is_qualified(Type::q_Const)) compile_error("`mutable const' is not sensible"); if (init) { if (storage == s_Static && (type.is_int() || type.is_enum_type()) && type.is_qualified(Type::q_Const)) /* allowed, 9.4.2p4 */; else compile_error("can't initialize member " + sym_name.get_name() + " of class " + sym->get_name()); init = Init_handler(this, init, true).process_initializer(type); } Symbol_pair pair = sym_name.lookup_for_decl(); if (pair && pair.tag != pair.untag) compile_error("`" + sym_name.get_name() + "' already defined"); if (storage == s_Static) { /* static variables. These get storage class "extern" because they are externally visible. Plus, an initializer contributes to the semantics of the program. */ Variable_symbol* vsym = new Variable_symbol(type, s_Extern, init, bitsize, init ? Symbol::st_Defined : Symbol::st_Declared); add_symbol(sym_name.get_name(), vsym); sym->members.push_back(vsym); return vsym->is_defined() ? vsym : 0; } else { /* members. These are always defined. */ Variable_symbol* vsym = new Variable_symbol(type, storage, init, bitsize, Symbol::st_Defined); add_symbol(sym_name.get_name(), vsym); sym->members.push_back(vsym); vsym->set_class(this->sym); /* if it wants to be POD, it can't contain [arrays of] pointer to member or non-POD */ Type nonarr = type.sans_array(); if (nonarr.get_kind() == Type::k_Member || nonarr.get_kind() == Type::k_Reference || !type.is_pod()) sym->pod = false; return 0; } } static bool is_covariant_or_same(Type n, Type o) { if (n == o) return true; /* Return types are covariant if... - both are pointers or references to classes... */ if ((n.get_kind() == Type::k_Pointer || n.get_kind() == Type::k_Reference) && o.get_kind() == n.get_kind() && n.get_basis_type().is_class_type() && o.get_basis_type().is_class_type()) { Class_symbol* nc = downcast(n.get_basis_type().get_type_symbol()); Class_symbol* oc = downcast(o.get_basis_type().get_type_symbol()); /* - the class in B::f (oc) is the same class or an unambiguous direct or indirect base class of the class in D::f (nc) - same cv-qualification - class in D::f (nc) has same or less cv-qualification then B::f (oc) */ return (nc == oc || oc->is_unique_base_class_of(nc)) && n.is_same_qualified_as(o) && !n.get_basis_type().is_more_qualified_than(o.get_basis_type()); } return false; } PUBLIC Function_signature* Class_scope::add_function_decl(Storage_class_specifier storage, Function_specifier_set fspec, Type type, const Symbol_name& sym_name) { if (sym_name.is_qualified()) compile_error("invalid name for member function"); if (sym_name.is_template()) compile_error("definition of template specialisation"); Function_symbol* fsym; Symbol_pair pair = lookup_here(sym_name.get_name(), true); if (!pair || pair.tag == pair.untag) { fsym = new Function_symbol(this, sym_name.get_kind()); add_symbol(sym_name.get_name(), fsym); sym->member_functions.push_back(fsym); } else { fsym = dynamic_cast(pair.untag); if (!fsym) compile_error("`" + sym_name.get_name() + "' is not a function"); } if (storage == s_None) storage = s_Member; else if (storage == s_Static) /* ok */ ; else compile_error("invalid storage class for member function"); /* make function virtual if a base class already contains such a function */ Class_symbol::bases_t all_your_base; sym->enumerate_base_classes(true, &all_your_base); for (Class_symbol::bases_t::iterator i = all_your_base.begin(); i != all_your_base.end(); ++i) { Function_symbol* fsym = dynamic_cast((*i)->lookup_helper(sym_name.get_name()).untag); if (!fsym) continue; for (Function_symbol::Sig_it si = fsym->sig_begin(); si != fsym->sig_end(); ++si) { if ((*si)->is_declared() && (*si)->get_storage_specifier() == s_Member && (*si)->get_proto_type().get_function_signature() == type.get_function_signature() && ((*si)->get_function_specifiers() & f_Virtual)) { /* okay, it's there. Valid? */ fspec |= f_Virtual; if (!is_covariant_or_same(type.get_return_type(), (*si)->get_return_type())) compile_error("overriding virtual function `" + (*si)->get_function()->get_name() + "' with conflicting return type"); } } } if ((fspec & f_Virtual) && storage != s_Member) compile_error("non-member function can't be virtual"); Function_signature* sig = fsym->add_signature(type, sym->get_type(), storage, fspec, Function_symbol::must_be_new); assert(!sig->is_declared()); sig->set_status(Symbol::st_Declared); if (fspec & f_Virtual) sym->pod = false; return sig; } PUBLIC void Class_scope::add_function_implementation(Function_signature* fsig, Block_scope* scope, Ptree* tree, Ptree* initializer) { /* functions declared inside a class are automatically inline */ fsig->merge_fspec(f_Inline); /* enqueue function body for later processing */ get_parent()->add_function_implementation(fsig, scope, tree, initializer); } PUBLIC bool Class_scope::is_constructor(Ptree* tree) { // FIXME: in template classes, "name " may also a constructor return tree->IsLeaf() && tree->ToString() == sym->get_real_name(); }