/* Compiler implementation of the D programming language * Copyright (C) 1999-2019 by The D Language Foundation, All Rights Reserved * written by Walter Bright * http://www.digitalmars.com * Distributed under the Boost Software License, Version 1.0. * http://www.boost.org/LICENSE_1_0.txt * https://github.com/D-Programming-Language/dmd/blob/master/src/class.c */ #include "root/dsystem.h" // mem{cpy|set}() #include "root/root.h" #include "root/rmem.h" #include "errors.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" #include "target.h" #include "objc.h" bool symbolIsVisible(Dsymbol *origin, Dsymbol *s); Objc *objc(); /********************************* ClassDeclaration ****************************/ ClassDeclaration *ClassDeclaration::object; ClassDeclaration *ClassDeclaration::throwable; ClassDeclaration *ClassDeclaration::exception; ClassDeclaration *ClassDeclaration::errorException; ClassDeclaration *ClassDeclaration::cpp_type_info_ptr; // Object.__cpp_type_info_ptr ClassDeclaration::ClassDeclaration(Loc loc, Identifier *id, BaseClasses *baseclasses, Dsymbols *members, bool inObject) : AggregateDeclaration(loc, id ? id : Identifier::generateId("__anonclass")) { static const 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(); this->members = members; baseClass = NULL; interfaces.length = 0; interfaces.ptr = NULL; vtblInterfaces = NULL; //printf("ClassDeclaration(%s), dim = %d\n", id->toChars(), this->baseclasses->dim); // For forward references type = new TypeClass(this); staticCtor = NULL; staticDtor = NULL; vtblsym = NULL; 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 (!inObject) error("%s", msg); Type::dtypeinfo = this; } if (id == Id::TypeInfo_Class) { if (!inObject) error("%s", msg); Type::typeinfoclass = this; } if (id == Id::TypeInfo_Interface) { if (!inObject) error("%s", msg); Type::typeinfointerface = this; } if (id == Id::TypeInfo_Struct) { if (!inObject) error("%s", msg); Type::typeinfostruct = this; } if (id == Id::TypeInfo_Pointer) { if (!inObject) error("%s", msg); Type::typeinfopointer = this; } if (id == Id::TypeInfo_Array) { if (!inObject) error("%s", msg); Type::typeinfoarray = this; } if (id == Id::TypeInfo_StaticArray) { //if (!inObject) // Type::typeinfostaticarray->error("%s", msg); Type::typeinfostaticarray = this; } if (id == Id::TypeInfo_AssociativeArray) { if (!inObject) error("%s", msg); Type::typeinfoassociativearray = this; } if (id == Id::TypeInfo_Enum) { if (!inObject) error("%s", msg); Type::typeinfoenum = this; } if (id == Id::TypeInfo_Function) { if (!inObject) error("%s", msg); Type::typeinfofunction = this; } if (id == Id::TypeInfo_Delegate) { if (!inObject) error("%s", msg); Type::typeinfodelegate = this; } if (id == Id::TypeInfo_Tuple) { if (!inObject) error("%s", msg); Type::typeinfotypelist = this; } if (id == Id::TypeInfo_Const) { if (!inObject) error("%s", msg); Type::typeinfoconst = this; } if (id == Id::TypeInfo_Invariant) { if (!inObject) error("%s", msg); Type::typeinfoinvariant = this; } if (id == Id::TypeInfo_Shared) { if (!inObject) error("%s", msg); Type::typeinfoshared = this; } if (id == Id::TypeInfo_Wild) { if (!inObject) error("%s", msg); Type::typeinfowild = this; } if (id == Id::TypeInfo_Vector) { if (!inObject) error("%s", msg); Type::typeinfovector = this; } } if (id == Id::Object) { if (!inObject) error("%s", msg); object = this; } if (id == Id::Throwable) { if (!inObject) error("%s", msg); throwable = this; } if (id == Id::Exception) { if (!inObject) error("%s", msg); exception = this; } if (id == Id::Error) { if (!inObject) error("%s", msg); errorException = this; } if (id == Id::cpp_type_info_ptr) { if (!inObject) error("%s", msg); cpp_type_info_ptr = this; } } com = false; isscope = false; isabstract = ABSfwdref; inuse = 0; baseok = BASEOKnone; cpp_type_info_ptr_sym = NULL; } ClassDeclaration *ClassDeclaration::create(Loc loc, Identifier *id, BaseClasses *baseclasses, Dsymbols *members, bool inObject) { return new ClassDeclaration(loc, id, baseclasses, members, inObject); } Dsymbol *ClassDeclaration::syntaxCopy(Dsymbol *s) { //printf("ClassDeclaration::syntaxCopy('%s')\n", toChars()); ClassDeclaration *cd = s ? (ClassDeclaration *)s : new ClassDeclaration(loc, ident, NULL, NULL, false); 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()); (*cd->baseclasses)[i] = b2; } return ScopeDsymbol::syntaxCopy(cd); } Scope *ClassDeclaration::newScope(Scope *sc) { Scope *sc2 = AggregateDeclaration::newScope(sc); if (isCOMclass()) { if (global.params.isWindows) sc2->linkage = LINKwindows; else { /* This enables us to use COM objects under Linux and * work with things like XPCOM */ sc2->linkage = LINKc; } } return sc2; } /* Bugzilla 12078, 12143 and 15733: * While resolving base classes and interfaces, a base may refer * the member of this derived class. In that time, if all bases of * this class can be determined, we can go forward the semantc process * beyond the Lancestorsdone. To do the recursive semantic analysis, * temporarily set and unset `_scope` around exp(). */ static Type *resolveBase(ClassDeclaration *cd, Scope *sc, Scope *&scx, Type *type) { if (!scx) { scx = sc->copy(); scx->setNoFree(); } cd->_scope = scx; Type *t = type->semantic(cd->loc, sc); cd->_scope = NULL; return t; } static void resolveBase(ClassDeclaration *cd, Scope *sc, Scope *&scx, ClassDeclaration *sym) { if (!scx) { scx = sc->copy(); scx->setNoFree(); } cd->_scope = scx; sym->semantic(NULL); cd->_scope = NULL; } static void badObjectDotD(ClassDeclaration *cd) { cd->error("missing or corrupt object.d"); fatal(); } 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 (semanticRun >= PASSsemanticdone) return; unsigned errors = global.errors; //printf("+ClassDeclaration::semantic(%s), type = %p, sizeok = %d, this = %p\n", toChars(), type, sizeok, this); Scope *scx = NULL; if (_scope) { sc = _scope; scx = _scope; // save so we don't make redundant copies _scope = NULL; } if (!parent) { assert(sc->parent); parent = sc->parent; } if (this->errors) type = Type::terror; type = type->semantic(loc, sc); if (type->ty == Tclass && ((TypeClass *)type)->sym != this) { TemplateInstance *ti = ((TypeClass *)type)->sym->isInstantiated(); if (ti && isError(ti)) ((TypeClass *)type)->sym = this; } // Ungag errors when not speculative Ungag ungag = ungagSpeculative(); if (semanticRun == PASSinit) { protection = sc->protection; storage_class |= sc->stc; if (storage_class & STCdeprecated) isdeprecated = true; 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 = true; if (storage_class & STCabstract) isabstract = ABSyes; userAttribDecl = sc->userAttribDecl; if (sc->linkage == LINKcpp) classKind = ClassKind::cpp; if (sc->linkage == LINKobjc) objc()->setObjc(this); } else if (symtab && !scx) { return; } semanticRun = PASSsemantic; if (baseok < BASEOKdone) { baseok = BASEOKin; // Expand any tuples in baseclasses[] for (size_t i = 0; i < baseclasses->dim; ) { BaseClass *b = (*baseclasses)[i]; b->type = resolveBase(this, sc, scx, b->type); Type *tb = b->type->toBasetype(); if (tb->ty == Ttuple) { TypeTuple *tup = (TypeTuple *)tb; 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); baseclasses->insert(i + j, b); } } else i++; } if (baseok >= BASEOKdone) { //printf("%s already semantic analyzed, semanticRun = %d\n", toChars(), semanticRun); if (semanticRun >= PASSsemanticdone) return; goto Lancestorsdone; } // See if there's a base class as first in baseclasses[] if (baseclasses->dim) { BaseClass *b = (*baseclasses)[0]; Type *tb = b->type->toBasetype(); TypeClass *tc = (tb->ty == Tclass) ? (TypeClass *)tb : NULL; if (!tc) { if (b->type != Type::terror) error("base type must be class or interface, not %s", b->type->toChars()); baseclasses->remove(0); goto L7; } 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()) goto L7; for (ClassDeclaration *cdb = tc->sym; cdb; cdb = cdb->baseClass) { if (cdb == this) { error("circular inheritance"); baseclasses->remove(0); goto L7; } } /* Bugzilla 11034: Essentially, class inheritance hierarchy * and instance size of each classes are orthogonal information. * Therefore, even if tc->sym->sizeof == SIZEOKnone, * we need to set baseClass field for class covariance check. */ baseClass = tc->sym; b->sym = baseClass; if (tc->sym->baseok < BASEOKdone) resolveBase(this, sc, scx, tc->sym); // Try to resolve forward reference if (tc->sym->baseok < BASEOKdone) { //printf("\ttry later, forward reference of base class %s\n", tc->sym->toChars()); if (tc->sym->_scope) tc->sym->_scope->_module->addDeferredSemantic(tc->sym); baseok = BASEOKnone; } 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; ) { BaseClass *b = (*baseclasses)[i]; Type *tb = b->type->toBasetype(); TypeClass *tc = (tb->ty == Tclass) ? (TypeClass *)tb : 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; } // Check for duplicate interfaces for (size_t j = (baseClass ? 1 : 0); j < i; j++) { BaseClass *b2 = (*baseclasses)[j]; if (b2->sym == tc->sym) { error("inherits from duplicate interface %s", b2->sym->toChars()); baseclasses->remove(i); continue; } } if (tc->sym->isDeprecated()) { if (!isDeprecated()) { // Deriving from deprecated class makes this one deprecated too isdeprecated = true; tc->checkDeprecated(loc, sc); } } b->sym = tc->sym; if (tc->sym->baseok < BASEOKdone) resolveBase(this, sc, scx, tc->sym); // Try to resolve forward reference if (tc->sym->baseok < BASEOKdone) { //printf("\ttry later, forward reference of base %s\n", tc->sym->toChars()); if (tc->sym->_scope) tc->sym->_scope->_module->addDeferredSemantic(tc->sym); baseok = BASEOKnone; } i++; } if (baseok == BASEOKnone) { // Forward referencee of one or more bases, try again later _scope = scx ? scx : sc->copy(); _scope->setNoFree(); _scope->_module->addDeferredSemantic(this); //printf("\tL%d semantic('%s') failed due to forward references\n", __LINE__, toChars()); return; } baseok = BASEOKdone; // If no base class, and this is not an Object, use Object as base class if (!baseClass && ident != Id::Object && !isCPPclass()) { if (!object || object->errors) badObjectDotD(this); Type *t = object->type; t = t->semantic(loc, sc)->toBasetype(); if (t->ty == Terror) badObjectDotD(this); assert(t->ty == Tclass); TypeClass *tc = (TypeClass *)t; BaseClass *b = new BaseClass(tc); baseclasses->shift(b); baseClass = tc->sym; assert(!baseClass->isInterfaceDeclaration()); b->sym = baseClass; } if (baseClass) { if (baseClass->storage_class & STCfinal) error("cannot inherit from final class %s", baseClass->toChars()); // Inherit properties from base class if (baseClass->isCOMclass()) com = true; if (baseClass->isCPPclass()) classKind = ClassKind::cpp; if (baseClass->isscope) isscope = true; enclosing = baseClass->enclosing; storage_class |= baseClass->storage_class & STC_TYPECTOR; } interfaces.length = baseclasses->dim - (baseClass ? 1 : 0); interfaces.ptr = baseclasses->tdata() + (baseClass ? 1 : 0); for (size_t i = 0; i < interfaces.length; i++) { BaseClass *b = interfaces.ptr[i]; // If this is an interface, and it derives from a COM interface, // then this is a COM interface too. if (b->sym->isCOMinterface()) com = true; if (isCPPclass() && !b->sym->isCPPinterface()) { ::error(loc, "C++ class '%s' cannot implement D interface '%s'", toPrettyChars(), b->sym->toPrettyChars()); } } interfaceSemantic(sc); } Lancestorsdone: //printf("\tClassDeclaration::semantic(%s) baseok = %d\n", toChars(), baseok); if (!members) // if opaque declaration { semanticRun = PASSsemanticdone; return; } if (!symtab) { symtab = new DsymbolTable(); /* Bugzilla 12152: The semantic analysis of base classes should be finished * before the members semantic analysis of this class, in order to determine * vtbl in this class. However if a base class refers the member of this class, * it can be resolved as a normal forward reference. * Call addMember() and setScope() to make this class members visible from the base classes. */ for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->addMember(sc, this); } Scope *sc2 = newScope(sc); /* 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]; //printf("[%d] setScope %s %s, sc2 = %p\n", i, s->kind(), s->toChars(), sc2); s->setScope(sc2); } sc2->pop(); } for (size_t i = 0; i < baseclasses->dim; i++) { BaseClass *b = (*baseclasses)[i]; Type *tb = b->type->toBasetype(); assert(tb->ty == Tclass); TypeClass *tc = (TypeClass *)tb; if (tc->sym->semanticRun < PASSsemanticdone) { // Forward referencee of one or more bases, try again later _scope = scx ? scx : sc->copy(); _scope->setNoFree(); if (tc->sym->_scope) tc->sym->_scope->_module->addDeferredSemantic(tc->sym); _scope->_module->addDeferredSemantic(this); //printf("\tL%d semantic('%s') failed due to forward references\n", __LINE__, toChars()); return; } } if (baseok == BASEOKdone) { baseok = BASEOKsemanticdone; // initialize vtbl if (baseClass) { if (isCPPclass() && baseClass->vtbl.dim == 0) { error("C++ base class %s needs at least one virtual function", baseClass->toChars()); } // Copy vtbl[] from base class vtbl.setDim(baseClass->vtbl.dim); memcpy(vtbl.tdata(), baseClass->vtbl.tdata(), sizeof(void *) * vtbl.dim); vthis = baseClass->vthis; } else { // No base class, so this is the root of the class hierarchy vtbl.setDim(0); if (vtblOffset()) vtbl.push(this); // leave room for classinfo as first member } /* 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 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()); } enclosing = NULL; } } else makeNested(); } Scope *sc2 = newScope(sc); for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->importAll(sc2); } // Note that members.dim can grow due to tuple expansion during semantic() for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->semantic(sc2); } if (!determineFields()) { assert(type == Type::terror); sc2->pop(); return; } /* Following special member functions creation needs semantic analysis * completion of sub-structs in each field types. */ for (size_t i = 0; i < fields.dim; i++) { VarDeclaration *v = fields[i]; Type *tb = v->type->baseElemOf(); if (tb->ty != Tstruct) continue; StructDeclaration *sd = ((TypeStruct *)tb)->sym; if (sd->semanticRun >= PASSsemanticdone) continue; sc2->pop(); _scope = scx ? scx : sc->copy(); _scope->setNoFree(); _scope->_module->addDeferredSemantic(this); //printf("\tdeferring %s\n", toChars()); return; } /* Look for special member functions. * They must be in this class, not in a base class. */ // Can be in base class aggNew = (NewDeclaration *)search(Loc(), Id::classNew); aggDelete = (DeleteDeclaration *)search(Loc(), Id::classDelete); // Look for the constructor ctor = searchCtor(); if (!ctor && noDefaultCtor) { // A class object is always created by constructor, so this check is legitimate. for (size_t i = 0; i < fields.dim; i++) { VarDeclaration *v = fields[i]; if (v->storage_class & STCnodefaultctor) ::error(v->loc, "field %s must be initialized in constructor", v->toChars()); } } // If this class has no constructor, but base class has a default // ctor, create a constructor: // this() { } if (!ctor && baseClass && baseClass->ctor) { FuncDeclaration *fd = resolveFuncCall(loc, sc2, baseClass->ctor, NULL, type, NULL, 1); if (!fd) // try shared base ctor instead fd = resolveFuncCall(loc, sc2, baseClass->ctor, NULL, type->sharedOf(), NULL, 1); if (fd && !fd->errors) { //printf("Creating default this(){} for class %s\n", toChars()); TypeFunction *btf = fd->type->toTypeFunction(); TypeFunction *tf = new TypeFunction(NULL, NULL, 0, LINKd, fd->storage_class); tf->mod = btf->mod; tf->purity = btf->purity; tf->isnothrow = btf->isnothrow; tf->isnogc = btf->isnogc; tf->trust = btf->trust; CtorDeclaration *ctor = new CtorDeclaration(loc, Loc(), 0, tf); ctor->fbody = new CompoundStatement(Loc(), new Statements()); members->push(ctor); ctor->addMember(sc, this); ctor->semantic(sc2); this->ctor = ctor; defaultCtor = ctor; } else { error("cannot implicitly generate a default ctor when base class %s is missing a default ctor", baseClass->toPrettyChars()); } } dtor = buildDtor(this, sc2); if (FuncDeclaration *f = hasIdentityOpAssign(this, sc2)) { if (!(f->storage_class & STCdisable)) error(f->loc, "identity assignment operator overload is illegal"); } inv = buildInv(this, sc2); Module::dprogress++; semanticRun = PASSsemanticdone; //printf("-ClassDeclaration.semantic(%s), type = %p\n", toChars(), type); //members.print(); sc2->pop(); if (type->ty == Tclass && ((TypeClass *)type)->sym != this) { // https://issues.dlang.org/show_bug.cgi?id=17492 ClassDeclaration *cd = ((TypeClass *)type)->sym; error("already exists at %s. Perhaps in another function with the same name?", cd->loc.toChars()); } if (global.errors != errors) { // The type is no good. type = Type::terror; this->errors = true; if (deferred) deferred->errors = true; } // Verify fields of a synchronized class are not public if (storage_class & STCsynchronized) { for (size_t i = 0; i < fields.dim; i++) { VarDeclaration *vd = fields[i]; if (!vd->isThisDeclaration() && !vd->prot().isMoreRestrictiveThan(Prot(PROTpublic))) { vd->error("Field members of a synchronized class cannot be %s", protectionToChars(vd->prot().kind)); } } } if (deferred && !global.gag) { deferred->semantic2(sc); deferred->semantic3(sc); } //printf("-ClassDeclaration::semantic(%s), type = %p, sizeok = %d, this = %p\n", toChars(), type, sizeok, this); } /********************************************* * Determine if 'this' is a base class of cd. * This is used to detect circular inheritance only. */ bool ClassDeclaration::isBaseOf2(ClassDeclaration *cd) { if (!cd) return false; //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->sym == this || isBaseOf2(b->sym)) return true; } return false; } /******************************************* * Determine if 'this' is a base class of cd. */ bool 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->semanticRun < PASSsemanticdone && !cd->isInterfaceDeclaration()) { cd->semantic(NULL); if (!cd->baseClass && cd->semanticRun < PASSsemanticdone) cd->error("base class is forward referenced by %s", toChars()); } if (this == cd->baseClass) return true; cd = cd->baseClass; } return false; } /********************************************* * Determine if 'this' has complete base class information. * This is used to detect forward references in covariant overloads. */ bool ClassDeclaration::isBaseInfoComplete() { return baseok >= BASEOKdone; } Dsymbol *ClassDeclaration::search(const Loc &loc, Identifier *ident, int flags) { //printf("%s.ClassDeclaration::search('%s', flags=x%x)\n", toChars(), ident->toChars(), flags); //if (_scope) printf("%s baseok = %d\n", toChars(), baseok); if (_scope && baseok < BASEOKdone) { if (!inuse) { // must semantic on base class/interfaces ++inuse; semantic(NULL); --inuse; } } if (!members || !symtab) // opaque or addMember is not yet done { error("is forward referenced when looking for '%s'", ident->toChars()); //*(char*)0=0; return NULL; } Dsymbol *s = ScopeDsymbol::search(loc, ident, flags); // don't search imports of base classes if (flags & SearchImportsOnly) return s; 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->sym) { if (!b->sym->symtab) error("base %s is forward referenced", b->sym->ident->toChars()); else { s = b->sym->search(loc, ident, flags); if (!s) continue; else if (s == this) // happens if s is nested in this and derives from this s = NULL; else if (!(flags & IgnoreSymbolVisibility) && !(s->prot().kind == PROTprotected) && !symbolIsVisible(this, s)) s = NULL; else break; } } } } return s; } /************************************ * Search base classes in depth-first, left-to-right order for * a class or interface named 'ident'. * Stops at first found. Does not look for additional matches. * Params: * ident = identifier to search for * Returns: * ClassDeclaration if found, null if not */ ClassDeclaration *ClassDeclaration::searchBase(Identifier *ident) { for (size_t i = 0; i < baseclasses->dim; i++) { BaseClass *b = (*baseclasses)[i]; ClassDeclaration *cdb = b->type->isClassHandle(); if (!cdb) // Bugzilla 10616 return NULL; if (cdb->ident->equals(ident)) return cdb; cdb = cdb->searchBase(ident); if (cdb) return cdb; } return NULL; } /**** * Runs through the inheritance graph to set the BaseClass.offset fields. * Recursive in order to account for the size of the interface classes, if they are * more than just interfaces. * Params: * cd = interface to look at * baseOffset = offset of where cd will be placed * Returns: * subset of instantiated size used by cd for interfaces */ static unsigned membersPlace(BaseClasses *vtblInterfaces, size_t &bi, ClassDeclaration *cd, unsigned baseOffset) { //printf(" membersPlace(%s, %d)\n", cd->toChars(), baseOffset); unsigned offset = baseOffset; for (size_t i = 0; i < cd->interfaces.length; i++) { BaseClass *b = cd->interfaces.ptr[i]; if (b->sym->sizeok != SIZEOKdone) b->sym->finalizeSize(); assert(b->sym->sizeok == SIZEOKdone); if (!b->sym->alignsize) b->sym->alignsize = Target::ptrsize; cd->alignmember(b->sym->alignsize, b->sym->alignsize, &offset); assert(bi < vtblInterfaces->dim); BaseClass *bv = (*vtblInterfaces)[bi]; if (b->sym->interfaces.length == 0) { //printf("\tvtblInterfaces[%d] b=%p b->sym = %s, offset = %d\n", bi, bv, bv->sym->toChars(), offset); bv->offset = offset; ++bi; // All the base interfaces down the left side share the same offset for (BaseClass *b2 = bv; b2->baseInterfaces.length; ) { b2 = &b2->baseInterfaces.ptr[0]; b2->offset = offset; //printf("\tvtblInterfaces[%d] b=%p sym = %s, offset = %d\n", bi, b2, b2->sym->toChars(), b2->offset); } } membersPlace(vtblInterfaces, bi, b->sym, offset); //printf(" %s size = %d\n", b->sym->toChars(), b->sym->structsize); offset += b->sym->structsize; if (cd->alignsize < b->sym->alignsize) cd->alignsize = b->sym->alignsize; } return offset - baseOffset; } void ClassDeclaration::finalizeSize() { assert(sizeok != SIZEOKdone); // Set the offsets of the fields and determine the size of the class if (baseClass) { assert(baseClass->sizeok == SIZEOKdone); alignsize = baseClass->alignsize; structsize = baseClass->structsize; if (isCPPclass() && global.params.isWindows) structsize = (structsize + alignsize - 1) & ~(alignsize - 1); } else if (isInterfaceDeclaration()) { if (interfaces.length == 0) { alignsize = Target::ptrsize; structsize = Target::ptrsize; // allow room for __vptr } } else { alignsize = Target::ptrsize; structsize = Target::ptrsize; // allow room for __vptr if (!isCPPclass()) structsize += Target::ptrsize; // allow room for __monitor } //printf("finalizeSize() %s, sizeok = %d\n", toChars(), sizeok); size_t bi = 0; // index into vtblInterfaces[] // Add vptr's for any interfaces implemented by this class structsize += membersPlace(vtblInterfaces, bi, this, structsize); if (isInterfaceDeclaration()) { sizeok = SIZEOKdone; return; } // FIXME: Currently setFieldOffset functions need to increase fields // to calculate each variable offsets. It can be improved later. fields.setDim(0); unsigned offset = structsize; for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->setFieldOffset(this, &offset, false); } sizeok = SIZEOKdone; // Calculate fields[i]->overlapped checkOverlappedFields(); } /********************************************************** * fd is in the vtbl[] for this class. * Return 1 if function is hidden (not findable through search). */ int isf(void *param, Dsymbol *s) { FuncDeclaration *fd = s->isFuncDeclaration(); if (!fd) return 0; //printf("param = %p, fd = %p %s\n", param, fd, fd->toChars()); return (RootObject *)param == fd; } bool ClassDeclaration::isFuncHidden(FuncDeclaration *fd) { //printf("ClassDeclaration::isFuncHidden(class = %s, fd = %s)\n", toChars(), fd->toPrettyChars()); Dsymbol *s = search(Loc(), fd->ident, IgnoreAmbiguous | IgnoreErrors); if (!s) { //printf("not found\n"); /* Because, due to a hack, if there are multiple definitions * of fd->ident, NULL is returned. */ return false; } 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, (void *)fd, &isf)) return false; } return true; } else { FuncDeclaration *fdstart = s->isFuncDeclaration(); //printf("%s fdstart = %p\n", s->kind(), fdstart); if (overloadApply(fdstart, (void *)fd, &isf)) return false; return !fd->parent->isTemplateMixin(); } } /**************** * 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 MATCH m1 = tf->equals(fd ->type) ? MATCHexact : MATCHnomatch; MATCH m2 = tf->equals(fdmatch->type) ? MATCHexact : MATCHnomatch; if (m1 > m2) goto Lfd; else if (m1 < m2) goto Lfdmatch; } { MATCH m1 = (tf->mod == fd ->type->mod) ? MATCHexact : MATCHnomatch; MATCH 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 MATCH m1 = fd ->parent->isClassDeclaration() ? MATCHexact : MATCHnomatch; MATCH m2 = fdmatch->parent->isClassDeclaration() ? MATCHexact : MATCHnomatch; if (m1 > m2) goto Lfd; else if (m1 < m2) goto Lfdmatch; } 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 *) { vtblInterfaces = new BaseClasses(); vtblInterfaces->reserve(interfaces.length); for (size_t i = 0; i < interfaces.length; i++) { BaseClass *b = interfaces.ptr[i]; vtblInterfaces->push(b); b->copyBaseInterfaces(vtblInterfaces); } } /**************************************** */ bool ClassDeclaration::isCOMclass() const { return com; } bool ClassDeclaration::isCOMinterface() const { return false; } bool ClassDeclaration::isCPPclass() const { return classKind == ClassKind::cpp; } bool ClassDeclaration::isCPPinterface() const { return false; } /**************************************** */ bool ClassDeclaration::isAbstract() { if (isabstract != ABSfwdref) return isabstract == ABSyes; /* Bugzilla 11169: Resolve forward references to all class member functions, * and determine whether this class is abstract. */ struct SearchAbstract { static int fp(Dsymbol *s, void *) { FuncDeclaration *fd = s->isFuncDeclaration(); if (!fd) return 0; if (fd->storage_class & STCstatic) return 0; if (fd->_scope) fd->semantic(NULL); if (fd->isAbstract()) return 1; return 0; } }; for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; if (s->apply(&SearchAbstract::fp, this)) { isabstract = ABSyes; return true; } } /* Iterate inherited member functions and check their abstract attribute. */ for (size_t i = 1; i < vtbl.dim; i++) { FuncDeclaration *fd = vtbl[i]->isFuncDeclaration(); //if (fd) printf("\tvtbl[%d] = [%s] %s\n", i, fd->loc.toChars(), fd->toChars()); if (!fd || fd->isAbstract()) { isabstract = ABSyes; return true; } } isabstract = ABSno; 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. * Returns: * 0 vtbl[0] is first virtual function pointer * 1 vtbl[0] is classinfo/interfaceinfo pointer */ int ClassDeclaration::vtblOffset() const { return classKind == ClassKind::cpp ? 0 : 1; } /**************************************** */ const char *ClassDeclaration::kind() const { return "class"; } /**************************************** */ void ClassDeclaration::addLocalClass(ClassDeclarations *aclasses) { aclasses->push(this); } /********************************* InterfaceDeclaration ****************************/ InterfaceDeclaration::InterfaceDeclaration(Loc loc, Identifier *id, BaseClasses *baseclasses) : ClassDeclaration(loc, id, baseclasses, NULL, false) { if (id == Id::IUnknown) // IUnknown is the root of all COM interfaces { com = true; classKind = ClassKind::cpp; // IUnknown is also a C++ interface } } Dsymbol *InterfaceDeclaration::syntaxCopy(Dsymbol *s) { InterfaceDeclaration *id = s ? (InterfaceDeclaration *)s : new InterfaceDeclaration(loc, ident, NULL); return ClassDeclaration::syntaxCopy(id); } Scope *InterfaceDeclaration::newScope(Scope *sc) { Scope *sc2 = ClassDeclaration::newScope(sc); if (com) sc2->linkage = LINKwindows; else if (classKind == ClassKind::cpp) sc2->linkage = LINKcpp; else if (classKind == ClassKind::objc) sc2->linkage = LINKobjc; return sc2; } void InterfaceDeclaration::semantic(Scope *sc) { //printf("InterfaceDeclaration::semantic(%s), type = %p\n", toChars(), type); if (semanticRun >= PASSsemanticdone) return; unsigned errors = global.errors; //printf("+InterfaceDeclaration.semantic(%s), type = %p\n", toChars(), type); Scope *scx = NULL; if (_scope) { sc = _scope; scx = _scope; // save so we don't make redundant copies _scope = NULL; } if (!parent) { assert(sc->parent && sc->func); parent = sc->parent; } assert(parent && !isAnonymous()); if (this->errors) type = Type::terror; type = type->semantic(loc, sc); if (type->ty == Tclass && ((TypeClass *)type)->sym != this) { TemplateInstance *ti = ((TypeClass *)type)->sym->isInstantiated(); if (ti && isError(ti)) ((TypeClass *)type)->sym = this; } // Ungag errors when not speculative Ungag ungag = ungagSpeculative(); if (semanticRun == PASSinit) { protection = sc->protection; storage_class |= sc->stc; if (storage_class & STCdeprecated) isdeprecated = true; userAttribDecl = sc->userAttribDecl; } else if (symtab) { if (sizeok == SIZEOKdone || !scx) { semanticRun = PASSsemanticdone; return; } } semanticRun = PASSsemantic; if (baseok < BASEOKdone) { baseok = BASEOKin; // Expand any tuples in baseclasses[] for (size_t i = 0; i < baseclasses->dim; ) { BaseClass *b = (*baseclasses)[i]; b->type = resolveBase(this, sc, scx, b->type); Type *tb = b->type->toBasetype(); if (tb->ty == Ttuple) { TypeTuple *tup = (TypeTuple *)tb; 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); baseclasses->insert(i + j, b); } } else i++; } if (baseok >= BASEOKdone) { //printf("%s already semantic analyzed, semanticRun = %d\n", toChars(), semanticRun); if (semanticRun >= PASSsemanticdone) return; goto Lancestorsdone; } if (!baseclasses->dim && sc->linkage == LINKcpp) classKind = ClassKind::cpp; if (sc->linkage == LINKobjc) objc()->setObjc(this); // Check for errors, handle forward references for (size_t i = 0; i < baseclasses->dim; ) { BaseClass *b = (*baseclasses)[i]; Type *tb = b->type->toBasetype(); TypeClass *tc = (tb->ty == Tclass) ? (TypeClass *)tb : 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; } // Check for duplicate interfaces for (size_t j = 0; j < i; j++) { BaseClass *b2 = (*baseclasses)[j]; if (b2->sym == tc->sym) { error("inherits from duplicate interface %s", b2->sym->toChars()); baseclasses->remove(i); continue; } } if (tc->sym == this || isBaseOf2(tc->sym)) { error("circular inheritance of interface"); baseclasses->remove(i); continue; } if (tc->sym->isDeprecated()) { if (!isDeprecated()) { // Deriving from deprecated class makes this one deprecated too isdeprecated = true; tc->checkDeprecated(loc, sc); } } b->sym = tc->sym; if (tc->sym->baseok < BASEOKdone) resolveBase(this, sc, scx, tc->sym); // Try to resolve forward reference if (tc->sym->baseok < BASEOKdone) { //printf("\ttry later, forward reference of base %s\n", tc->sym->toChars()); if (tc->sym->_scope) tc->sym->_scope->_module->addDeferredSemantic(tc->sym); baseok = BASEOKnone; } i++; } if (baseok == BASEOKnone) { // Forward referencee of one or more bases, try again later _scope = scx ? scx : sc->copy(); _scope->setNoFree(); _scope->_module->addDeferredSemantic(this); return; } baseok = BASEOKdone; interfaces.length = baseclasses->dim; interfaces.ptr = baseclasses->tdata(); for (size_t i = 0; i < interfaces.length; i++) { BaseClass *b = interfaces.ptr[i]; // If this is an interface, and it derives from a COM interface, // then this is a COM interface too. if (b->sym->isCOMinterface()) com = true; if (b->sym->isCPPinterface()) classKind = ClassKind::cpp; } interfaceSemantic(sc); } Lancestorsdone: if (!members) // if opaque declaration { semanticRun = PASSsemanticdone; return; } if (!symtab) symtab = new DsymbolTable(); for (size_t i = 0; i < baseclasses->dim; i++) { BaseClass *b = (*baseclasses)[i]; Type *tb = b->type->toBasetype(); assert(tb->ty == Tclass); TypeClass *tc = (TypeClass *)tb; if (tc->sym->semanticRun < PASSsemanticdone) { // Forward referencee of one or more bases, try again later _scope = scx ? scx : sc->copy(); _scope->setNoFree(); if (tc->sym->_scope) tc->sym->_scope->_module->addDeferredSemantic(tc->sym); _scope->_module->addDeferredSemantic(this); return; } } if (baseok == BASEOKdone) { baseok = BASEOKsemanticdone; // initialize vtbl 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.length; i++) { BaseClass *b = interfaces.ptr[i]; // Skip if b has already appeared for (size_t k = 0; k < i; k++) { if (b == interfaces.ptr[k]) goto Lcontinue; } // Copy vtbl[] from base class if (b->sym->vtblOffset()) { size_t d = b->sym->vtbl.dim; if (d > 1) { vtbl.reserve(d - 1); for (size_t j = 1; j < d; j++) vtbl.push(b->sym->vtbl[j]); } } else { vtbl.append(&b->sym->vtbl); } Lcontinue: ; } } for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->addMember(sc, this); } Scope *sc2 = newScope(sc); /* 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]; //printf("setScope %s %s\n", s->kind(), s->toChars()); s->setScope(sc2); } for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->importAll(sc2); } for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->semantic(sc2); } Module::dprogress++; semanticRun = PASSsemanticdone; //printf("-InterfaceDeclaration.semantic(%s), type = %p\n", toChars(), type); //members->print(); sc2->pop(); if (global.errors != errors) { // The type is no good. type = Type::terror; } assert(type->ty != Tclass || ((TypeClass *)type)->sym == this); } /******************************************* * 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: * false not a base * true is a base */ bool 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.length; j++) { BaseClass *b = cd->interfaces.ptr[j]; //printf("\tX base %s\n", b->sym->toChars()); if (this == b->sym) { //printf("\tfound at offset %d\n", b->offset); if (poffset) { // don't return incorrect offsets https://issues.dlang.org/show_bug.cgi?id=16980 *poffset = cd->sizeok == SIZEOKdone ? b->offset : OFFSET_FWDREF; } //printf("\tfound at offset %d\n", b->offset); return true; } if (isBaseOf(b, poffset)) return true; } if (cd->baseClass && isBaseOf(cd->baseClass, poffset)) return true; if (poffset) *poffset = 0; return false; } bool InterfaceDeclaration::isBaseOf(BaseClass *bc, int *poffset) { //printf("%s.InterfaceDeclaration::isBaseOf(bc = '%s')\n", toChars(), bc->sym->toChars()); for (size_t j = 0; j < bc->baseInterfaces.length; j++) { BaseClass *b = &bc->baseInterfaces.ptr[j]; //printf("\tY base %s\n", b->sym->toChars()); if (this == b->sym) { //printf("\tfound at offset %d\n", b->offset); if (poffset) { *poffset = b->offset; } return true; } if (isBaseOf(b, poffset)) { return true; } } if (poffset) *poffset = 0; 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 InterfaceDeclaration::vtblOffset() const { if (isCOMinterface() || isCPPinterface()) return 0; return 1; } bool InterfaceDeclaration::isCOMinterface() const { return com; } bool InterfaceDeclaration::isCPPinterface() const { return classKind == ClassKind::cpp; } /******************************************* */ const char *InterfaceDeclaration::kind() const { return "interface"; } /******************************** BaseClass *****************************/ BaseClass::BaseClass() { this->type = NULL; this->sym = NULL; this->offset = 0; this->baseInterfaces.length = 0; this->baseInterfaces.ptr = NULL; } BaseClass::BaseClass(Type *type) { //printf("BaseClass(this = %p, '%s')\n", this, type->toChars()); this->type = type; this->sym = NULL; this->offset = 0; this->baseInterfaces.length = 0; this->baseInterfaces.ptr = 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: * true if any entries were filled in by members of cd (not exclusively * by base classes) */ bool BaseClass::fillVtbl(ClassDeclaration *cd, FuncDeclarations *vtbl, int newinstance) { bool result = false; //printf("BaseClass::fillVtbl(this='%s', cd='%s')\n", sym->toChars(), cd->toChars()); if (vtbl) vtbl->setDim(sym->vtbl.dim); // first entry is ClassInfo reference for (size_t j = sym->vtblOffset(); j < sym->vtbl.dim; j++) { FuncDeclaration *ifd = sym->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->toTypeFunction(); 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 //printf("newinstance = %d fd->toParent() = %s ifd->toParent() = %s\n", //newinstance, fd->toParent()->toChars(), ifd->toParent()->toChars()); if (newinstance && fd->toParent() != cd && ifd->toParent() == sym) cd->error("interface function '%s' is not implemented", ifd->toFullSignature()); if (fd->toParent() == cd) result = true; } else { //printf(" not found %p\n", fd); // BUG: should mark this class as abstract? if (!cd->isAbstract()) cd->error("interface function '%s' is not implemented", ifd->toFullSignature()); fd = NULL; } if (vtbl) (*vtbl)[j] = fd; } return result; } void BaseClass::copyBaseInterfaces(BaseClasses *vtblInterfaces) { //printf("+copyBaseInterfaces(), %s\n", sym->toChars()); // if (baseInterfaces.length) // return; baseInterfaces.length = sym->interfaces.length; baseInterfaces.ptr = (BaseClass *)mem.xcalloc(baseInterfaces.length, sizeof(BaseClass)); //printf("%s.copyBaseInterfaces()\n", sym->toChars()); for (size_t i = 0; i < baseInterfaces.length; i++) { void *pb = &baseInterfaces.ptr[i]; BaseClass *b2 = sym->interfaces.ptr[i]; assert(b2->vtbl.dim == 0); // should not be filled yet BaseClass *b = (BaseClass *)memcpy(pb, b2, sizeof(BaseClass)); if (i) // single inheritance is i==0 vtblInterfaces->push(b); // only need for M.I. b->copyBaseInterfaces(vtblInterfaces); } //printf("-copyBaseInterfaces\n"); }