// Compiler implementation of the D programming language // Copyright (c) 1999-2012 by Digital Mars // All Rights Reserved // written by Walter Bright // http://www.digitalmars.com // License for redistribution is by either the Artistic License // in artistic.txt, or the GNU General Public License in gnu.txt. // See the included readme.txt for details. #include #include #include #include // mem{cpy|set}() #include "root.h" #include "rmem.h" #include "enum.h" #include "init.h" #include "attrib.h" #include "declaration.h" #include "aggregate.h" #include "id.h" #include "mtype.h" #include "scope.h" #include "module.h" #include "expression.h" #include "statement.h" #include "template.h" /********************************* ClassDeclaration ****************************/ ClassDeclaration *ClassDeclaration::classinfo; ClassDeclaration *ClassDeclaration::object; ClassDeclaration *ClassDeclaration::throwable; ClassDeclaration *ClassDeclaration::exception; ClassDeclaration *ClassDeclaration::errorException; ClassDeclaration::ClassDeclaration(Loc loc, Identifier *id, BaseClasses *baseclasses) : AggregateDeclaration(loc, id) { static char msg[] = "only object.d can define this reserved class name"; if (baseclasses) // Actually, this is a transfer this->baseclasses = baseclasses; else this->baseclasses = new BaseClasses(); baseClass = NULL; interfaces_dim = 0; interfaces = NULL; vtblInterfaces = NULL; //printf("ClassDeclaration(%s), dim = %d\n", id->toChars(), this->baseclasses->dim); // For forward references type = new TypeClass(this); handle = type; staticCtor = NULL; staticDtor = NULL; #if IN_DMD vtblsym = NULL; #endif vclassinfo = NULL; if (id) { // Look for special class names if (id == Id::__sizeof || id == Id::__xalignof || id == Id::mangleof) error("illegal class name"); // BUG: What if this is the wrong TypeInfo, i.e. it is nested? if (id->toChars()[0] == 'T') { if (id == Id::TypeInfo) { if (Type::typeinfo) Type::typeinfo->error("%s", msg); Type::typeinfo = this; } if (id == Id::TypeInfo_Class) { if (Type::typeinfoclass) Type::typeinfoclass->error("%s", msg); Type::typeinfoclass = this; } if (id == Id::TypeInfo_Interface) { if (Type::typeinfointerface) Type::typeinfointerface->error("%s", msg); Type::typeinfointerface = this; } if (id == Id::TypeInfo_Struct) { if (Type::typeinfostruct) Type::typeinfostruct->error("%s", msg); Type::typeinfostruct = this; } if (id == Id::TypeInfo_Typedef) { if (Type::typeinfotypedef) Type::typeinfotypedef->error("%s", msg); Type::typeinfotypedef = this; } if (id == Id::TypeInfo_Pointer) { if (Type::typeinfopointer) Type::typeinfopointer->error("%s", msg); Type::typeinfopointer = this; } if (id == Id::TypeInfo_Array) { if (Type::typeinfoarray) Type::typeinfoarray->error("%s", msg); Type::typeinfoarray = this; } if (id == Id::TypeInfo_StaticArray) { //if (Type::typeinfostaticarray) //Type::typeinfostaticarray->error("%s", msg); Type::typeinfostaticarray = this; } if (id == Id::TypeInfo_AssociativeArray) { if (Type::typeinfoassociativearray) Type::typeinfoassociativearray->error("%s", msg); Type::typeinfoassociativearray = this; } if (id == Id::TypeInfo_Enum) { if (Type::typeinfoenum) Type::typeinfoenum->error("%s", msg); Type::typeinfoenum = this; } if (id == Id::TypeInfo_Function) { if (Type::typeinfofunction) Type::typeinfofunction->error("%s", msg); Type::typeinfofunction = this; } if (id == Id::TypeInfo_Delegate) { if (Type::typeinfodelegate) Type::typeinfodelegate->error("%s", msg); Type::typeinfodelegate = this; } if (id == Id::TypeInfo_Tuple) { if (Type::typeinfotypelist) Type::typeinfotypelist->error("%s", msg); Type::typeinfotypelist = this; } #if DMDV2 if (id == Id::TypeInfo_Const) { if (Type::typeinfoconst) Type::typeinfoconst->error("%s", msg); Type::typeinfoconst = this; } if (id == Id::TypeInfo_Invariant) { if (Type::typeinfoinvariant) Type::typeinfoinvariant->error("%s", msg); Type::typeinfoinvariant = this; } if (id == Id::TypeInfo_Shared) { if (Type::typeinfoshared) Type::typeinfoshared->error("%s", msg); Type::typeinfoshared = this; } if (id == Id::TypeInfo_Wild) { if (Type::typeinfowild) Type::typeinfowild->error("%s", msg); Type::typeinfowild = this; } if (id == Id::TypeInfo_Vector) { if (Type::typeinfovector) Type::typeinfovector->error("%s", msg); Type::typeinfovector = this; } #endif } if (id == Id::Object) { if (object) object->error("%s", msg); object = this; } if (id == Id::Throwable) { if (throwable) throwable->error("%s", msg); throwable = this; } if (id == Id::Exception) { if (exception) exception->error("%s", msg); exception = this; } if (id == Id::Error) { if (errorException) errorException->error("%s", msg); errorException = this; } //if (id == Id::ClassInfo) if (id == Id::TypeInfo_Class) { if (classinfo) classinfo->error("%s", msg); classinfo = this; } #if !MODULEINFO_IS_STRUCT if (id == Id::ModuleInfo) { if (Module::moduleinfo) Module::moduleinfo->error("%s", msg); Module::moduleinfo = this; } #endif } com = 0; isscope = 0; isabstract = 0; inuse = 0; } Dsymbol *ClassDeclaration::syntaxCopy(Dsymbol *s) { ClassDeclaration *cd; //printf("ClassDeclaration::syntaxCopy('%s')\n", toChars()); if (s) cd = (ClassDeclaration *)s; else cd = new ClassDeclaration(loc, ident, NULL); cd->storage_class |= storage_class; cd->baseclasses->setDim(this->baseclasses->dim); for (size_t i = 0; i < cd->baseclasses->dim; i++) { BaseClass *b = (*this->baseclasses)[i]; BaseClass *b2 = new BaseClass(b->type->syntaxCopy(), b->protection); (*cd->baseclasses)[i] = b2; } ScopeDsymbol::syntaxCopy(cd); return cd; } void ClassDeclaration::semantic(Scope *sc) { //printf("ClassDeclaration::semantic(%s), type = %p, sizeok = %d, this = %p\n", toChars(), type, sizeok, this); //printf("\tparent = %p, '%s'\n", sc->parent, sc->parent ? sc->parent->toChars() : ""); //printf("sc->stc = %x\n", sc->stc); //{ static int n; if (++n == 20) *(char*)0=0; } if (!ident) // if anonymous class { const char *id = "__anonclass"; ident = Identifier::generateId(id); } if (!sc) sc = scope; if (!parent && sc->parent && !sc->parent->isModule()) parent = sc->parent; type = type->semantic(loc, sc); handle = type; if (!members) // if forward reference { //printf("\tclass '%s' is forward referenced\n", toChars()); return; } if (symtab) { if (sizeok == SIZEOKdone || !scope) { //printf("\tsemantic for '%s' is already completed\n", toChars()); return; // semantic() already completed } } else symtab = new DsymbolTable(); Scope *scx = NULL; if (scope) { sc = scope; scx = scope; // save so we don't make redundant copies scope = NULL; } unsigned dprogress_save = Module::dprogress; int errors = global.gaggedErrors; if (sc->stc & STCdeprecated) { isdeprecated = true; } userAttributes = sc->userAttributes; if (sc->linkage == LINKcpp) error("cannot create C++ classes"); // Expand any tuples in baseclasses[] for (size_t i = 0; i < baseclasses->dim; ) { BaseClass *b = (*baseclasses)[i]; b->type = b->type->semantic(loc, sc); Type *tb = b->type->toBasetype(); if (tb->ty == Ttuple) { TypeTuple *tup = (TypeTuple *)tb; enum PROT protection = b->protection; baseclasses->remove(i); size_t dim = Parameter::dim(tup->arguments); for (size_t j = 0; j < dim; j++) { Parameter *arg = Parameter::getNth(tup->arguments, j); b = new BaseClass(arg->type, protection); baseclasses->insert(i + j, b); } } else i++; } // See if there's a base class as first in baseclasses[] if (baseclasses->dim) { TypeClass *tc; BaseClass *b; Type *tb; b = (*baseclasses)[0]; //b->type = b->type->semantic(loc, sc); tb = b->type->toBasetype(); if (tb->ty != Tclass) { if (b->type != Type::terror) error("base type must be class or interface, not %s", b->type->toChars()); baseclasses->remove(0); } else { tc = (TypeClass *)(tb); if (tc->sym->isDeprecated()) { if (!isDeprecated()) { // Deriving from deprecated class makes this one deprecated too isdeprecated = true; tc->checkDeprecated(loc, sc); } } if (tc->sym->isInterfaceDeclaration()) ; else { for (ClassDeclaration *cdb = tc->sym; cdb; cdb = cdb->baseClass) { if (cdb == this) { error("circular inheritance"); baseclasses->remove(0); goto L7; } } if (!tc->sym->symtab || tc->sym->sizeok == SIZEOKnone) { // Try to resolve forward reference if (/*sc->mustsemantic &&*/ tc->sym->scope) tc->sym->semantic(NULL); } if (!tc->sym->symtab || tc->sym->scope || tc->sym->sizeok == SIZEOKnone) { //printf("%s: forward reference of base class %s\n", toChars(), tc->sym->toChars()); //error("forward reference of base class %s", baseClass->toChars()); // Forward reference of base class, try again later //printf("\ttry later, forward reference of base class %s\n", tc->sym->toChars()); scope = scx ? scx : new Scope(*sc); scope->setNoFree(); if (tc->sym->scope) tc->sym->scope->module->addDeferredSemantic(tc->sym); scope->module->addDeferredSemantic(this); return; } else { baseClass = tc->sym; b->base = baseClass; } L7: ; } } } // Treat the remaining entries in baseclasses as interfaces // Check for errors, handle forward references for (size_t i = (baseClass ? 1 : 0); i < baseclasses->dim; ) { TypeClass *tc; BaseClass *b; Type *tb; b = (*baseclasses)[i]; b->type = b->type->semantic(loc, sc); tb = b->type->toBasetype(); if (tb->ty == Tclass) tc = (TypeClass *)tb; else tc = NULL; if (!tc || !tc->sym->isInterfaceDeclaration()) { if (b->type != Type::terror) error("base type must be interface, not %s", b->type->toChars()); baseclasses->remove(i); continue; } else { if (tc->sym->isDeprecated()) { if (!isDeprecated()) { // Deriving from deprecated class makes this one deprecated too isdeprecated = true; tc->checkDeprecated(loc, sc); } } // Check for duplicate interfaces for (size_t j = (baseClass ? 1 : 0); j < i; j++) { BaseClass *b2 = (*baseclasses)[j]; if (b2->base == tc->sym) error("inherits from duplicate interface %s", b2->base->toChars()); } if (!tc->sym->symtab) { // Try to resolve forward reference if (/*sc->mustsemantic &&*/ tc->sym->scope) tc->sym->semantic(NULL); } b->base = tc->sym; if (!b->base->symtab || b->base->scope) { //error("forward reference of base class %s", baseClass->toChars()); // Forward reference of base, try again later //printf("\ttry later, forward reference of base %s\n", baseClass->toChars()); scope = scx ? scx : new Scope(*sc); scope->setNoFree(); if (tc->sym->scope) tc->sym->scope->module->addDeferredSemantic(tc->sym); scope->module->addDeferredSemantic(this); return; } } i++; } // If no base class, and this is not an Object, use Object as base class if (!baseClass && ident != Id::Object) { if (!object) { error("missing or corrupt object.d"); fatal(); } Type *t = object->type; t = t->semantic(loc, sc)->toBasetype(); assert(t->ty == Tclass); TypeClass *tc = (TypeClass *)t; BaseClass *b = new BaseClass(tc, PROTpublic); baseclasses->shift(b); baseClass = tc->sym; assert(!baseClass->isInterfaceDeclaration()); b->base = baseClass; } interfaces_dim = baseclasses->dim; interfaces = baseclasses->tdata(); if (baseClass) { if (baseClass->storage_class & STCfinal) error("cannot inherit from final class %s", baseClass->toChars()); interfaces_dim--; interfaces++; // Copy vtbl[] from base class vtbl.setDim(baseClass->vtbl.dim); memcpy(vtbl.tdata(), baseClass->vtbl.tdata(), sizeof(void *) * vtbl.dim); // Inherit properties from base class com = baseClass->isCOMclass(); isscope = baseClass->isscope; vthis = baseClass->vthis; storage_class |= baseClass->storage_class & STC_TYPECTOR; } else { // No base class, so this is the root of the class hierarchy vtbl.setDim(0); vtbl.push(this); // leave room for classinfo as first member } protection = sc->protection; storage_class |= sc->stc; if (sizeok == SIZEOKnone) { interfaceSemantic(sc); for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->addMember(sc, this, 1); } /* If this is a nested class, add the hidden 'this' * member which is a pointer to the enclosing scope. */ if (vthis) // if inheriting from nested class { // Use the base class's 'this' member isnested = true; if (storage_class & STCstatic) error("static class cannot inherit from nested class %s", baseClass->toChars()); if (toParent2() != baseClass->toParent2() && (!toParent2() || !baseClass->toParent2()->getType() || !baseClass->toParent2()->getType()->isBaseOf(toParent2()->getType(), NULL))) { if (toParent2()) { error("is nested within %s, but super class %s is nested within %s", toParent2()->toChars(), baseClass->toChars(), baseClass->toParent2()->toChars()); } else { error("is not nested, but super class %s is nested within %s", baseClass->toChars(), baseClass->toParent2()->toChars()); } isnested = false; } } else if (!(storage_class & STCstatic)) { Dsymbol *s = toParent2(); if (s) { AggregateDeclaration *ad = s->isClassDeclaration(); FuncDeclaration *fd = s->isFuncDeclaration(); if (ad || fd) { isnested = true; Type *t; if (ad) t = ad->handle; else if (fd) { AggregateDeclaration *ad2 = fd->isMember2(); if (ad2) t = ad2->handle; else { t = Type::tvoidptr; } } else assert(0); if (t->ty == Tstruct) // ref to struct t = Type::tvoidptr; assert(!vthis); vthis = new ThisDeclaration(loc, t); members->push(vthis); } } } } if (storage_class & STCauto) error("storage class 'auto' is invalid when declaring a class, did you mean to use 'scope'?"); if (storage_class & STCscope) isscope = 1; if (storage_class & STCabstract) isabstract = 1; sc = sc->push(this); //sc->stc &= ~(STCfinal | STCauto | STCscope | STCstatic | STCabstract | STCdeprecated | STC_TYPECTOR | STCtls | STCgshared); //sc->stc |= storage_class & STC_TYPECTOR; sc->stc &= STCsafe | STCtrusted | STCsystem; sc->parent = this; sc->inunion = 0; if (isCOMclass()) { #if _WIN32 sc->linkage = LINKwindows; #else /* This enables us to use COM objects under Linux and * work with things like XPCOM */ sc->linkage = LINKc; #endif } sc->protection = PROTpublic; sc->explicitProtection = 0; sc->structalign = STRUCTALIGN_DEFAULT; if (baseClass) { sc->offset = baseClass->structsize; alignsize = baseClass->alignsize; // if (isnested) // sc->offset += PTRSIZE; // room for uplevel context pointer } else { sc->offset = PTRSIZE * 2; // allow room for __vptr and __monitor alignsize = PTRSIZE; } sc->userAttributes = NULL; structsize = sc->offset; Scope scsave = *sc; size_t members_dim = members->dim; sizeok = SIZEOKnone; /* Set scope so if there are forward references, we still might be able to * resolve individual members like enums. */ for (size_t i = 0; i < members_dim; i++) { Dsymbol *s = (*members)[i]; /* There are problems doing this in the general case because * Scope keeps track of things like 'offset' */ if (s->isEnumDeclaration() || (s->isAggregateDeclaration() && s->ident) || s->isTemplateMixin() || s->isAttribDeclaration() || s->isAliasDeclaration()) { //printf("[%d] setScope %s %s, sc = %p\n", i, s->kind(), s->toChars(), sc); s->setScope(sc); } } for (size_t i = 0; i < members_dim; i++) { Dsymbol *s = (*members)[i]; s->semantic(sc); } // Set the offsets of the fields and determine the size of the class unsigned offset = structsize; bool isunion = isUnionDeclaration() != NULL; for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->setFieldOffset(this, &offset, false); } sc->offset = structsize; if (global.gag && global.gaggedErrors != errors) { // The type is no good, yet the error messages were gagged. type = Type::terror; } if (sizeok == SIZEOKfwd) // failed due to forward references { // semantic() failed due to forward references // Unwind what we did, and defer it for later for (size_t i = 0; i < fields.dim; i++) { Dsymbol *s = fields[i]; VarDeclaration *vd = s->isVarDeclaration(); if (vd) vd->offset = 0; } fields.setDim(0); structsize = 0; alignsize = 0; // structalign = 0; sc = sc->pop(); scope = scx ? scx : new Scope(*sc); scope->setNoFree(); scope->module->addDeferredSemantic(this); Module::dprogress = dprogress_save; //printf("\tsemantic('%s') failed due to forward references\n", toChars()); return; } //printf("\tsemantic('%s') successful\n", toChars()); //members->print(); /* Look for special member functions. * They must be in this class, not in a base class. */ ctor = search(0, Id::ctor, 0); #if DMDV1 if (ctor && (ctor->toParent() != this || !ctor->isCtorDeclaration())) ctor = NULL; #else if (ctor && (ctor->toParent() != this || !(ctor->isCtorDeclaration() || ctor->isTemplateDeclaration()))) ctor = NULL; // search() looks through ancestor classes #endif // dtor = (DtorDeclaration *)search(Id::dtor, 0); // if (dtor && dtor->toParent() != this) // dtor = NULL; // inv = (InvariantDeclaration *)search(Id::classInvariant, 0); // if (inv && inv->toParent() != this) // inv = NULL; // Can be in base class aggNew = (NewDeclaration *)search(0, Id::classNew, 0); aggDelete = (DeleteDeclaration *)search(0, Id::classDelete, 0); // If this class has no constructor, but base class does, create // a constructor: // this() { } if (!ctor && baseClass && baseClass->ctor) { //printf("Creating default this(){} for class %s\n", toChars()); Type *tf = new TypeFunction(NULL, NULL, 0, LINKd, 0); CtorDeclaration *ctor = new CtorDeclaration(loc, 0, 0, tf); ctor->isImplicit = true; ctor->fbody = new CompoundStatement(0, new Statements()); members->push(ctor); ctor->addMember(sc, this, 1); *sc = scsave; // why? What about sc->nofree? ctor->semantic(sc); this->ctor = ctor; defaultCtor = ctor; } #if 0 if (baseClass) { if (!aggDelete) aggDelete = baseClass->aggDelete; if (!aggNew) aggNew = baseClass->aggNew; } #endif // Allocate instance of each new interface sc->offset = structsize; for (size_t i = 0; i < vtblInterfaces->dim; i++) { BaseClass *b = (*vtblInterfaces)[i]; unsigned thissize = PTRSIZE; alignmember(STRUCTALIGN_DEFAULT, thissize, &sc->offset); assert(b->offset == 0); b->offset = sc->offset; // Take care of single inheritance offsets while (b->baseInterfaces_dim) { b = &b->baseInterfaces[0]; b->offset = sc->offset; } sc->offset += thissize; if (alignsize < thissize) alignsize = thissize; } structsize = sc->offset; #if IN_LLVM if (sc->structalign == STRUCTALIGN_DEFAULT) structsize = (structsize + alignsize - 1) & ~(alignsize - 1); else structsize = (structsize + sc->structalign - 1) & ~(sc->structalign - 1); #endif sizeok = SIZEOKdone; Module::dprogress++; dtor = buildDtor(sc); if (Dsymbol *assign = search_function(this, Id::assign)) { Expression *e = new NullExp(loc, type); // dummy rvalue Expressions *arguments = new Expressions(); arguments->push(e); // check identity opAssign exists FuncDeclaration *fd = assign->isFuncDeclaration(); if (fd) { fd = fd->overloadResolve(loc, e, arguments, 1); if (fd && !(fd->storage_class & STCdisable)) goto Lassignerr; } if (TemplateDeclaration *td = assign->isTemplateDeclaration()) { fd = td->deduceFunctionTemplate(sc, loc, NULL, e, arguments, 1+2); if (fd && !(fd->storage_class & STCdisable)) goto Lassignerr; } Lassignerr: if (fd && !(fd->storage_class & STCdisable)) error("identity assignment operator overload is illegal"); } sc->pop(); #if 0 // Do not call until toObjfile() because of forward references // Fill in base class vtbl[]s for (i = 0; i < vtblInterfaces->dim; i++) { BaseClass *b = (*vtblInterfaces)[i]; //b->fillVtbl(this, &b->vtbl, 1); } #endif //printf("-ClassDeclaration::semantic(%s), type = %p\n", toChars(), type); if (deferred && !global.gag) { deferred->semantic2(sc); deferred->semantic3(sc); } } void ClassDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { if (!isAnonymous()) { buf->printf("%s ", kind()); buf->writestring(toChars()); if (baseclasses->dim) buf->writestring(" : "); } for (size_t i = 0; i < baseclasses->dim; i++) { BaseClass *b = (*baseclasses)[i]; if (i) buf->writeByte(','); //buf->writestring(b->base->ident->toChars()); b->type->toCBuffer(buf, NULL, hgs); } if (members) { buf->writenl(); buf->writeByte('{'); buf->writenl(); buf->level++; for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->toCBuffer(buf, hgs); } buf->level--; buf->writestring("}"); } else buf->writeByte(';'); buf->writenl(); } #if 0 void ClassDeclaration::defineRef(Dsymbol *s) { ClassDeclaration *cd; AggregateDeclaration::defineRef(s); cd = s->isClassDeclaration(); baseType = cd->baseType; cd->baseType = NULL; } #endif /********************************************* * Determine if 'this' is a base class of cd. * This is used to detect circular inheritance only. */ int ClassDeclaration::isBaseOf2(ClassDeclaration *cd) { if (!cd) return 0; //printf("ClassDeclaration::isBaseOf2(this = '%s', cd = '%s')\n", toChars(), cd->toChars()); for (size_t i = 0; i < cd->baseclasses->dim; i++) { BaseClass *b = (*cd->baseclasses)[i]; if (b->base == this || isBaseOf2(b->base)) return 1; } return 0; } /******************************************* * Determine if 'this' is a base class of cd. */ int ClassDeclaration::isBaseOf(ClassDeclaration *cd, int *poffset) { //printf("ClassDeclaration::isBaseOf(this = '%s', cd = '%s')\n", toChars(), cd->toChars()); if (poffset) *poffset = 0; while (cd) { /* cd->baseClass might not be set if cd is forward referenced. */ if (!cd->baseClass && cd->baseclasses->dim && !cd->isInterfaceDeclaration()) { cd->semantic(NULL); if (!cd->baseClass) cd->error("base class is forward referenced by %s", toChars()); } if (this == cd->baseClass) return 1; cd = cd->baseClass; } return 0; } /********************************************* * Determine if 'this' has complete base class information. * This is used to detect forward references in covariant overloads. */ int ClassDeclaration::isBaseInfoComplete() { if (!baseClass) return ident == Id::Object; for (size_t i = 0; i < baseclasses->dim; i++) { BaseClass *b = (*baseclasses)[i]; if (!b->base || !b->base->isBaseInfoComplete()) return 0; } return 1; } Dsymbol *ClassDeclaration::search(Loc loc, Identifier *ident, int flags) { Dsymbol *s; //printf("%s.ClassDeclaration::search('%s')\n", toChars(), ident->toChars()); if (scope && !symtab) { Scope *sc = scope; sc->mustsemantic++; // If speculatively gagged, ungag now. unsigned oldgag = global.gag; if (global.isSpeculativeGagging()) global.gag = 0; semantic(sc); global.gag = oldgag; sc->mustsemantic--; } if (!members || !symtab) { error("is forward referenced when looking for '%s'", ident->toChars()); //*(char*)0=0; return NULL; } s = ScopeDsymbol::search(loc, ident, flags); if (!s) { // Search bases classes in depth-first, left to right order for (size_t i = 0; i < baseclasses->dim; i++) { BaseClass *b = (*baseclasses)[i]; if (b->base) { if (!b->base->symtab) error("base %s is forward referenced", b->base->ident->toChars()); else { s = b->base->search(loc, ident, flags); if (s == this) // happens if s is nested in this and derives from this s = NULL; else if (s) break; } } } } return s; } Dsymbol *ClassDeclaration::searchBase(Loc loc, Identifier *ident) { // Search bases classes in depth-first, left to right order for (size_t i = 0; i < baseclasses->dim; i++) { BaseClass *b = (*baseclasses)[i]; Dsymbol *cdb = b->type->isClassHandle(); if (cdb->ident->equals(ident)) return cdb; cdb = ((ClassDeclaration *)cdb)->searchBase(loc, ident); if (cdb) return cdb; } return NULL; } /********************************************************** * fd is in the vtbl[] for this class. * Return 1 if function is hidden (not findable through search). */ #if DMDV2 int isf(void *param, FuncDeclaration *fd) { //printf("param = %p, fd = %p %s\n", param, fd, fd->toChars()); return param == fd; } int ClassDeclaration::isFuncHidden(FuncDeclaration *fd) { //printf("ClassDeclaration::isFuncHidden(class = %s, fd = %s)\n", toChars(), fd->toChars()); Dsymbol *s = search(0, fd->ident, 4|2); if (!s) { //printf("not found\n"); /* Because, due to a hack, if there are multiple definitions * of fd->ident, NULL is returned. */ return 0; } s = s->toAlias(); OverloadSet *os = s->isOverloadSet(); if (os) { for (size_t i = 0; i < os->a.dim; i++) { Dsymbol *s2 = os->a[i]; FuncDeclaration *f2 = s2->isFuncDeclaration(); if (f2 && overloadApply(f2, &isf, fd)) return 0; } return 1; } else { FuncDeclaration *fdstart = s->isFuncDeclaration(); //printf("%s fdstart = %p\n", s->kind(), fdstart); if (overloadApply(fdstart, &isf, fd)) return 0; return !fd->parent->isTemplateMixin(); } } #endif /**************** * Find virtual function matching identifier and type. * Used to build virtual function tables for interface implementations. */ FuncDeclaration *ClassDeclaration::findFunc(Identifier *ident, TypeFunction *tf) { //printf("ClassDeclaration::findFunc(%s, %s) %s\n", ident->toChars(), tf->toChars(), toChars()); FuncDeclaration *fdmatch = NULL; FuncDeclaration *fdambig = NULL; ClassDeclaration *cd = this; Dsymbols *vtbl = &cd->vtbl; while (1) { for (size_t i = 0; i < vtbl->dim; i++) { FuncDeclaration *fd = (*vtbl)[i]->isFuncDeclaration(); if (!fd) continue; // the first entry might be a ClassInfo //printf("\t[%d] = %s\n", i, fd->toChars()); if (ident == fd->ident && fd->type->covariant(tf) == 1) { //printf("fd->parent->isClassDeclaration() = %p\n", fd->parent->isClassDeclaration()); if (!fdmatch) goto Lfd; if (fd == fdmatch) goto Lfdmatch; { // Function type matcing: exact > covariant int m1 = tf->equals(fd ->type) ? MATCHexact : MATCHnomatch; int m2 = tf->equals(fdmatch->type) ? MATCHexact : MATCHnomatch; if (m1 > m2) goto Lfd; else if (m1 < m2) goto Lfdmatch; } { int m1 = (tf->mod == fd ->type->mod) ? MATCHexact : MATCHnomatch; int m2 = (tf->mod == fdmatch->type->mod) ? MATCHexact : MATCHnomatch; if (m1 > m2) goto Lfd; else if (m1 < m2) goto Lfdmatch; } { // The way of definition: non-mixin > mixin int m1 = fd ->parent->isClassDeclaration() ? MATCHexact : MATCHnomatch; int m2 = fdmatch->parent->isClassDeclaration() ? MATCHexact : MATCHnomatch; if (m1 > m2) goto Lfd; else if (m1 < m2) goto Lfdmatch; } Lambig: fdambig = fd; //printf("Lambig fdambig = %s %s [%s]\n", fdambig->toChars(), fdambig->type->toChars(), fdambig->loc.toChars()); continue; Lfd: fdmatch = fd, fdambig = NULL; //printf("Lfd fdmatch = %s %s [%s]\n", fdmatch->toChars(), fdmatch->type->toChars(), fdmatch->loc.toChars()); continue; Lfdmatch: continue; } //else printf("\t\t%d\n", fd->type->covariant(tf)); } if (!cd) break; vtbl = &cd->vtblFinal; cd = cd->baseClass; } if (fdambig) error("ambiguous virtual function %s", fdambig->toChars()); return fdmatch; } void ClassDeclaration::interfaceSemantic(Scope *sc) { InterfaceDeclaration *id = isInterfaceDeclaration(); vtblInterfaces = new BaseClasses(); vtblInterfaces->reserve(interfaces_dim); for (size_t i = 0; i < interfaces_dim; i++) { BaseClass *b = interfaces[i]; // If this is an interface, and it derives from a COM interface, // then this is a COM interface too. if (b->base->isCOMinterface()) com = 1; if (b->base->isCPPinterface() && id) id->cpp = 1; vtblInterfaces->push(b); b->copyBaseInterfaces(vtblInterfaces); } } /**************************************** */ int ClassDeclaration::isCOMclass() { return com; } int ClassDeclaration::isCOMinterface() { return 0; } #if DMDV2 int ClassDeclaration::isCPPinterface() { return 0; } #endif /**************************************** */ int ClassDeclaration::isAbstract() { if (isabstract) return TRUE; for (size_t i = 1; i < vtbl.dim; i++) { FuncDeclaration *fd = vtbl[i]->isFuncDeclaration(); //printf("\tvtbl[%d] = %p\n", i, fd); if (!fd || fd->isAbstract()) { isabstract |= 1; return TRUE; } } return FALSE; } /**************************************** * Determine if slot 0 of the vtbl[] is reserved for something else. * For class objects, yes, this is where the classinfo ptr goes. * For COM interfaces, no. * For non-COM interfaces, yes, this is where the Interface ptr goes. */ int ClassDeclaration::vtblOffset() { return 1; } /**************************************** */ const char *ClassDeclaration::kind() { return "class"; } /**************************************** */ void ClassDeclaration::addLocalClass(ClassDeclarations *aclasses) { aclasses->push(this); } /********************************* InterfaceDeclaration ****************************/ InterfaceDeclaration::InterfaceDeclaration(Loc loc, Identifier *id, BaseClasses *baseclasses) : ClassDeclaration(loc, id, baseclasses) { com = 0; cpp = 0; if (id == Id::IUnknown) // IUnknown is the root of all COM interfaces { com = 1; cpp = 1; // IUnknown is also a C++ interface } } Dsymbol *InterfaceDeclaration::syntaxCopy(Dsymbol *s) { InterfaceDeclaration *id; if (s) id = (InterfaceDeclaration *)s; else id = new InterfaceDeclaration(loc, ident, NULL); ClassDeclaration::syntaxCopy(id); return id; } void InterfaceDeclaration::semantic(Scope *sc) { //printf("InterfaceDeclaration::semantic(%s), type = %p\n", toChars(), type); if (inuse) return; if (!sc) sc = scope; if (!parent && sc->parent && !sc->parent->isModule()) parent = sc->parent; type = type->semantic(loc, sc); handle = type; if (!members) // if forward reference { //printf("\tinterface '%s' is forward referenced\n", toChars()); return; } if (symtab) // if already done { if (!scope) return; } else symtab = new DsymbolTable(); Scope *scx = NULL; if (scope) { sc = scope; scx = scope; // save so we don't make redundant copies scope = NULL; } int errors = global.gaggedErrors; if (sc->stc & STCdeprecated) { isdeprecated = true; } userAttributes = sc->userAttributes; // Expand any tuples in baseclasses[] for (size_t i = 0; i < baseclasses->dim; ) { BaseClass *b = (*baseclasses)[i]; b->type = b->type->semantic(loc, sc); Type *tb = b->type->toBasetype(); if (tb->ty == Ttuple) { TypeTuple *tup = (TypeTuple *)tb; enum PROT protection = b->protection; baseclasses->remove(i); size_t dim = Parameter::dim(tup->arguments); for (size_t j = 0; j < dim; j++) { Parameter *arg = Parameter::getNth(tup->arguments, j); b = new BaseClass(arg->type, protection); baseclasses->insert(i + j, b); } } else i++; } if (!baseclasses->dim && sc->linkage == LINKcpp) cpp = 1; // Check for errors, handle forward references for (size_t i = 0; i < baseclasses->dim; ) { TypeClass *tc; BaseClass *b; Type *tb; b = (*baseclasses)[i]; b->type = b->type->semantic(loc, sc); tb = b->type->toBasetype(); if (tb->ty == Tclass) tc = (TypeClass *)tb; else tc = NULL; if (!tc || !tc->sym->isInterfaceDeclaration()) { if (b->type != Type::terror) error("base type must be interface, not %s", b->type->toChars()); baseclasses->remove(i); continue; } else { // Check for duplicate interfaces for (size_t j = 0; j < i; j++) { BaseClass *b2 = (*baseclasses)[j]; if (b2->base == tc->sym) error("inherits from duplicate interface %s", b2->base->toChars()); } b->base = tc->sym; if (b->base == this || isBaseOf2(b->base)) { error("circular inheritance of interface"); baseclasses->remove(i); continue; } if (!b->base->symtab) { // Try to resolve forward reference if (sc->mustsemantic && b->base->scope) b->base->semantic(NULL); } if (!b->base->symtab || b->base->scope || b->base->inuse) { //error("forward reference of base class %s", baseClass->toChars()); // Forward reference of base, try again later //printf("\ttry later, forward reference of base %s\n", b->base->toChars()); scope = scx ? scx : new Scope(*sc); scope->setNoFree(); scope->module->addDeferredSemantic(this); return; } } #if 0 // Inherit const/invariant from base class storage_class |= b->base->storage_class & STC_TYPECTOR; #endif i++; } interfaces_dim = baseclasses->dim; interfaces = baseclasses->tdata(); interfaceSemantic(sc); if (vtblOffset()) vtbl.push(this); // leave room at vtbl[0] for classinfo // Cat together the vtbl[]'s from base interfaces for (size_t i = 0; i < interfaces_dim; i++) { BaseClass *b = interfaces[i]; // Skip if b has already appeared for (size_t k = 0; k < i; k++) { if (b == interfaces[k]) goto Lcontinue; } // Copy vtbl[] from base class if (b->base->vtblOffset()) { size_t d = b->base->vtbl.dim; if (d > 1) { vtbl.reserve(d - 1); for (size_t j = 1; j < d; j++) vtbl.push(b->base->vtbl[j]); } } else { vtbl.append(&b->base->vtbl); } Lcontinue: ; } protection = sc->protection; storage_class |= sc->stc & STC_TYPECTOR; for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->addMember(sc, this, 1); } sc = sc->push(this); sc->stc &= STCsafe | STCtrusted | STCsystem; sc->parent = this; if (isCOMinterface()) sc->linkage = LINKwindows; else if (isCPPinterface()) sc->linkage = LINKcpp; sc->structalign = STRUCTALIGN_DEFAULT; sc->protection = PROTpublic; sc->explicitProtection = 0; // structalign = sc->structalign; sc->offset = PTRSIZE * 2; sc->userAttributes = NULL; structsize = sc->offset; inuse++; /* Set scope so if there are forward references, we still might be able to * resolve individual members like enums. */ for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; /* There are problems doing this in the general case because * Scope keeps track of things like 'offset' */ if (s->isEnumDeclaration() || (s->isAggregateDeclaration() && s->ident)) { //printf("setScope %s %s\n", s->kind(), s->toChars()); s->setScope(sc); } } for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->semantic(sc); } if (global.gag && global.gaggedErrors != errors) { // The type is no good, yet the error messages were gagged. type = Type::terror; } inuse--; //members->print(); sc->pop(); //printf("-InterfaceDeclaration::semantic(%s), type = %p\n", toChars(), type); } /******************************************* * Determine if 'this' is a base class of cd. * (Actually, if it is an interface supported by cd) * Output: * *poffset offset to start of class * OFFSET_RUNTIME must determine offset at runtime * Returns: * 0 not a base * 1 is a base */ int InterfaceDeclaration::isBaseOf(ClassDeclaration *cd, int *poffset) { //printf("%s.InterfaceDeclaration::isBaseOf(cd = '%s')\n", toChars(), cd->toChars()); assert(!baseClass); for (size_t j = 0; j < cd->interfaces_dim; j++) { BaseClass *b = cd->interfaces[j]; //printf("\tbase %s\n", b->base->toChars()); if (this == b->base) { //printf("\tfound at offset %d\n", b->offset); if (poffset) { *poffset = b->offset; if (j && cd->isInterfaceDeclaration()) *poffset = OFFSET_RUNTIME; } return 1; } if (isBaseOf(b, poffset)) { if (j && poffset && cd->isInterfaceDeclaration()) *poffset = OFFSET_RUNTIME; return 1; } } if (cd->baseClass && isBaseOf(cd->baseClass, poffset)) return 1; if (poffset) *poffset = 0; return 0; } int InterfaceDeclaration::isBaseOf(BaseClass *bc, int *poffset) { //printf("%s.InterfaceDeclaration::isBaseOf(bc = '%s')\n", toChars(), bc->base->toChars()); for (size_t j = 0; j < bc->baseInterfaces_dim; j++) { BaseClass *b = &bc->baseInterfaces[j]; if (this == b->base) { if (poffset) { *poffset = b->offset; if (j && bc->base->isInterfaceDeclaration()) *poffset = OFFSET_RUNTIME; } return 1; } if (isBaseOf(b, poffset)) { if (j && poffset && bc->base->isInterfaceDeclaration()) *poffset = OFFSET_RUNTIME; return 1; } } if (poffset) *poffset = 0; return 0; } /********************************************* * Determine if 'this' has clomplete base class information. * This is used to detect forward references in covariant overloads. */ int InterfaceDeclaration::isBaseInfoComplete() { assert(!baseClass); for (size_t i = 0; i < baseclasses->dim; i++) { BaseClass *b = (*baseclasses)[i]; if (!b->base || !b->base->isBaseInfoComplete ()) return 0; } return 1; } /**************************************** * Determine if slot 0 of the vtbl[] is reserved for something else. * For class objects, yes, this is where the ClassInfo ptr goes. * For COM interfaces, no. * For non-COM interfaces, yes, this is where the Interface ptr goes. */ int InterfaceDeclaration::vtblOffset() { if (isCOMinterface() || isCPPinterface()) return 0; return 1; } int InterfaceDeclaration::isCOMinterface() { return com; } #if DMDV2 int InterfaceDeclaration::isCPPinterface() { return cpp; } #endif /******************************************* */ const char *InterfaceDeclaration::kind() { return "interface"; } /******************************** BaseClass *****************************/ BaseClass::BaseClass() { memset(this, 0, sizeof(BaseClass)); } BaseClass::BaseClass(Type *type, enum PROT protection) { //printf("BaseClass(this = %p, '%s')\n", this, type->toChars()); this->type = type; this->protection = protection; base = NULL; offset = 0; baseInterfaces_dim = 0; baseInterfaces = NULL; } /**************************************** * Fill in vtbl[] for base class based on member functions of class cd. * Input: * vtbl if !=NULL, fill it in * newinstance !=0 means all entries must be filled in by members * of cd, not members of any base classes of cd. * Returns: * !=0 if any entries were filled in by members of cd (not exclusively * by base classes) */ int BaseClass::fillVtbl(ClassDeclaration *cd, FuncDeclarations *vtbl, int newinstance) { ClassDeclaration *id = base; int result = 0; //printf("BaseClass::fillVtbl(this='%s', cd='%s')\n", base->toChars(), cd->toChars()); if (vtbl) vtbl->setDim(base->vtbl.dim); // first entry is ClassInfo reference for (size_t j = base->vtblOffset(); j < base->vtbl.dim; j++) { FuncDeclaration *ifd = base->vtbl[j]->isFuncDeclaration(); FuncDeclaration *fd; TypeFunction *tf; //printf(" vtbl[%d] is '%s'\n", j, ifd ? ifd->toChars() : "null"); assert(ifd); // Find corresponding function in this class tf = (ifd->type->ty == Tfunction) ? (TypeFunction *)(ifd->type) : NULL; fd = cd->findFunc(ifd->ident, tf); if (fd && !fd->isAbstract()) { //printf(" found\n"); // Check that calling conventions match if (fd->linkage != ifd->linkage) fd->error("linkage doesn't match interface function"); // Check that it is current if (newinstance && fd->toParent() != cd && ifd->toParent() == base) cd->error("interface function %s.%s is not implemented", id->toChars(), ifd->ident->toChars()); if (fd->toParent() == cd) result = 1; } else { //printf(" not found\n"); // BUG: should mark this class as abstract? if (!cd->isAbstract()) cd->error("interface function %s.%s isn't implemented", id->toChars(), ifd->ident->toChars()); fd = NULL; } if (vtbl) (*vtbl)[j] = fd; } return result; } void BaseClass::copyBaseInterfaces(BaseClasses *vtblInterfaces) { //printf("+copyBaseInterfaces(), %s\n", base->toChars()); // if (baseInterfaces_dim) // return; baseInterfaces_dim = base->interfaces_dim; baseInterfaces = (BaseClass *)mem.calloc(baseInterfaces_dim, sizeof(BaseClass)); //printf("%s.copyBaseInterfaces()\n", base->toChars()); for (size_t i = 0; i < baseInterfaces_dim; i++) { BaseClass *b = &baseInterfaces[i]; BaseClass *b2 = base->interfaces[i]; assert(b2->vtbl.dim == 0); // should not be filled yet memcpy(b, b2, sizeof(BaseClass)); if (i) // single inheritance is i==0 vtblInterfaces->push(b); // only need for M.I. b->copyBaseInterfaces(vtblInterfaces); } //printf("-copyBaseInterfaces\n"); }