void ClassDeclaration::populateVTable() { if(vtable.size() > 0) return; //already populated if(base && base->getDeclaration() && base->getDeclaration()->classDeclaration()) { ClassDeclaration *bclass = base->getDeclaration()->classDeclaration(); bclass->populateVTable(); // copy in base members for(int i = 0; i < bclass->vtable.size(); i++) { vtable.push_back(bclass->vtable[i]); } } //XXX static methods are populated in vtable. // I like the idea, but I haven't tested it's validity yet for(int i = 0; i < methods.size(); i++) { for(int j = 0; j < vtable.size(); j++) { // if overridden method if(methods[i]->getName() == vtable[j]->getName() && methods[i]->getType()->coercesTo(vtable[j]->getType())) { //TODO: double check that function return type can be varient methods[i]->setVTableIndex(j); vtable[j] = methods[i]; goto CONTINUE; } } //not overridden, put at vtable end methods[i]->setVTableIndex(vtable.size()); vtable.push_back(methods[i]); CONTINUE:; } }
std::vector<llvm::Constant*> IrStruct::createClassDefaultInitializer() { ClassDeclaration* cd = aggrdecl->isClassDeclaration(); assert(cd && "invalid class aggregate"); IF_LOG Logger::println("Building class default initializer %s @ %s", cd->toPrettyChars(), cd->loc.toChars()); LOG_SCOPE; IF_LOG Logger::println("Instance size: %u", cd->structsize); // find the fields that contribute to the default initializer. // these will define the default type. std::vector<llvm::Constant*> constants; constants.reserve(32); // add vtbl constants.push_back(getVtblSymbol()); // add monitor constants.push_back(getNullValue(DtoType(Type::tvoid->pointerTo()))); // we start right after the vtbl and monitor size_t offset = PTRSIZE * 2; size_t field_index = 2; // add data members recursively addBaseClassInits(constants, cd, offset, field_index); // tail padding? if (offset < cd->structsize) add_zeros(constants, cd->structsize - offset); return constants; }
LLGlobalVariable * IrStruct::getInterfaceArraySymbol() { if (classInterfacesArray) return classInterfacesArray; ClassDeclaration* cd = aggrdecl->isClassDeclaration(); size_t n = stripModifiers(type)->irtype->isClass()->getNumInterfaceVtbls(); assert(n > 0 && "getting ClassInfo.interfaces storage symbol, but we " "don't implement any interfaces"); VarDeclarationIter idx(ClassDeclaration::classinfo->fields, 3); LLType* InterfaceTy = DtoType(idx->type->nextOf()); // create Interface[N] LLArrayType* array_type = llvm::ArrayType::get(InterfaceTy,n); // put it in a global std::string name("_D"); name.append(cd->mangle()); name.append("16__interfaceInfosZ"); llvm::GlobalValue::LinkageTypes _linkage = DtoExternalLinkage(aggrdecl); classInterfacesArray = new llvm::GlobalVariable(*gIR->module, array_type, true, _linkage, NULL, name); return classInterfacesArray; }
llvm::FunctionType* DtoBaseFunctionType(FuncDeclaration* fdecl) { Dsymbol* parent = fdecl->toParent(); ClassDeclaration* cd = parent->isClassDeclaration(); assert(cd); FuncDeclaration* f = fdecl; while (cd) { ClassDeclaration* base = cd->baseClass; if (!base) break; FuncDeclaration* f2 = base->findFunc(fdecl->ident, (TypeFunction*)fdecl->type); if (f2) { f = f2; cd = base; } else break; } DtoResolveDsymbol(f); return llvm::cast<llvm::FunctionType>(DtoType(f->type)); }
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; assert(tf); // should always be non-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%s isn't implemented", id->toChars(), ifd->ident->toChars(), Parameter::argsTypesToChars(tf->parameters, tf->varargs)); fd = NULL; } if (vtbl) (*vtbl)[j] = fd; } return result; }
void TypeInfoClassDeclaration::semantic3(Scope* sc) { TypeClass *tc = (TypeClass *)tinfo; ClassDeclaration *cd = tc->sym; // rebuild scope of sym, because the symbol is just added anyway if (Scope* sc = rebuildScope(cd)) { cd->generateTypeInfoData(sc); while(sc && !sc->nofree) sc = sc->pop(); } }
void ADLHelper::addBaseClasses(Declaration* declaration) { ClassDeclaration * classDecl = dynamic_cast<ClassDeclaration*>(declaration); if (classDecl) { int nBaseClassesCount = classDecl->baseClassesSize(); for (int i = 0; i < nBaseClassesCount; ++i) { const BaseClassInstance baseClass = classDecl->baseClasses()[i]; StructureType::Ptr type = baseClass.baseClass.type<StructureType>(); if (type) addAssociatedClass(type->declaration(m_topContext.data())); } } }
void DeclarationBuilder::visitClassDeclaration(IClassDeclaration *node) { inClassScope = true; DeclarationBuilderBase::visitClassDeclaration(node); if(node->getComment()) setComment(node->getComment()); DUChainWriteLocker lock; ClassDeclaration *dec = openDefinition<ClassDeclaration>(identifierForNode(node->getName()), editorFindRange(node->getName(), 0)); dec->setType(lastType()); dec->setKind(KDevelop::Declaration::Type); dec->setInternalContext(lastContext()); dec->setClassType(ClassDeclarationData::Class); closeDeclaration(); inClassScope = false; }
ClassDeclaration *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]; ClassDeclaration *cdb = b->type->isClassHandle(); if (cdb->ident->equals(ident)) return cdb; cdb = cdb->searchBase(loc, ident); if (cdb) return cdb; } return NULL; }
void accessCheck(Loc loc, Scope *sc, Expression *e, Declaration *d) { #if LOG if (e) { printf("accessCheck(%s . %s)\n", e->toChars(), d->toChars()); printf("\te->type = %s\n", e->type->toChars()); } else { //printf("accessCheck(%s)\n", d->toChars()); } #endif if (!e) { if (d->prot() == PROTprivate && d->getModule() != sc->module || d->prot() == PROTpackage && !hasPackageAccess(sc, d)) error(loc, "%s %s.%s is not accessible from %s", d->kind(), d->getModule()->toChars(), d->toChars(), sc->module->toChars()); } else if (e->type->ty == Tclass) { // Do access check ClassDeclaration *cd; cd = (ClassDeclaration *)(((TypeClass *)e->type)->sym); #if 1 if (e->op == TOKsuper) { ClassDeclaration *cd2; cd2 = sc->func->toParent()->isClassDeclaration(); if (cd2) cd = cd2; } #endif cd->accessCheck(loc, sc, d); } else if (e->type->ty == Tstruct) { // Do access check StructDeclaration *cd; cd = (StructDeclaration *)(((TypeStruct *)e->type)->sym); cd->accessCheck(loc, sc, d); } }
void test_visitors() { TestVisitor tv; Loc loc; Identifier *ident = Identifier::idPool("test"); IntegerExp *ie = IntegerExp::createi(loc, 42, Type::tint32); ie->accept(&tv); assert(tv.expr == true); IdentifierExp *id = IdentifierExp::create (loc, ident); id->accept(&tv); assert(tv.idexpr == true); Module *mod = Module::create("test", ident, 0, 0); mod->accept(&tv); assert(tv.package == true); ExpStatement *es = ExpStatement::create(loc, ie); es->accept(&tv); assert(tv.stmt == true); TypePointer *tp = TypePointer::create(Type::tvoid); tp->accept(&tv); assert(tv.type == true); LinkDeclaration *ld = LinkDeclaration::create(LINKd, NULL); ld->accept(&tv); assert(tv.attrib == true); ClassDeclaration *cd = ClassDeclaration::create(loc, Identifier::idPool("TypeInfo"), NULL, NULL, true); cd->accept(&tv); assert(tv.aggr = true); AliasDeclaration *ad = AliasDeclaration::create(loc, ident, tp); ad->accept(&tv); assert(tv.decl == true); cd = ClassDeclaration::create(loc, Identifier::idPool("TypeInfo_Pointer"), NULL, NULL, true); TypeInfoPointerDeclaration *ti = TypeInfoPointerDeclaration::create(tp); ti->accept(&tv); assert(tv.typeinfo == true); }
void TestDUChain::testBaseClasses() { TestFile file("class Base {}; class Inherited : public Base {};", "cpp"); QVERIFY(file.parseAndWait()); DUChainReadLocker lock; DUContext* top = file.topContext().data(); QVERIFY(top); QCOMPARE(top->localDeclarations().count(), 2); Declaration* baseDecl = top->localDeclarations().first(); QCOMPARE(baseDecl->identifier(), Identifier("Base")); ClassDeclaration* inheritedDecl = dynamic_cast<ClassDeclaration*>(top->localDeclarations()[1]); QCOMPARE(inheritedDecl->identifier(), Identifier("Inherited")); QVERIFY(inheritedDecl); QCOMPARE(inheritedDecl->baseClassesSize(), 1u); QCOMPARE(baseDecl->uses().count(), 1); QCOMPARE(baseDecl->uses().first().count(), 1); QCOMPARE(baseDecl->uses().first().first(), RangeInRevision(0, 40, 0, 44)); }
void visit(ClassReferenceExp *e) override { IF_LOG Logger::print("ClassReferenceExp::toConstElem: %s @ %s\n", e->toChars(), e->type->toChars()); LOG_SCOPE; ClassDeclaration *origClass = e->originalClass(); DtoResolveClass(origClass); StructLiteralExp *value = e->value; if (value->globalVar) { IF_LOG Logger::cout() << "Using existing global: " << *value->globalVar << '\n'; } else { value->globalVar = new llvm::GlobalVariable( p->module, origClass->type->ctype->isClass()->getMemoryLLType(), false, llvm::GlobalValue::InternalLinkage, nullptr, ".classref"); std::map<VarDeclaration *, llvm::Constant *> varInits; // Unfortunately, ClassReferenceExp::getFieldAt is badly broken – it // places the base class fields _after_ those of the subclass. { const size_t nexprs = value->elements->dim; std::stack<ClassDeclaration *> classHierachy; ClassDeclaration *cur = origClass; while (cur) { classHierachy.push(cur); cur = cur->baseClass; } size_t i = 0; while (!classHierachy.empty()) { cur = classHierachy.top(); classHierachy.pop(); for (size_t j = 0; j < cur->fields.dim; ++j) { if ((*value->elements)[i]) { VarDeclaration *field = cur->fields[j]; IF_LOG Logger::println("Getting initializer for: %s", field->toChars()); LOG_SCOPE; varInits[field] = toConstElem((*value->elements)[i]); } ++i; } } assert(i == nexprs); } llvm::Constant *constValue = getIrAggr(origClass)->createInitializerConstant(varInits); if (constValue->getType() != value->globalVar->getType()->getContainedType(0)) { auto finalGlobalVar = new llvm::GlobalVariable( p->module, constValue->getType(), false, llvm::GlobalValue::InternalLinkage, nullptr, ".classref"); value->globalVar->replaceAllUsesWith( DtoBitCast(finalGlobalVar, value->globalVar->getType())); value->globalVar->eraseFromParent(); value->globalVar = finalGlobalVar; } value->globalVar->setInitializer(constValue); } result = value->globalVar; if (e->type->ty == Tclass) { ClassDeclaration *targetClass = static_cast<TypeClass *>(e->type)->sym; if (InterfaceDeclaration *it = targetClass->isInterfaceDeclaration()) { assert(it->isBaseOf(origClass, NULL)); IrTypeClass *typeclass = origClass->type->ctype->isClass(); // find interface impl size_t i_index = typeclass->getInterfaceIndex(it); assert(i_index != ~0UL); // offset pointer result = DtoGEPi(result, 0, i_index); } } assert(e->type->ty == Tclass || e->type->ty == Tenum); result = DtoBitCast(result, DtoType(e->type)); }
void ImplementationItem::execute(KTextEditor::View* view, const KTextEditor::Range& word) { DUChainReadLocker lock(DUChain::lock()); KTextEditor::Document *document = view->document(); QString replText; if (m_declaration) { //TODO:respect custom code styles // get existing modifiers so we can respect the user's choice of public/protected and final QStringList modifiers = getMethodTokens(document->text(KTextEditor::Range(KTextEditor::Cursor::start(), word.start()))); // get range to replace KTextEditor::Range replaceRange(word); if (!modifiers.isEmpty()) { // TODO: is there no easy API to map QString Index to a KTextEditor::Cursor ?! QString methodText = document->text(KTextEditor::Range(KTextEditor::Cursor::start(), word.start())); methodText = methodText.left(methodText.lastIndexOf(modifiers.last(), -1, Qt::CaseInsensitive)); replaceRange.start() = KTextEditor::Cursor(methodText.count('\n'), methodText.length() - methodText.lastIndexOf('\n') - 1); } // get indendation QString indendation; { QString currentLine = document->line(replaceRange.start().line()); indendation = getIndendation(currentLine); if ( !currentLine.isEmpty() && currentLine != indendation ) { // since theres some non-whitespace in this line, skip to the enxt one replText += '\n' + indendation; } if (indendation.isEmpty()) { // use a minimal indendation // TODO: respect code style indendation = QStringLiteral(" "); replText += indendation; } } #if 0 //Disabled, because not everyone writes phpdoc for every function //TODO: move to a phpdoc helper // build phpdoc comment { QualifiedIdentifier parentClassIdentifier; if (DUContext* pctx = m_declaration->context()) { parentClassIdentifier = pctx->localScopeIdentifier(); } else { qCDebug(COMPLETION) << "completion item for implementation has no parent context!"; } replText += "/**\n" + indendation + " * "; // insert old comment: const QString indentationWithExtra = "\n" + indendation + " *"; replText += m_declaration->comment().replace('\n', indentationWithExtra.toAscii().constData()); replText += "\n" + indendation + " * @overload " + m_declaration->internalContext()->scopeIdentifier(true).toString(); replText += "\n" + indendation + " **/\n" + indendation; } #endif // write function signature // copy existing modifiers if (!modifiers.isEmpty()) { // the tokens are in a bad order and there's no reverse method or similar, so we can't simply join the tokens QStringList::const_iterator i = modifiers.constEnd() - 1; while (true) { replText += (*i) + ' '; if (i == modifiers.constBegin()) { break; } else { --i; } } } QString functionName; bool isConstructorOrDestructor = false; bool isInterface = false; if (ClassMemberDeclaration* member = dynamic_cast<ClassMemberDeclaration*>(m_declaration.data())) { // NOTE: it should _never_ be private - but that's the completionmodel / context / worker's job if (!modifiers.contains(QStringLiteral("public")) && !modifiers.contains(QStringLiteral("protected"))) { if (member->accessPolicy() == Declaration::Protected) { replText += QLatin1String("protected "); } else { replText += QLatin1String("public "); } } if (!modifiers.contains(QStringLiteral("static")) && member->isStatic()) { replText += QLatin1String("static "); } functionName = member->identifier().toString(); ClassMethodDeclaration* method = dynamic_cast<ClassMethodDeclaration*>(m_declaration.data()); if (method) { functionName = method->prettyName().str(); isConstructorOrDestructor = method->isConstructor() || method->isDestructor(); } if (member->context() && member->context()->owner()) { ClassDeclaration* classDec = dynamic_cast<ClassDeclaration*>(member->context()->owner()); if (classDec) { isInterface = (classDec->classType() == ClassDeclarationData::Interface); } } } else { qCDebug(COMPLETION) << "completion item for implementation was not a classfunction declaration!"; functionName = m_declaration->identifier().toString(); } if (m_type == ImplementationItem::OverrideVar) { replText += "$" + functionName + " = "; } else { if (!modifiers.contains(QStringLiteral("function"))) { replText += QLatin1String("function "); } replText += functionName; { // get argument list QString arguments; createArgumentList(*this, arguments, 0, true); replText += arguments; } QString arguments; QVector<Declaration*> parameters; if (DUChainUtils::getArgumentContext(m_declaration.data())) parameters = DUChainUtils::getArgumentContext(m_declaration.data())->localDeclarations(); arguments = '('; bool first = true; foreach(Declaration* dec, parameters) { if (first) first = false; else arguments += QLatin1String(", "); arguments += '$' + dec->identifier().toString(); } arguments += ')'; bool voidReturnType = false; if (FunctionType::Ptr::dynamicCast(m_declaration->abstractType())) { AbstractType::Ptr retType = FunctionType::Ptr::staticCast(m_declaration->abstractType())->returnType(); if (retType->equals(new IntegralType(IntegralType::TypeVoid))) { voidReturnType = true; } } replText += QStringLiteral("\n%1{\n%1 ").arg(indendation); if (isInterface || m_type == ImplementationItem::Implement) { } else if (!isConstructorOrDestructor && !voidReturnType) { replText += QStringLiteral("$ret = parent::%2%3;\n%1 return $ret;").arg(indendation, functionName, arguments); } else { replText += QStringLiteral("parent::%1%2;").arg(functionName, arguments); } replText += QStringLiteral("\n%1}\n%1") .arg(indendation); } //TODO: properly place the cursor inside the {} part document->replaceText(replaceRange, replText); } else {
/****************************************** * Return elem that evaluates to the static frame pointer for function fd. * If fd is a member function, the returned expression will compute the value * of fd's 'this' variable. * This routine is critical for implementing nested functions. */ elem *getEthis(Loc loc, IRState *irs, Dsymbol *fd) { elem *ethis; FuncDeclaration *thisfd = irs->getFunc(); Dsymbol *fdparent = fd->toParent2(); Dsymbol *fdp = fdparent; /* These two are compiler generated functions for the in and out contracts, * and are called from an overriding function, not just the one they're * nested inside, so this hack is so they'll pass */ if (fdparent != thisfd && (fd->ident == Id::require || fd->ident == Id::ensure)) { FuncDeclaration *fdthis = thisfd; for (size_t i = 0; ; ) { if (i == fdthis->foverrides.dim) { if (i == 0) break; fdthis = fdthis->foverrides[0]; i = 0; continue; } if (fdthis->foverrides[i] == fdp) { fdparent = thisfd; break; } i++; } } //printf("[%s] getEthis(thisfd = '%s', fd = '%s', fdparent = '%s')\n", loc.toChars(), thisfd->toPrettyChars(), fd->toPrettyChars(), fdparent->toPrettyChars()); if (fdparent == thisfd) { /* Going down one nesting level, i.e. we're calling * a nested function from its enclosing function. */ if (irs->sclosure && !(fd->ident == Id::require || fd->ident == Id::ensure)) { ethis = el_var(irs->sclosure); } else if (irs->sthis) { // We have a 'this' pointer for the current function /* If no variables in the current function's frame are * referenced by nested functions, then we can 'skip' * adding this frame into the linked list of stack * frames. */ if (thisfd->hasNestedFrameRefs()) { /* Local variables are referenced, can't skip. * Address of 'sthis' gives the 'this' for the nested * function */ ethis = el_ptr(irs->sthis); } else { ethis = el_var(irs->sthis); } } else { /* No 'this' pointer for current function, */ if (thisfd->hasNestedFrameRefs()) { /* OPframeptr is an operator that gets the frame pointer * for the current function, i.e. for the x86 it gets * the value of EBP */ ethis = el_long(TYnptr, 0); ethis->Eoper = OPframeptr; } else { /* Use NULL if no references to the current function's frame */ ethis = el_long(TYnptr, 0); } } } else { if (!irs->sthis) // if no frame pointer for this function { fd->error(loc, "is a nested function and cannot be accessed from %s", irs->getFunc()->toPrettyChars()); return el_long(TYnptr, 0); // error recovery } /* Go up a nesting level, i.e. we need to find the 'this' * of an enclosing function. * Our 'enclosing function' may also be an inner class. */ ethis = el_var(irs->sthis); Dsymbol *s = thisfd; while (fd != s) { FuncDeclaration *fdp = s->toParent2()->isFuncDeclaration(); //printf("\ts = '%s'\n", s->toChars()); thisfd = s->isFuncDeclaration(); if (thisfd) { /* Enclosing function is a function. */ // Error should have been caught by front end assert(thisfd->isNested() || thisfd->vthis); } else { /* Enclosed by an aggregate. That means the current * function must be a member function of that aggregate. */ AggregateDeclaration *ad = s->isAggregateDeclaration(); if (!ad) { Lnoframe: irs->getFunc()->error(loc, "cannot get frame pointer to %s", fd->toPrettyChars()); return el_long(TYnptr, 0); // error recovery } ClassDeclaration *cd = ad->isClassDeclaration(); ClassDeclaration *cdx = fd->isClassDeclaration(); if (cd && cdx && cdx->isBaseOf(cd, NULL)) break; StructDeclaration *sd = ad->isStructDeclaration(); if (fd == sd) break; if (!ad->isNested() || !ad->vthis) goto Lnoframe; ethis = el_bin(OPadd, TYnptr, ethis, el_long(TYsize_t, ad->vthis->offset)); ethis = el_una(OPind, TYnptr, ethis); } if (fdparent == s->toParent2()) break; /* Remember that frames for functions that have no * nested references are skipped in the linked list * of frames. */ if (fdp && fdp->hasNestedFrameRefs()) ethis = el_una(OPind, TYnptr, ethis); s = s->toParent2(); assert(s); } } #if 0 printf("ethis:\n"); elem_print(ethis); printf("\n"); #endif return ethis; }
void TestCppCodegen::testSimplifiedUpdating() { { InsertIntoDUChain code1("duchaintest_1.h", "template <typename T> struct test{};" "template <> struct test<int> {};"); code1.parse(TopDUContext::SimplifiedVisibleDeclarationsAndContexts | TopDUContext::AST); DUChainReadLocker lock; //No specializations are handled in simplified parsing Cpp::TemplateDeclaration *specClassDecl = dynamic_cast<Cpp::TemplateDeclaration*>(code1->localDeclarations()[1]); QVERIFY(!specClassDecl->specializedFrom().data()); } { InsertIntoDUChain code1("duchaintest_1.h", "struct A { struct Member2; struct Member1; };"); InsertIntoDUChain code3("duchaintest_3.h", "#include <duchaintest_1.h>\n struct C : public A { Member1 m1; Member2 m2; };"); qWarning() << "********************* Parsing step 1"; code3.parse(TopDUContext::AllDeclarationsContextsUsesAndAST); DUChainReadLocker lock; QCOMPARE(code3->localDeclarations().size(), 1); QCOMPARE(code3->childContexts().size(), 1); QCOMPARE(code3->childContexts()[0]->localDeclarations().size(), 2); QVERIFY(code3->childContexts()[0]->localDeclarations()[0]->abstractType().cast<StructureType>()); QVERIFY(code3->childContexts()[0]->localDeclarations()[1]->abstractType().cast<StructureType>()); QCOMPARE(code3->childContexts()[0]->importedParentContexts().size(), 1); QCOMPARE(code1->childContexts().size(), 1); } { InsertIntoDUChain code1("duchaintest_1.h", "struct A { struct Member1; };"); InsertIntoDUChain code2("duchaintest_2.h", "template<class T> struct B : public T{ struct Member2; };"); InsertIntoDUChain code3("duchaintest_3.h", "#include <duchaintest_2.h>\n #include <duchaintest_1.h>\n struct C : public B<A> { Member1 m1; Member2 m2; };"); qWarning() << "********************* Parsing step 1"; code3.parse(TopDUContext::AllDeclarationsContextsUsesAndAST); DUChainReadLocker lock; QCOMPARE(code3->localDeclarations().size(), 1); QCOMPARE(code3->childContexts().size(), 1); QCOMPARE(code3->childContexts()[0]->localDeclarations().size(), 2); QVERIFY(code3->childContexts()[0]->localDeclarations()[0]->abstractType().cast<StructureType>()); QVERIFY(code3->childContexts()[0]->localDeclarations()[1]->abstractType().cast<StructureType>()); QCOMPARE(code3->childContexts()[0]->importedParentContexts().size(), 1); QCOMPARE(code1->childContexts().size(), 1); QCOMPARE(code2->localDeclarations().size(), 1); ClassDeclaration* BClass = dynamic_cast<ClassDeclaration*>(code2->localDeclarations()[0]); QVERIFY(BClass); QCOMPARE(BClass->baseClassesSize(), 1u); QCOMPARE(BClass->baseClasses()[0].baseClass.abstractType()->toString(), QString("T")); QCOMPARE(code3->childContexts()[0]->importedParentContexts().size(), 1); DUContext* BAContext = code3->childContexts()[0]->importedParentContexts()[0].context(code3.topContext()); QVERIFY(BAContext); QVERIFY(!BAContext->inSymbolTable()); //2 contexts are imported: The template-context and the parent-class context QCOMPARE(BAContext->importedParentContexts().size(), 2); QCOMPARE(BAContext->importedParentContexts()[1].context(code3.topContext()), code1->childContexts()[0]); ClassDeclaration* classDecl = dynamic_cast<ClassDeclaration*>(BAContext->owner()); QVERIFY(classDecl); QCOMPARE(classDecl->baseClassesSize(), 1u); QCOMPARE(classDecl->baseClasses()[0].baseClass.index(), code1->localDeclarations()[0]->indexedType().index()); lock.unlock(); qWarning() << "********************* Parsing step 2"; code3.parse(TopDUContext::AllDeclarationsContextsUsesAndAST | TopDUContext::ForceUpdateRecursive, true); lock.lock(); QCOMPARE(code3->localDeclarations().size(), 1); QCOMPARE(code3->childContexts().size(), 1); QCOMPARE(code3->childContexts()[0]->localDeclarations().size(), 2); QCOMPARE(code1->childContexts().size(), 1); QVERIFY(code3->childContexts()[0]->localDeclarations()[0]->abstractType().cast<StructureType>()); QVERIFY(code3->childContexts()[0]->localDeclarations()[1]->abstractType().cast<StructureType>()); //BClass should have been updated, not deleted QVERIFY(BClass == dynamic_cast<ClassDeclaration*>(code2->localDeclarations()[0])); QCOMPARE(BClass->baseClassesSize(), 1u); QCOMPARE(BClass->baseClasses()[0].baseClass.abstractType()->toString(), QString("T")); //The template-instantiation context "B<A>" should have been deleted DUContext* BAContext2 = code3->childContexts()[0]->importedParentContexts()[0].context(code3.topContext()); // qDebug() << "BAContexts" << BAContext << BAContext2; // QVERIFY(BAContext != BAContext2); QCOMPARE(BAContext2->importedParentContexts().size(), 2); QCOMPARE(BAContext2->importedParentContexts()[1].context(code3.topContext()), code1->childContexts()[0]); classDecl = dynamic_cast<ClassDeclaration*>(BAContext2->owner()); QVERIFY(classDecl); QCOMPARE(classDecl->baseClassesSize(), 1u); QCOMPARE(classDecl->baseClasses()[0].baseClass.index(), code1->localDeclarations()[0]->indexedType().index()); } { InsertIntoDUChain code1("duchaintest_1.h", "struct A { struct Member1; };"); InsertIntoDUChain code2("duchaintest_2.h", "template<class T> struct B : public T{ struct Member2; };"); InsertIntoDUChain code3("duchaintest_3.h", "#include <duchaintest_2.h>\n #include <duchaintest_1.h>\n typedef B<A> Parent; struct C : public Parent { Member1 m1; Member2 m2; };"); code3.parse(TopDUContext::AllDeclarationsContextsUsesAndAST); DUChainReadLocker lock; QCOMPARE(code3->localDeclarations().size(), 2); QCOMPARE(code3->childContexts().size(), 1); QCOMPARE(code3->childContexts()[0]->localDeclarations().size(), 2); QVERIFY(code3->childContexts()[0]->localDeclarations()[0]->abstractType().cast<StructureType>()); QVERIFY(code3->childContexts()[0]->localDeclarations()[1]->abstractType().cast<StructureType>()); lock.unlock(); code3.parse(TopDUContext::AllDeclarationsContextsUsesAndAST | TopDUContext::ForceUpdateRecursive, true); lock.lock(); QCOMPARE(code3->localDeclarations().size(), 2); QCOMPARE(code3->childContexts().size(), 1); QCOMPARE(code3->childContexts()[0]->localDeclarations().size(), 2); QVERIFY(code3->childContexts()[0]->localDeclarations()[0]->abstractType().cast<StructureType>()); QVERIFY(code3->childContexts()[0]->localDeclarations()[1]->abstractType().cast<StructureType>()); } { QString text = "class C { class D d; };"; InsertIntoDUChain code("testsimplified.cpp", text); code.parse(TopDUContext::AllDeclarationsContextsUsesAndAST); DUChainReadLocker lock; dumpAST(code); //The forward-declaration of 'D' is forwarded into the top-context QCOMPARE(code->localDeclarations().size(), 2); Declaration* classDecl = code->localDeclarations()[0]; QCOMPARE(code->childContexts().size(), 1); QCOMPARE(code->childContexts()[0]->localDeclarations().size(), 1); QVERIFY(code->childContexts()[0]->localDeclarations()[0]->abstractType().cast<StructureType>()); QCOMPARE(code->childContexts()[0]->localDeclarations()[0]->abstractType()->toString(), QString("D")); lock.unlock(); code.parse(TopDUContext::AllDeclarationsContextsUsesAndAST | TopDUContext::ForceUpdate, true); lock.lock(); QCOMPARE(code->localDeclarations().size(), 2); //Verify that an update has happened, rather than recreating everything QCOMPARE(code->localDeclarations()[0], classDecl); QCOMPARE(code->childContexts().size(), 1); QCOMPARE(code->childContexts()[0]->localDeclarations().size(), 1); QVERIFY(code->childContexts()[0]->localDeclarations()[0]->abstractType().cast<StructureType>()); QCOMPARE(code->childContexts()[0]->localDeclarations()[0]->abstractType()->toString(), QString("D")); } { QString text = "class C {int test(); int mem; }; void test(int a); int i;"; InsertIntoDUChain code("testsimplified.cpp", text); code.parse(TopDUContext::SimplifiedVisibleDeclarationsAndContexts | TopDUContext::AST); DUChainReadLocker lock; dumpAST(code); QCOMPARE(code->localDeclarations().size(), 3); QCOMPARE(code->childContexts().size(), 1); QCOMPARE(code->childContexts()[0]->localDeclarations().size(), 2); QVERIFY(!code->childContexts()[0]->localDeclarations()[0]->abstractType()); QVERIFY(!code->childContexts()[0]->localDeclarations()[1]->abstractType()); //In simplified parsing mode, the type should not have been built QVERIFY(!code->localDeclarations()[0]->abstractType()); QVERIFY(code->localDeclarations()[0]->kind() == Declaration::Type); QVERIFY(!code->localDeclarations()[1]->abstractType()); QVERIFY(code->localDeclarations()[1]->kind() == Declaration::Instance); QVERIFY(!code->localDeclarations()[2]->abstractType()); QVERIFY(code->localDeclarations()[2]->kind() == Declaration::Instance); { uint count; const KDevelop::CodeModelItem* items; KDevelop::CodeModel::self().items(code->url(), count, items); for(uint a = 0; a < count; ++a) { if(items[a].id == code->localDeclarations()[0]->qualifiedIdentifier()) { QVERIFY(items[a].kind & KDevelop::CodeModelItem::Class); } } } } { InsertIntoDUChain codeA("A.h", "#ifndef A_H\n #define A_H\n class A{}; \n#endif"); InsertIntoDUChain codeB("B.h", "#include <A.h>\n class B{};"); codeB.parse(TopDUContext::SimplifiedVisibleDeclarationsAndContexts | TopDUContext::AST | TopDUContext::Recursive); DUChainReadLocker lock; //This is not only for debug-output, but also verifies that the AST is there as requesed dumpAST(codeA); QCOMPARE(codeB->importedParentContexts().size(), 1); QCOMPARE(codeA->localDeclarations().size(), 1); QVERIFY(codeA->parsingEnvironmentFile()->featuresSatisfied((TopDUContext::Features)(TopDUContext::SimplifiedVisibleDeclarationsAndContexts | TopDUContext::AST | TopDUContext::Recursive))); QVERIFY(codeB->parsingEnvironmentFile()->featuresSatisfied((TopDUContext::Features)(TopDUContext::SimplifiedVisibleDeclarationsAndContexts | TopDUContext::AST | TopDUContext::Recursive))); lock.unlock(); //Update with more features codeB.parse(TopDUContext::AllDeclarationsContextsUsesAndAST | TopDUContext::Recursive, true); lock.lock(); QCOMPARE(codeB->importedParentContexts().size(), 1); QCOMPARE(codeA->localDeclarations().size(), 1); QVERIFY(codeA->parsingEnvironmentFile()->featuresSatisfied((TopDUContext::Features)(TopDUContext::AllDeclarationsContextsUsesAndAST | TopDUContext::Recursive))); QVERIFY(codeB->parsingEnvironmentFile()->featuresSatisfied((TopDUContext::Features)(TopDUContext::AllDeclarationsContextsUsesAndAST | TopDUContext::Recursive))); } { ///Test whether "empty" files work InsertIntoDUChain codeA("Q.h", ""); InsertIntoDUChain codeB("B.h", "#include <Q.h>\n class B{};"); codeB.parse(TopDUContext::SimplifiedVisibleDeclarationsAndContexts | TopDUContext::AST | TopDUContext::Recursive); DUChainReadLocker lock; QCOMPARE(codeB->importedParentContexts().size(), 1); QVERIFY(codeA->localDeclarations().isEmpty()); QVERIFY(!codeA->parsingEnvironmentFile()->isProxyContext()); } { ///Test the 'ignoring' of header-guards InsertIntoDUChain codeA("A.h", "#ifndef A_H\n #define A_H\n class A{};\n #ifdef HONK\n class Honk {};\n #endif\n \n#endif \n"); InsertIntoDUChain codeB("B.h", "#define A_H\n \n #include <A.h>\n class B{};"); QVERIFY(!codeA.tryGet()); codeB.parse(TopDUContext::SimplifiedVisibleDeclarationsAndContexts | TopDUContext::AST | TopDUContext::Recursive); DUChainReadLocker lock; //This is not only for debug-output, but also verifies that the AST is there as requesed dumpAST(codeA); QCOMPARE(codeA->localDeclarations().size(), 1); QCOMPARE(codeB->importedParentContexts().size(), 1); lock.unlock(); codeB.parse(TopDUContext::SimplifiedVisibleDeclarationsAndContexts | TopDUContext::AST | TopDUContext::ForceUpdateRecursive | TopDUContext::Recursive); lock.lock(); QCOMPARE(codeA->localDeclarations().size(), 1); QCOMPARE(codeB->importedParentContexts().size(), 1); } }
// Put out instance of ModuleInfo for this Module void Module::genmoduleinfo() { // resolve ModuleInfo if (!moduleinfo) { error("object.d is missing the ModuleInfo struct"); fatal(); } // check for patch else { unsigned sizeof_ModuleInfo = 16 * Target::ptrsize; if (sizeof_ModuleInfo != moduleinfo->structsize) { error("object.d ModuleInfo class is incorrect"); fatal(); } } // use the RTTIBuilder RTTIBuilder b(moduleinfo); // some types LLType* moduleinfoTy = moduleinfo->type->irtype->getLLType(); LLType* classinfoTy = ClassDeclaration::classinfo->type->irtype->getLLType(); // importedModules[] std::vector<LLConstant*> importInits; LLConstant* importedModules = 0; llvm::ArrayType* importedModulesTy = 0; for (size_t i = 0; i < aimports.dim; i++) { Module *m = static_cast<Module *>(aimports.data[i]); if (!m->needModuleInfo() || m == this) continue; // declare the imported module info std::string m_name("_D"); m_name.append(m->mangle()); m_name.append("12__ModuleInfoZ"); llvm::GlobalVariable* m_gvar = gIR->module->getGlobalVariable(m_name); if (!m_gvar) m_gvar = new llvm::GlobalVariable(*gIR->module, moduleinfoTy, false, llvm::GlobalValue::ExternalLinkage, NULL, m_name); importInits.push_back(m_gvar); } // has import array? if (!importInits.empty()) { importedModulesTy = llvm::ArrayType::get(getPtrToType(moduleinfoTy), importInits.size()); importedModules = LLConstantArray::get(importedModulesTy, importInits); } // localClasses[] LLConstant* localClasses = 0; llvm::ArrayType* localClassesTy = 0; ClassDeclarations aclasses; //printf("members->dim = %d\n", members->dim); for (size_t i = 0; i < members->dim; i++) { Dsymbol *member; member = static_cast<Dsymbol *>(members->data[i]); //printf("\tmember '%s'\n", member->toChars()); member->addLocalClass(&aclasses); } // fill inits std::vector<LLConstant*> classInits; for (size_t i = 0; i < aclasses.dim; i++) { ClassDeclaration* cd = static_cast<ClassDeclaration*>(aclasses.data[i]); cd->codegen(Type::sir); if (cd->isInterfaceDeclaration()) { Logger::println("skipping interface '%s' in moduleinfo", cd->toPrettyChars()); continue; } else if (cd->sizeok != 1) { Logger::println("skipping opaque class declaration '%s' in moduleinfo", cd->toPrettyChars()); continue; } Logger::println("class: %s", cd->toPrettyChars()); LLConstant *c = DtoBitCast(cd->ir.irAggr->getClassInfoSymbol(), classinfoTy); classInits.push_back(c); } // has class array? if (!classInits.empty()) { localClassesTy = llvm::ArrayType::get(classinfoTy, classInits.size()); localClasses = LLConstantArray::get(localClassesTy, classInits); } // These must match the values in druntime/src/object_.d #define MIstandalone 4 #define MItlsctor 8 #define MItlsdtor 0x10 #define MIctor 0x20 #define MIdtor 0x40 #define MIxgetMembers 0x80 #define MIictor 0x100 #define MIunitTest 0x200 #define MIimportedModules 0x400 #define MIlocalClasses 0x800 #define MInew 0x80000000 // it's the "new" layout llvm::Function* fsharedctor = build_module_shared_ctor(); llvm::Function* fshareddtor = build_module_shared_dtor(); llvm::Function* funittest = build_module_unittest(); llvm::Function* fctor = build_module_ctor(); llvm::Function* fdtor = build_module_dtor(); unsigned flags = MInew; if (fctor) flags |= MItlsctor; if (fdtor) flags |= MItlsdtor; if (fsharedctor) flags |= MIctor; if (fshareddtor) flags |= MIdtor; #if 0 if (fgetmembers) flags |= MIxgetMembers; if (fictor) flags |= MIictor; #endif if (funittest) flags |= MIunitTest; if (importedModules) flags |= MIimportedModules; if (localClasses) flags |= MIlocalClasses; if (!needmoduleinfo) flags |= MIstandalone; b.push_uint(flags); // flags b.push_uint(0); // index if (fctor) b.push(fctor); if (fdtor) b.push(fdtor); if (fsharedctor) b.push(fsharedctor); if (fshareddtor) b.push(fshareddtor); #if 0 if (fgetmembers) b.push(fgetmembers); if (fictor) b.push(fictor); #endif if (funittest) b.push(funittest); if (importedModules) { b.push_size(importInits.size()); b.push(importedModules); } if (localClasses) { b.push_size(classInits.size()); b.push(localClasses); } // Put out module name as a 0-terminated string. const char *name = toPrettyChars(); const size_t len = strlen(name) + 1; llvm::IntegerType *it = llvm::IntegerType::getInt8Ty(gIR->context()); llvm::ArrayType *at = llvm::ArrayType::get(it, len); b.push(toConstantArray(it, at, name, len, false)); // create and set initializer b.finalize(moduleInfoType, moduleInfoSymbol()); // build the modulereference and ctor for registering it LLFunction* mictor = build_module_reference_and_ctor(moduleInfoSymbol()); AppendFunctionToLLVMGlobalCtorsDtors(mictor, 65535, true); }
void FuncDeclaration::toObjFile(int multiobj) { FuncDeclaration *func = this; ClassDeclaration *cd = func->parent->isClassDeclaration(); int reverse; int has_arguments; //printf("FuncDeclaration::toObjFile(%p, %s.%s)\n", func, parent->toChars(), func->toChars()); //if (type) printf("type = %s\n", func->type->toChars()); #if 0 //printf("line = %d\n",func->getWhere() / LINEINC); EEcontext *ee = env->getEEcontext(); if (ee->EEcompile == 2) { if (ee->EElinnum < (func->getWhere() / LINEINC) || ee->EElinnum > (func->endwhere / LINEINC) ) return; // don't compile this function ee->EEfunc = func->toSymbol(); } #endif if (semanticRun >= PASSobj) // if toObjFile() already run return; // If errors occurred compiling it, such as bugzilla 6118 if (type && type->ty == Tfunction && ((TypeFunction *)type)->next->ty == Terror) return; if (!func->fbody) { return; } if (func->isUnitTestDeclaration() && !global.params.useUnitTests) return; if (multiobj && !isStaticDtorDeclaration() && !isStaticCtorDeclaration()) { obj_append(this); return; } assert(semanticRun == PASSsemantic3done); semanticRun = PASSobj; if (global.params.verbose) printf("function %s\n",func->toChars()); Symbol *s = func->toSymbol(); func_t *f = s->Sfunc; #if TARGET_WINDOS /* This is done so that the 'this' pointer on the stack is the same * distance away from the function parameters, so that an overriding * function can call the nested fdensure or fdrequire of its overridden function * and the stack offsets are the same. */ if (isVirtual() && (fensure || frequire)) f->Fflags3 |= Ffakeeh; #endif #if TARGET_OSX s->Sclass = SCcomdat; #else s->Sclass = SCglobal; #endif for (Dsymbol *p = parent; p; p = p->parent) { if (p->isTemplateInstance()) { s->Sclass = SCcomdat; break; } } /* Vector operations should be comdat's */ if (isArrayOp) s->Sclass = SCcomdat; if (isNested()) { // if (!(config.flags3 & CFG3pic)) // s->Sclass = SCstatic; f->Fflags3 |= Fnested; } else { const char *libname = (global.params.symdebug) ? global.params.debuglibname : global.params.defaultlibname; // Pull in RTL startup code if (func->isMain()) { objextdef("_main"); #if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS obj_ehsections(); // initialize exception handling sections #endif #if TARGET_WINDOS objextdef("__acrtused_con"); #endif obj_includelib(libname); s->Sclass = SCglobal; } else if (strcmp(s->Sident, "main") == 0 && linkage == LINKc) { #if TARGET_WINDOS objextdef("__acrtused_con"); // bring in C startup code obj_includelib("snn.lib"); // bring in C runtime library #endif s->Sclass = SCglobal; } else if (func->isWinMain()) { objextdef("__acrtused"); obj_includelib(libname); s->Sclass = SCglobal; } // Pull in RTL startup code else if (func->isDllMain()) { objextdef("__acrtused_dll"); obj_includelib(libname); s->Sclass = SCglobal; } } cstate.CSpsymtab = &f->Flocsym; // Find module m for this function Module *m = NULL; for (Dsymbol *p = parent; p; p = p->parent) { m = p->isModule(); if (m) break; } IRState irs(m, func); Dsymbols deferToObj; // write these to OBJ file later irs.deferToObj = &deferToObj; TypeFunction *tf; enum RET retmethod; symbol *shidden = NULL; Symbol *sthis = NULL; tym_t tyf; tyf = tybasic(s->Stype->Tty); //printf("linkage = %d, tyf = x%x\n", linkage, tyf); reverse = tyrevfunc(s->Stype->Tty); assert(func->type->ty == Tfunction); tf = (TypeFunction *)(func->type); has_arguments = (tf->linkage == LINKd) && (tf->varargs == 1); retmethod = tf->retStyle(); if (retmethod == RETstack) { // If function returns a struct, put a pointer to that // as the first argument ::type *thidden = tf->next->pointerTo()->toCtype(); char hiddenparam[5+4+1]; static int hiddenparami; // how many we've generated so far sprintf(hiddenparam,"__HID%d",++hiddenparami); shidden = symbol_name(hiddenparam,SCparameter,thidden); shidden->Sflags |= SFLtrue | SFLfree; #if DMDV1 if (func->nrvo_can && func->nrvo_var && func->nrvo_var->nestedref) #else if (func->nrvo_can && func->nrvo_var && func->nrvo_var->nestedrefs.dim) #endif type_setcv(&shidden->Stype, shidden->Stype->Tty | mTYvolatile); irs.shidden = shidden; this->shidden = shidden; } else { // Register return style cannot make nrvo. // Auto functions keep the nrvo_can flag up to here, // so we should eliminate it before entering backend. nrvo_can = 0; } if (vthis) { assert(!vthis->csym); sthis = vthis->toSymbol(); irs.sthis = sthis; if (!(f->Fflags3 & Fnested)) f->Fflags3 |= Fmember; } Symbol **params; unsigned pi; // Estimate number of parameters, pi pi = (v_arguments != NULL); if (parameters) pi += parameters->dim; // Allow extra 2 for sthis and shidden params = (Symbol **)alloca((pi + 2) * sizeof(Symbol *)); // Get the actual number of parameters, pi, and fill in the params[] pi = 0; if (v_arguments) { params[pi] = v_arguments->toSymbol(); pi += 1; } if (parameters) { for (size_t i = 0; i < parameters->dim; i++) { VarDeclaration *v = (*parameters)[i]; if (v->csym) { error("compiler error, parameter '%s', bugzilla 2962?", v->toChars()); assert(0); } params[pi + i] = v->toSymbol(); } pi += parameters->dim; } if (reverse) { // Reverse params[] entries for (size_t i = 0; i < pi/2; i++) { Symbol *sptmp = params[i]; params[i] = params[pi - 1 - i]; params[pi - 1 - i] = sptmp; } } if (shidden) { #if 0 // shidden becomes last parameter params[pi] = shidden; #else // shidden becomes first parameter memmove(params + 1, params, pi * sizeof(params[0])); params[0] = shidden; #endif pi++; } if (sthis) { #if 0 // sthis becomes last parameter params[pi] = sthis; #else // sthis becomes first parameter memmove(params + 1, params, pi * sizeof(params[0])); params[0] = sthis; #endif pi++; } if ((global.params.isLinux || global.params.isOSX || global.params.isFreeBSD || global.params.isSolaris) && linkage != LINKd && shidden && sthis) { /* swap shidden and sthis */ Symbol *sp = params[0]; params[0] = params[1]; params[1] = sp; } for (size_t i = 0; i < pi; i++) { Symbol *sp = params[i]; sp->Sclass = SCparameter; sp->Sflags &= ~SFLspill; sp->Sfl = FLpara; symbol_add(sp); } // Determine register assignments if (pi) { size_t numintegerregs = 0, numfloatregs = 0; const unsigned char* argregs = getintegerparamsreglist(tyf, &numintegerregs); const unsigned char* floatregs = getfloatparamsreglist(tyf, &numfloatregs); // Order of assignment of pointer or integer parameters int r = 0; int xmmcnt = 0; for (size_t i = 0; i < pi; i++) { Symbol *sp = params[i]; tym_t ty = tybasic(sp->Stype->Tty); // BUG: doesn't work for structs if (r < numintegerregs) { if ((I64 || (i == 0 && (tyf == TYjfunc || tyf == TYmfunc))) && type_jparam(sp->Stype)) { sp->Sclass = SCfastpar; sp->Spreg = argregs[r]; sp->Sfl = FLauto; ++r; } } if (xmmcnt < numfloatregs) { if (tyxmmreg(ty)) { sp->Sclass = SCfastpar; sp->Spreg = floatregs[xmmcnt]; sp->Sfl = FLauto; ++xmmcnt; } } } } if (func->fbody) { block *b; Blockx bx; Statement *sbody; localgot = NULL; sbody = func->fbody; memset(&bx,0,sizeof(bx)); bx.startblock = block_calloc(); bx.curblock = bx.startblock; bx.funcsym = s; bx.scope_index = -1; bx.classdec = cd; bx.member = func; bx.module = getModule(); irs.blx = &bx; #if DMDV2 buildClosure(&irs); #endif #if 0 if (func->isSynchronized()) { if (cd) { elem *esync; if (func->isStatic()) { // monitor is in ClassInfo esync = el_ptr(cd->toSymbol()); } else { // 'this' is the monitor esync = el_var(sthis); } if (func->isStatic() || sbody->usesEH() || !(config.flags2 & CFG2seh)) { // BUG: what if frequire or fensure uses EH? sbody = new SynchronizedStatement(func->loc, esync, sbody); } else { #if TARGET_WINDOS if (config.flags2 & CFG2seh) { /* The "jmonitor" uses an optimized exception handling frame * which is a little shorter than the more general EH frame. * It isn't strictly necessary. */ s->Sfunc->Fflags3 |= Fjmonitor; } #endif el_free(esync); } } else { error("synchronized function %s must be a member of a class", func->toChars()); } } #elif TARGET_WINDOS if (func->isSynchronized() && cd && config.flags2 & CFG2seh && !func->isStatic() && !sbody->usesEH()) { /* The "jmonitor" hack uses an optimized exception handling frame * which is a little shorter than the more general EH frame. */ s->Sfunc->Fflags3 |= Fjmonitor; } #endif sbody->toIR(&irs); bx.curblock->BC = BCret; f->Fstartblock = bx.startblock; // einit = el_combine(einit,bx.init); if (isCtorDeclaration()) { assert(sthis); for (b = f->Fstartblock; b; b = b->Bnext) { if (b->BC == BCret) { b->BC = BCretexp; b->Belem = el_combine(b->Belem, el_var(sthis)); } } } } // If static constructor #if DMDV2 if (isSharedStaticCtorDeclaration()) // must come first because it derives from StaticCtorDeclaration { ssharedctors.push(s); } else #endif if (isStaticCtorDeclaration()) { sctors.push(s); } // If static destructor #if DMDV2 if (isSharedStaticDtorDeclaration()) // must come first because it derives from StaticDtorDeclaration { SharedStaticDtorDeclaration *f = isSharedStaticDtorDeclaration(); assert(f); if (f->vgate) { /* Increment destructor's vgate at construction time */ esharedctorgates.push(f); } sshareddtors.shift(s); } else #endif if (isStaticDtorDeclaration()) { StaticDtorDeclaration *f = isStaticDtorDeclaration(); assert(f); if (f->vgate) { /* Increment destructor's vgate at construction time */ ectorgates.push(f); } sdtors.shift(s); } // If unit test if (isUnitTestDeclaration()) { stests.push(s); } if (global.errors) return; writefunc(s); if (isExport()) obj_export(s, Poffset); for (size_t i = 0; i < irs.deferToObj->dim; i++) { Dsymbol *s = (*irs.deferToObj)[i]; FuncDeclaration *fd = s->isFuncDeclaration(); if (fd) { FuncDeclaration *fdp = fd->toParent2()->isFuncDeclaration(); if (fdp && fdp->semanticRun < PASSobj) { /* Bugzilla 7595 * FuncDeclaration::buildClosure() relies on nested functions * being toObjFile'd after the outer function. Otherwise, the * v->offset's for the closure variables are wrong. * So, defer fd until after fdp is done. */ fdp->deferred.push(fd); continue; } } s->toObjFile(0); } for (size_t i = 0; i < deferred.dim; i++) { FuncDeclaration *fd = deferred[i]; fd->toObjFile(0); } #if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS // A hack to get a pointer to this function put in the .dtors segment if (ident && memcmp(ident->toChars(), "_STD", 4) == 0) obj_staticdtor(s); #endif #if DMDV2 if (irs.startaddress) { printf("Setting start address\n"); obj_startaddress(irs.startaddress); } #endif }
void visit(ClassDeclaration *cd) { //printf("ClassDeclaration::toObjFile('%s')\n", cd->toChars()); if (cd->type->ty == Terror) { cd->error("had semantic errors when compiling"); return; } if (!cd->members) return; if (multiobj && !cd->hasStaticCtorOrDtor()) { obj_append(cd); return; } if (global.params.symdebug) toDebug(cd); assert(!cd->scope); // semantic() should have been run to completion enum_SC scclass = SCglobal; if (cd->isInstantiated()) scclass = SCcomdat; // Put out the members for (size_t i = 0; i < cd->members->dim; i++) { Dsymbol *member = (*cd->members)[i]; /* There might be static ctors in the members, and they cannot * be put in separate obj files. */ member->accept(this); } // Generate C symbols toSymbol(cd); toVtblSymbol(cd); Symbol *sinit = toInitializer(cd); ////////////////////////////////////////////// // Generate static initializer sinit->Sclass = scclass; sinit->Sfl = FLdata; ClassDeclaration_toDt(cd, &sinit->Sdt); out_readonly(sinit); outdata(sinit); ////////////////////////////////////////////// // Put out the TypeInfo genTypeInfo(cd->type, NULL); //toObjFile(cd->type->vtinfo, multiobj); ////////////////////////////////////////////// // Put out the ClassInfo cd->csym->Sclass = scclass; cd->csym->Sfl = FLdata; /* The layout is: { void **vptr; monitor_t monitor; byte[] initializer; // static initialization data char[] name; // class name void *[] vtbl; Interface[] interfaces; ClassInfo *base; // base class void *destructor; void *invariant; // class invariant ClassFlags flags; void *deallocator; OffsetTypeInfo[] offTi; void *defaultConstructor; //const(MemberInfo[]) function(string) xgetMembers; // module getMembers() function void *xgetRTInfo; //TypeInfo typeinfo; } */ dt_t *dt = NULL; unsigned offset = Target::classinfosize; // must be ClassInfo.size if (Type::typeinfoclass) { if (Type::typeinfoclass->structsize != Target::classinfosize) { #ifdef DEBUG printf("Target::classinfosize = x%x, Type::typeinfoclass->structsize = x%x\n", offset, Type::typeinfoclass->structsize); #endif cd->error("mismatch between dmd and object.d or object.di found. Check installation and import paths with -v compiler switch."); fatal(); } } if (Type::typeinfoclass) dtxoff(&dt, toVtblSymbol(Type::typeinfoclass), 0, TYnptr); // vtbl for ClassInfo else dtsize_t(&dt, 0); // BUG: should be an assert() dtsize_t(&dt, 0); // monitor // initializer[] assert(cd->structsize >= 8 || (cd->cpp && cd->structsize >= 4)); dtsize_t(&dt, cd->structsize); // size dtxoff(&dt, sinit, 0, TYnptr); // initializer // name[] const char *name = cd->ident->toChars(); size_t namelen = strlen(name); if (!(namelen > 9 && memcmp(name, "TypeInfo_", 9) == 0)) { name = cd->toPrettyChars(); namelen = strlen(name); } dtsize_t(&dt, namelen); dtabytes(&dt, TYnptr, 0, namelen + 1, name); // vtbl[] dtsize_t(&dt, cd->vtbl.dim); dtxoff(&dt, cd->vtblsym, 0, TYnptr); // interfaces[] dtsize_t(&dt, cd->vtblInterfaces->dim); if (cd->vtblInterfaces->dim) dtxoff(&dt, cd->csym, offset, TYnptr); // (*) else dtsize_t(&dt, 0); // base if (cd->baseClass) dtxoff(&dt, toSymbol(cd->baseClass), 0, TYnptr); else dtsize_t(&dt, 0); // destructor if (cd->dtor) dtxoff(&dt, toSymbol(cd->dtor), 0, TYnptr); else dtsize_t(&dt, 0); // invariant if (cd->inv) dtxoff(&dt, toSymbol(cd->inv), 0, TYnptr); else dtsize_t(&dt, 0); // flags ClassFlags::Type flags = ClassFlags::hasOffTi; if (cd->isCOMclass()) flags |= ClassFlags::isCOMclass; if (cd->isCPPclass()) flags |= ClassFlags::isCPPclass; flags |= ClassFlags::hasGetMembers; flags |= ClassFlags::hasTypeInfo; if (cd->ctor) flags |= ClassFlags::hasCtor; for (ClassDeclaration *pc = cd; pc; pc = pc->baseClass) { if (pc->dtor) { flags |= ClassFlags::hasDtor; break; } } if (cd->isabstract) flags |= ClassFlags::isAbstract; for (ClassDeclaration *pc = cd; pc; pc = pc->baseClass) { if (pc->members) { for (size_t i = 0; i < pc->members->dim; i++) { Dsymbol *sm = (*pc->members)[i]; //printf("sm = %s %s\n", sm->kind(), sm->toChars()); if (sm->hasPointers()) goto L2; } } } flags |= ClassFlags::noPointers; L2: dtsize_t(&dt, flags); // deallocator if (cd->aggDelete) dtxoff(&dt, toSymbol(cd->aggDelete), 0, TYnptr); else dtsize_t(&dt, 0); // offTi[] dtsize_t(&dt, 0); dtsize_t(&dt, 0); // null for now, fix later // defaultConstructor if (cd->defaultCtor && !(cd->defaultCtor->storage_class & STCdisable)) dtxoff(&dt, toSymbol(cd->defaultCtor), 0, TYnptr); else dtsize_t(&dt, 0); // xgetRTInfo if (cd->getRTInfo) Expression_toDt(cd->getRTInfo, &dt); else if (flags & ClassFlags::noPointers) dtsize_t(&dt, 0); else dtsize_t(&dt, 1); //dtxoff(&dt, toSymbol(type->vtinfo), 0, TYnptr); // typeinfo ////////////////////////////////////////////// // Put out (*vtblInterfaces)[]. Must immediately follow csym, because // of the fixup (*) offset += cd->vtblInterfaces->dim * (4 * Target::ptrsize); for (size_t i = 0; i < cd->vtblInterfaces->dim; i++) { BaseClass *b = (*cd->vtblInterfaces)[i]; ClassDeclaration *id = b->sym; /* The layout is: * struct Interface * { * ClassInfo *interface; * void *[] vtbl; * size_t offset; * } */ // Fill in vtbl[] b->fillVtbl(cd, &b->vtbl, 1); dtxoff(&dt, toSymbol(id), 0, TYnptr); // ClassInfo // vtbl[] dtsize_t(&dt, id->vtbl.dim); dtxoff(&dt, cd->csym, offset, TYnptr); dtsize_t(&dt, b->offset); // this offset offset += id->vtbl.dim * Target::ptrsize; } // Put out the (*vtblInterfaces)[].vtbl[] // This must be mirrored with ClassDeclaration::baseVtblOffset() //printf("putting out %d interface vtbl[]s for '%s'\n", vtblInterfaces->dim, toChars()); for (size_t i = 0; i < cd->vtblInterfaces->dim; i++) { BaseClass *b = (*cd->vtblInterfaces)[i]; ClassDeclaration *id = b->sym; //printf(" interface[%d] is '%s'\n", i, id->toChars()); size_t j = 0; if (id->vtblOffset()) { // First entry is ClassInfo reference //dtxoff(&dt, toSymbol(id), 0, TYnptr); // First entry is struct Interface reference dtxoff(&dt, cd->csym, Target::classinfosize + i * (4 * Target::ptrsize), TYnptr); j = 1; } assert(id->vtbl.dim == b->vtbl.dim); for (; j < id->vtbl.dim; j++) { assert(j < b->vtbl.dim); #if 0 RootObject *o = b->vtbl[j]; if (o) { printf("o = %p\n", o); assert(o->dyncast() == DYNCAST_DSYMBOL); Dsymbol *s = (Dsymbol *)o; printf("s->kind() = '%s'\n", s->kind()); } #endif FuncDeclaration *fd = b->vtbl[j]; if (fd) dtxoff(&dt, toThunkSymbol(fd, b->offset), 0, TYnptr); else dtsize_t(&dt, 0); } } // Put out the overriding interface vtbl[]s. // This must be mirrored with ClassDeclaration::baseVtblOffset() //printf("putting out overriding interface vtbl[]s for '%s' at offset x%x\n", toChars(), offset); ClassDeclaration *pc; for (pc = cd->baseClass; pc; pc = pc->baseClass) { for (size_t k = 0; k < pc->vtblInterfaces->dim; k++) { BaseClass *bs = (*pc->vtblInterfaces)[k]; FuncDeclarations bvtbl; if (bs->fillVtbl(cd, &bvtbl, 0)) { //printf("\toverriding vtbl[] for %s\n", bs->sym->toChars()); ClassDeclaration *id = bs->sym; size_t j = 0; if (id->vtblOffset()) { // First entry is ClassInfo reference //dtxoff(&dt, toSymbol(id), 0, TYnptr); // First entry is struct Interface reference dtxoff(&dt, toSymbol(pc), Target::classinfosize + k * (4 * Target::ptrsize), TYnptr); j = 1; } for (; j < id->vtbl.dim; j++) { assert(j < bvtbl.dim); FuncDeclaration *fd = bvtbl[j]; if (fd) dtxoff(&dt, toThunkSymbol(fd, bs->offset), 0, TYnptr); else dtsize_t(&dt, 0); } } } } cd->csym->Sdt = dt; // ClassInfo cannot be const data, because we use the monitor on it outdata(cd->csym); if (cd->isExport()) objmod->export_symbol(cd->csym, 0); ////////////////////////////////////////////// // Put out the vtbl[] //printf("putting out %s.vtbl[]\n", toChars()); dt = NULL; if (cd->vtblOffset()) dtxoff(&dt, cd->csym, 0, TYnptr); // first entry is ClassInfo reference for (size_t i = cd->vtblOffset(); i < cd->vtbl.dim; i++) { FuncDeclaration *fd = cd->vtbl[i]->isFuncDeclaration(); //printf("\tvtbl[%d] = %p\n", i, fd); if (fd && (fd->fbody || !cd->isAbstract())) { // Ensure function has a return value (Bugzilla 4869) fd->functionSemantic(); Symbol *s = toSymbol(fd); if (cd->isFuncHidden(fd)) { /* fd is hidden from the view of this class. * If fd overlaps with any function in the vtbl[], then * issue 'hidden' error. */ for (size_t j = 1; j < cd->vtbl.dim; j++) { if (j == i) continue; FuncDeclaration *fd2 = cd->vtbl[j]->isFuncDeclaration(); if (!fd2->ident->equals(fd->ident)) continue; if (fd->leastAsSpecialized(fd2) || fd2->leastAsSpecialized(fd)) { TypeFunction *tf = (TypeFunction *)fd->type; if (tf->ty == Tfunction) cd->error("use of %s%s is hidden by %s; use 'alias %s = %s.%s;' to introduce base class overload set", fd->toPrettyChars(), parametersTypeToChars(tf->parameters, tf->varargs), cd->toChars(), fd->toChars(), fd->parent->toChars(), fd->toChars()); else cd->error("use of %s is hidden by %s", fd->toPrettyChars(), cd->toChars()); break; } } } dtxoff(&dt, s, 0, TYnptr); } else dtsize_t(&dt, 0); } cd->vtblsym->Sdt = dt; cd->vtblsym->Sclass = scclass; cd->vtblsym->Sfl = FLdata; out_readonly(cd->vtblsym); outdata(cd->vtblsym); if (cd->isExport()) objmod->export_symbol(cd->vtblsym,0); }
Expression *CastExp::optimize(int result) { //printf("CastExp::optimize(result = %d) %s\n", result, toChars()); //printf("from %s to %s\n", type->toChars(), to->toChars()); //printf("from %s\n", type->toChars()); //printf("e1->type %s\n", e1->type->toChars()); //printf("type = %p\n", type); assert(type); enum TOK op1 = e1->op; e1 = e1->optimize(result); if (result & WANTinterpret) e1 = fromConstInitializer(e1); if ((e1->op == TOKstring || e1->op == TOKarrayliteral) && (type->ty == Tpointer || type->ty == Tarray) && type->next->equals(e1->type->next) ) { // make a copy before adjusting type to avoid // messing up the type of an existing initializer e1 = e1->syntaxCopy(); e1->type = type; return e1; } /* The first test here is to prevent infinite loops */ if (op1 != TOKarrayliteral && e1->op == TOKarrayliteral) return e1->castTo(NULL, to); if (e1->op == TOKnull && (type->ty == Tpointer || type->ty == Tclass)) { e1->type = type; return e1; } if (result & WANTflags && type->ty == Tclass && e1->type->ty == Tclass) { // See if we can remove an unnecessary cast ClassDeclaration *cdfrom; ClassDeclaration *cdto; int offset; cdfrom = e1->type->isClassHandle(); cdto = type->isClassHandle(); if (cdto->isBaseOf(cdfrom, &offset) && offset == 0) { e1->type = type; return e1; } } Expression *e; if (e1->isConst()) { if (e1->op == TOKsymoff) { if (type->size() == e1->type->size() && type->toBasetype()->ty != Tsarray) { e1->type = type; return e1; } return this; } if (to->toBasetype()->ty == Tvoid) e = this; else e = Cast(type, to, e1); } else e = this; return e; }
void Module::genmoduleinfo() { //printf("Module::genmoduleinfo() %s\n", toChars()); if (! Module::moduleinfo) { ObjectNotFound(Id::ModuleInfo); } Symbol *msym = toSymbol(); ////////////////////////////////////////////// csym->Sclass = SCglobal; csym->Sfl = FLdata; dt_t *dt = NULL; ClassDeclarations aclasses; //printf("members->dim = %d\n", members->dim); for (size_t i = 0; i < members->dim; i++) { Dsymbol *member = (*members)[i]; //printf("\tmember '%s'\n", member->toChars()); member->addLocalClass(&aclasses); } // importedModules[] size_t aimports_dim = aimports.dim; for (size_t i = 0; i < aimports.dim; i++) { Module *m = aimports[i]; if (!m->needmoduleinfo) aimports_dim--; } FuncDeclaration *sgetmembers = findGetMembers(); // These must match the values in druntime/src/object_.d #define MIstandalone 0x4 #define MItlsctor 0x8 #define MItlsdtor 0x10 #define MIctor 0x20 #define MIdtor 0x40 #define MIxgetMembers 0x80 #define MIictor 0x100 #define MIunitTest 0x200 #define MIimportedModules 0x400 #define MIlocalClasses 0x800 #define MIname 0x1000 unsigned flags = 0; if (!needmoduleinfo) flags |= MIstandalone; if (sctor) flags |= MItlsctor; if (sdtor) flags |= MItlsdtor; if (ssharedctor) flags |= MIctor; if (sshareddtor) flags |= MIdtor; if (sgetmembers) flags |= MIxgetMembers; if (sictor) flags |= MIictor; if (stest) flags |= MIunitTest; if (aimports_dim) flags |= MIimportedModules; if (aclasses.dim) flags |= MIlocalClasses; flags |= MIname; dtdword(&dt, flags); // _flags dtdword(&dt, 0); // _index if (flags & MItlsctor) dtxoff(&dt, sctor, 0, TYnptr); if (flags & MItlsdtor) dtxoff(&dt, sdtor, 0, TYnptr); if (flags & MIctor) dtxoff(&dt, ssharedctor, 0, TYnptr); if (flags & MIdtor) dtxoff(&dt, sshareddtor, 0, TYnptr); if (flags & MIxgetMembers) dtxoff(&dt, sgetmembers->toSymbol(), 0, TYnptr); if (flags & MIictor) dtxoff(&dt, sictor, 0, TYnptr); if (flags & MIunitTest) dtxoff(&dt, stest, 0, TYnptr); if (flags & MIimportedModules) { dtsize_t(&dt, aimports_dim); for (size_t i = 0; i < aimports.dim; i++) { Module *m = aimports[i]; if (m->needmoduleinfo) { Symbol *s = m->toSymbol(); /* Weak references don't pull objects in from the library, * they resolve to 0 if not pulled in by something else. * Don't pull in a module just because it was imported. */ s->Sflags |= SFLweak; dtxoff(&dt, s, 0, TYnptr); } } } if (flags & MIlocalClasses) { dtsize_t(&dt, aclasses.dim); for (size_t i = 0; i < aclasses.dim; i++) { ClassDeclaration *cd = aclasses[i]; dtxoff(&dt, cd->toSymbol(), 0, TYnptr); } } if (flags & MIname) { // Put out module name as a 0-terminated string, to save bytes nameoffset = dt_size(dt); const char *name = toPrettyChars(); namelen = strlen(name); dtnbytes(&dt, namelen + 1, name); //printf("nameoffset = x%x\n", nameoffset); } csym->Sdt = dt; // Cannot be CONST because the startup code sets flag bits in it outdata(csym); ////////////////////////////////////////////// objmod->moduleinfo(msym); }
llvm::GlobalVariable * IrAggr::getInterfaceVtbl(BaseClass * b, bool new_instance, size_t interfaces_index) { ClassGlobalMap::iterator it = interfaceVtblMap.find(b->base); if (it != interfaceVtblMap.end()) return it->second; IF_LOG Logger::println("Building vtbl for implementation of interface %s in class %s", b->base->toPrettyChars(), aggrdecl->toPrettyChars()); LOG_SCOPE; ClassDeclaration* cd = aggrdecl->isClassDeclaration(); assert(cd && "not a class aggregate"); FuncDeclarations vtbl_array; b->fillVtbl(cd, &vtbl_array, new_instance); std::vector<llvm::Constant*> constants; constants.reserve(vtbl_array.dim); if (!b->base->isCPPinterface()) { // skip interface info for CPP interfaces // start with the interface info VarDeclarationIter interfaces_idx(ClassDeclaration::classinfo->fields, 3); // index into the interfaces array llvm::Constant* idxs[2] = { DtoConstSize_t(0), DtoConstSize_t(interfaces_index) }; llvm::Constant* c = llvm::ConstantExpr::getGetElementPtr( getInterfaceArraySymbol(), idxs, true); constants.push_back(c); } // add virtual function pointers size_t n = vtbl_array.dim; for (size_t i = b->base->vtblOffset(); i < n; i++) { Dsymbol* dsym = static_cast<Dsymbol*>(vtbl_array.data[i]); if (dsym == NULL) { // FIXME // why is this null? // happens for mini/s.d constants.push_back(getNullValue(getVoidPtrType())); continue; } FuncDeclaration* fd = dsym->isFuncDeclaration(); assert(fd && "vtbl entry not a function"); assert((!fd->isAbstract() || fd->fbody) && "null symbol in interface implementation vtable"); fd->codegen(Type::sir); assert(fd->ir.irFunc && "invalid vtbl function"); LLFunction *fn = fd->ir.irFunc->func; // If the base is a cpp interface, 'this' parameter is a pointer to // the interface not the underlying object as expected. Instead of // the function, we place into the vtable a small wrapper, called thunk, // that casts 'this' to the object and then pass it to the real function. if (b->base->isCPPinterface()) { TypeFunction *f = (TypeFunction*)fd->type->toBasetype(); assert(f->fty.arg_this); // create the thunk function OutBuffer name; name.writestring("Th"); name.printf("%i", b->offset); name.writestring(fd->mangle()); LLFunction *thunk = LLFunction::Create(isaFunction(fn->getType()->getContainedType(0)), DtoLinkage(fd), name.toChars(), gIR->module); // create entry and end blocks llvm::BasicBlock* beginbb = llvm::BasicBlock::Create(gIR->context(), "entry", thunk); llvm::BasicBlock* endbb = llvm::BasicBlock::Create(gIR->context(), "endentry", thunk); gIR->scopes.push_back(IRScope(beginbb, endbb)); // copy the function parameters, so later we can pass them to the real function std::vector<LLValue*> args; llvm::Function::arg_iterator iarg = thunk->arg_begin(); for (; iarg != thunk->arg_end(); ++iarg) args.push_back(iarg); // cast 'this' to Object LLValue* &thisArg = args[(f->fty.arg_sret == 0) ? 0 : 1]; LLType* thisType = thisArg->getType(); thisArg = DtoBitCast(thisArg, getVoidPtrType()); thisArg = DtoGEP1(thisArg, DtoConstInt(-b->offset)); thisArg = DtoBitCast(thisArg, thisType); // call the real vtbl function. LLValue *retVal = gIR->ir->CreateCall(fn, args); // return from the thunk if (thunk->getReturnType() == LLType::getVoidTy(gIR->context())) llvm::ReturnInst::Create(gIR->context(), beginbb); else llvm::ReturnInst::Create(gIR->context(), retVal, beginbb); // clean up gIR->scopes.pop_back(); thunk->getBasicBlockList().pop_back(); fn = thunk; } constants.push_back(fn); } // build the vtbl constant llvm::Constant* vtbl_constant = LLConstantStruct::getAnon(gIR->context(), constants, false); // create the global variable to hold it llvm::GlobalValue::LinkageTypes _linkage = DtoExternalLinkage(aggrdecl); std::string mangle("_D"); mangle.append(cd->mangle()); mangle.append("11__interface"); mangle.append(b->base->mangle()); mangle.append("6__vtblZ"); llvm::GlobalVariable* GV = getOrCreateGlobal(cd->loc, *gIR->module, vtbl_constant->getType(), true, _linkage, vtbl_constant, mangle ); // insert into the vtbl map interfaceVtblMap.insert(std::make_pair(b->base, GV)); return GV; }
LLConstant * IrStruct::getVtblInit() { if (constVtbl) return constVtbl; IF_LOG Logger::println("Building vtbl initializer"); LOG_SCOPE; ClassDeclaration* cd = aggrdecl->isClassDeclaration(); assert(cd && "not class"); std::vector<llvm::Constant*> constants; constants.reserve(cd->vtbl.dim); // start with the classinfo llvm::Constant* c = getClassInfoSymbol(); c = DtoBitCast(c, DtoType(ClassDeclaration::classinfo->type)); constants.push_back(c); // add virtual function pointers size_t n = cd->vtbl.dim; for (size_t i = 1; i < n; i++) { Dsymbol* dsym = static_cast<Dsymbol*>(cd->vtbl.data[i]); assert(dsym && "null vtbl member"); FuncDeclaration* fd = dsym->isFuncDeclaration(); assert(fd && "vtbl entry not a function"); if (cd->isAbstract() || (fd->isAbstract() && !fd->fbody)) { c = getNullValue(DtoType(fd->type->pointerTo())); } else { fd->codegen(Type::sir); assert(fd->ir.irFunc && "invalid vtbl function"); c = fd->ir.irFunc->func; #if DMDV2 if (cd->isFuncHidden(fd)) { /* fd is hidden from the view of this class. * If fd overlaps with any function in the vtbl[], then * issue 'hidden' error. */ for (size_t j = 1; j < n; j++) { if (j == i) continue; FuncDeclaration *fd2 = static_cast<Dsymbol *>(cd->vtbl.data[j])->isFuncDeclaration(); if (!fd2->ident->equals(fd->ident)) continue; if (fd->leastAsSpecialized(fd2) || fd2->leastAsSpecialized(fd)) { if (global.params.warnings) { TypeFunction *tf = static_cast<TypeFunction *>(fd->type); if (tf->ty == Tfunction) error("%s%s is hidden by %s\n", fd->toPrettyChars(), Parameter::argsTypesToChars(tf->parameters, tf->varargs), toChars()); else error("%s is hidden by %s\n", fd->toPrettyChars(), toChars()); } c = DtoBitCast(LLVM_D_GetRuntimeFunction(gIR->module, "_d_hidden_func"), c->getType()); break; } } } #endif } constants.push_back(c); } // build the constant struct LLType* vtblTy = stripModifiers(type)->irtype->isClass()->getVtbl(); constVtbl = LLConstantStruct::get(isaStruct(vtblTy), constants); #if 0 IF_LOG Logger::cout() << "constVtbl type: " << *constVtbl->getType() << std::endl; IF_LOG Logger::cout() << "vtbl type: " << *stripModifiers(type)->irtype->isClass()->getVtbl() << std::endl; #endif #if 0 size_t nc = constants.size(); for (size_t i = 0; i < nc; ++i) { if (constVtbl->getOperand(i)->getType() != vtblTy->getContainedType(i)) { Logger::cout() << "type mismatch for entry # " << i << " in vtbl initializer" << std::endl; constVtbl->getOperand(i)->dump(); vtblTy->getContainedType(i)->dump(); } } #endif assert(constVtbl->getType() == stripModifiers(type)->irtype->isClass()->getVtbl() && "vtbl initializer type mismatch"); return constVtbl; }
Expression *CastExp::optimize(int result) { //printf("CastExp::optimize(result = %d) %s\n", result, toChars()); //printf("from %s to %s\n", type->toChars(), to->toChars()); //printf("from %s\n", type->toChars()); //printf("e1->type %s\n", e1->type->toChars()); //printf("type = %p\n", type); assert(type); enum TOK op1 = e1->op; #define X 0 e1 = e1->optimize(result); e1 = fromConstInitializer(result, e1); if ((e1->op == TOKstring || e1->op == TOKarrayliteral) && (type->ty == Tpointer || type->ty == Tarray) && e1->type->nextOf()->size() == type->nextOf()->size() ) { e1 = e1->castTo(NULL, type); if (X) printf(" returning1 %s\n", e1->toChars()); return e1; } if (e1->op == TOKstructliteral && e1->type->implicitConvTo(type) >= MATCHconst) { e1->type = type; if (X) printf(" returning2 %s\n", e1->toChars()); return e1; } /* The first test here is to prevent infinite loops */ if (op1 != TOKarrayliteral && e1->op == TOKarrayliteral) return e1->castTo(NULL, to); if (e1->op == TOKnull && (type->ty == Tpointer || type->ty == Tclass || type->ty == Tarray)) { e1->type = type; if (X) printf(" returning3 %s\n", e1->toChars()); return e1; } if (result & WANTflags && type->ty == Tclass && e1->type->ty == Tclass) { // See if we can remove an unnecessary cast ClassDeclaration *cdfrom; ClassDeclaration *cdto; int offset; cdfrom = e1->type->isClassHandle(); cdto = type->isClassHandle(); if (cdto->isBaseOf(cdfrom, &offset) && offset == 0) { e1->type = type; if (X) printf(" returning4 %s\n", e1->toChars()); return e1; } } // We can convert 'head const' to mutable if (to->constOf()->equals(e1->type->constOf())) // if (to->constConv(e1->type) >= MATCHconst) { e1->type = type; if (X) printf(" returning5 %s\n", e1->toChars()); return e1; } Expression *e; if (e1->isConst()) { if (e1->op == TOKsymoff) { if (type->size() == e1->type->size() && type->toBasetype()->ty != Tsarray) { e1->type = type; return e1; } return this; } if (to->toBasetype()->ty == Tvoid) e = this; else e = Cast(type, to, e1); } else e = this; if (X) printf(" returning6 %s\n", e->toChars()); return e; #undef X }
void InterfaceDeclaration::toObjFile(int multiobj) { enum_SC scclass; //printf("InterfaceDeclaration::toObjFile('%s')\n", toChars()); if (type->ty == Terror) { error("had semantic errors when compiling"); return; } if (!members) return; if (global.params.symdebug) toDebug(); scclass = SCglobal; if (isInstantiated()) scclass = SCcomdat; // Put out the members for (size_t i = 0; i < members->dim; i++) { Dsymbol *member = (*members)[i]; member->toObjFile(0); } // Generate C symbols toSymbol(); ////////////////////////////////////////////// // Put out the TypeInfo type->getTypeInfo(NULL); type->vtinfo->toObjFile(multiobj); ////////////////////////////////////////////// // Put out the ClassInfo csym->Sclass = scclass; csym->Sfl = FLdata; /* The layout is: { void **vptr; monitor_t monitor; byte[] initializer; // static initialization data char[] name; // class name void *[] vtbl; Interface[] interfaces; Object *base; // base class void *destructor; void *invariant; // class invariant uint flags; void *deallocator; OffsetTypeInfo[] offTi; void *defaultConstructor; //const(MemberInfo[]) function(string) xgetMembers; // module getMembers() function void* xgetRTInfo; //TypeInfo typeinfo; } */ dt_t *dt = NULL; if (Type::typeinfoclass) dtxoff(&dt, Type::typeinfoclass->toVtblSymbol(), 0, TYnptr); // vtbl for ClassInfo else dtsize_t(&dt, 0); // BUG: should be an assert() dtsize_t(&dt, 0); // monitor // initializer[] dtsize_t(&dt, 0); // size dtsize_t(&dt, 0); // initializer // name[] const char *name = toPrettyChars(); size_t namelen = strlen(name); dtsize_t(&dt, namelen); dtabytes(&dt, TYnptr, 0, namelen + 1, name); // vtbl[] dtsize_t(&dt, 0); dtsize_t(&dt, 0); // (*vtblInterfaces)[] unsigned offset; dtsize_t(&dt, vtblInterfaces->dim); if (vtblInterfaces->dim) { offset = global.params.isLP64 ? CLASSINFO_SIZE_64 : CLASSINFO_SIZE; // must be ClassInfo.size if (Type::typeinfoclass) { if (Type::typeinfoclass->structsize != offset) { error("mismatch between dmd and object.d or object.di found. Check installation and import paths with -v compiler switch."); fatal(); } } dtxoff(&dt, csym, offset, TYnptr); // (*) } else { offset = 0; dtsize_t(&dt, 0); } // base assert(!baseClass); dtsize_t(&dt, 0); // dtor dtsize_t(&dt, 0); // invariant dtsize_t(&dt, 0); // flags ClassFlags::Type flags = ClassFlags::hasOffTi | ClassFlags::hasTypeInfo; if (isCOMinterface()) flags |= ClassFlags::isCOMclass; dtsize_t(&dt, flags); // deallocator dtsize_t(&dt, 0); // offTi[] dtsize_t(&dt, 0); dtsize_t(&dt, 0); // null for now, fix later // defaultConstructor dtsize_t(&dt, 0); // xgetMembers //dtsize_t(&dt, 0); // xgetRTInfo // xgetRTInfo if (getRTInfo) getRTInfo->toDt(&dt); else dtsize_t(&dt, 0); // no pointers //dtxoff(&dt, type->vtinfo->toSymbol(), 0, TYnptr); // typeinfo ////////////////////////////////////////////// // Put out (*vtblInterfaces)[]. Must immediately follow csym, because // of the fixup (*) offset += vtblInterfaces->dim * (4 * Target::ptrsize); for (size_t i = 0; i < vtblInterfaces->dim; i++) { BaseClass *b = (*vtblInterfaces)[i]; ClassDeclaration *id = b->base; // ClassInfo dtxoff(&dt, id->toSymbol(), 0, TYnptr); // vtbl[] dtsize_t(&dt, 0); dtsize_t(&dt, 0); // this offset dtsize_t(&dt, b->offset); } csym->Sdt = dt; out_readonly(csym); outdata(csym); if (isExport()) objmod->export_symbol(csym,0); }
Expression *semanticTraits(TraitsExp *e, Scope *sc) { #if LOGSEMANTIC printf("TraitsExp::semantic() %s\n", e->toChars()); #endif if (e->ident != Id::compiles && e->ident != Id::isSame && e->ident != Id::identifier && e->ident != Id::getProtection) { if (!TemplateInstance::semanticTiargs(e->loc, sc, e->args, 1)) return new ErrorExp(); } size_t dim = e->args ? e->args->dim : 0; if (e->ident == Id::isArithmetic) { return isTypeX(e, &isTypeArithmetic); } else if (e->ident == Id::isFloating) { return isTypeX(e, &isTypeFloating); } else if (e->ident == Id::isIntegral) { return isTypeX(e, &isTypeIntegral); } else if (e->ident == Id::isScalar) { return isTypeX(e, &isTypeScalar); } else if (e->ident == Id::isUnsigned) { return isTypeX(e, &isTypeUnsigned); } else if (e->ident == Id::isAssociativeArray) { return isTypeX(e, &isTypeAssociativeArray); } else if (e->ident == Id::isStaticArray) { return isTypeX(e, &isTypeStaticArray); } else if (e->ident == Id::isAbstractClass) { return isTypeX(e, &isTypeAbstractClass); } else if (e->ident == Id::isFinalClass) { return isTypeX(e, &isTypeFinalClass); } else if (e->ident == Id::isPOD) { if (dim != 1) goto Ldimerror; RootObject *o = (*e->args)[0]; Type *t = isType(o); StructDeclaration *sd; if (!t) { e->error("type expected as second argument of __traits %s instead of %s", e->ident->toChars(), o->toChars()); goto Lfalse; } Type *tb = t->baseElemOf(); if (tb->ty == Tstruct && ((sd = (StructDeclaration *)(((TypeStruct *)tb)->sym)) != NULL)) { if (sd->isPOD()) goto Ltrue; else goto Lfalse; } goto Ltrue; } else if (e->ident == Id::isNested) { if (dim != 1) goto Ldimerror; RootObject *o = (*e->args)[0]; Dsymbol *s = getDsymbol(o); AggregateDeclaration *a; FuncDeclaration *f; if (!s) { } else if ((a = s->isAggregateDeclaration()) != NULL) { if (a->isNested()) goto Ltrue; else goto Lfalse; } else if ((f = s->isFuncDeclaration()) != NULL) { if (f->isNested()) goto Ltrue; else goto Lfalse; } e->error("aggregate or function expected instead of '%s'", o->toChars()); goto Lfalse; } else if (e->ident == Id::isAbstractFunction) { return isFuncX(e, &isFuncAbstractFunction); } else if (e->ident == Id::isVirtualFunction) { return isFuncX(e, &isFuncVirtualFunction); } else if (e->ident == Id::isVirtualMethod) { return isFuncX(e, &isFuncVirtualMethod); } else if (e->ident == Id::isFinalFunction) { return isFuncX(e, &isFuncFinalFunction); } else if (e->ident == Id::isOverrideFunction) { return isFuncX(e, &isFuncOverrideFunction); } else if (e->ident == Id::isStaticFunction) { return isFuncX(e, &isFuncStaticFunction); } else if (e->ident == Id::isRef) { return isDeclX(e, &isDeclRef); } else if (e->ident == Id::isOut) { return isDeclX(e, &isDeclOut); } else if (e->ident == Id::isLazy) { return isDeclX(e, &isDeclLazy); } else if (e->ident == Id::identifier) { // Get identifier for symbol as a string literal /* Specify 0 for bit 0 of the flags argument to semanticTiargs() so that * a symbol should not be folded to a constant. * Bit 1 means don't convert Parameter to Type if Parameter has an identifier */ if (!TemplateInstance::semanticTiargs(e->loc, sc, e->args, 2)) return new ErrorExp(); if (dim != 1) goto Ldimerror; RootObject *o = (*e->args)[0]; Parameter *po = isParameter(o); Identifier *id; if (po) { id = po->ident; assert(id); } else { Dsymbol *s = getDsymbol(o); if (!s || !s->ident) { e->error("argument %s has no identifier", o->toChars()); goto Lfalse; } id = s->ident; } StringExp *se = new StringExp(e->loc, id->toChars()); return se->semantic(sc); } else if (e->ident == Id::getProtection) { if (dim != 1) goto Ldimerror; Scope *sc2 = sc->push(); sc2->flags = sc->flags | SCOPEnoaccesscheck; bool ok = TemplateInstance::semanticTiargs(e->loc, sc2, e->args, 1); sc2->pop(); if (!ok) return new ErrorExp(); RootObject *o = (*e->args)[0]; Dsymbol *s = getDsymbol(o); if (!s) { if (!isError(o)) e->error("argument %s has no protection", o->toChars()); goto Lfalse; } if (s->scope) s->semantic(s->scope); PROT protection = s->prot(); const char *protName = Pprotectionnames[protection]; assert(protName); StringExp *se = new StringExp(e->loc, (char *) protName); return se->semantic(sc); } else if (e->ident == Id::parent) { if (dim != 1) goto Ldimerror; RootObject *o = (*e->args)[0]; Dsymbol *s = getDsymbol(o); if (s) { if (FuncDeclaration *fd = s->isFuncDeclaration()) // Bugzilla 8943 s = fd->toAliasFunc(); if (!s->isImport()) // Bugzilla 8922 s = s->toParent(); } if (!s || s->isImport()) { e->error("argument %s has no parent", o->toChars()); goto Lfalse; } if (FuncDeclaration *f = s->isFuncDeclaration()) { if (TemplateDeclaration *td = getFuncTemplateDecl(f)) { if (td->overroot) // if not start of overloaded list of TemplateDeclaration's td = td->overroot; // then get the start Expression *ex = new TemplateExp(e->loc, td, f); ex = ex->semantic(sc); return ex; } if (FuncLiteralDeclaration *fld = f->isFuncLiteralDeclaration()) { // Directly translate to VarExp instead of FuncExp Expression *ex = new VarExp(e->loc, fld, 1); return ex->semantic(sc); } } return (new DsymbolExp(e->loc, s))->semantic(sc); } else if (e->ident == Id::hasMember || e->ident == Id::getMember || e->ident == Id::getOverloads || e->ident == Id::getVirtualMethods || e->ident == Id::getVirtualFunctions) { if (dim != 2) goto Ldimerror; RootObject *o = (*e->args)[0]; Expression *ex = isExpression((*e->args)[1]); if (!ex) { e->error("expression expected as second argument of __traits %s", e->ident->toChars()); goto Lfalse; } ex = ex->ctfeInterpret(); StringExp *se = ex->toStringExp(); if (!se || se->length() == 0) { e->error("string expected as second argument of __traits %s instead of %s", e->ident->toChars(), ex->toChars()); goto Lfalse; } se = se->toUTF8(sc); if (se->sz != 1) { e->error("string must be chars"); goto Lfalse; } Identifier *id = Lexer::idPool((char *)se->string); /* Prefer dsymbol, because it might need some runtime contexts. */ Dsymbol *sym = getDsymbol(o); if (sym) { ex = new DsymbolExp(e->loc, sym); ex = new DotIdExp(e->loc, ex, id); } else if (Type *t = isType(o)) ex = typeDotIdExp(e->loc, t, id); else if (Expression *ex2 = isExpression(o)) ex = new DotIdExp(e->loc, ex2, id); else { e->error("invalid first argument"); goto Lfalse; } if (e->ident == Id::hasMember) { if (sym) { Dsymbol *sm = sym->search(e->loc, id); if (sm) goto Ltrue; } /* Take any errors as meaning it wasn't found */ Scope *sc2 = sc->push(); ex = ex->trySemantic(sc2); sc2->pop(); if (!ex) goto Lfalse; else goto Ltrue; } else if (e->ident == Id::getMember) { ex = ex->semantic(sc); return ex; } else if (e->ident == Id::getVirtualFunctions || e->ident == Id::getVirtualMethods || e->ident == Id::getOverloads) { unsigned errors = global.errors; Expression *eorig = ex; ex = ex->semantic(sc); if (errors < global.errors) e->error("%s cannot be resolved", eorig->toChars()); /* Create tuple of functions of ex */ //ex->print(); Expressions *exps = new Expressions(); FuncDeclaration *f; if (ex->op == TOKvar) { VarExp *ve = (VarExp *)ex; f = ve->var->isFuncDeclaration(); ex = NULL; } else if (ex->op == TOKdotvar) { DotVarExp *dve = (DotVarExp *)ex; f = dve->var->isFuncDeclaration(); if (dve->e1->op == TOKdottype || dve->e1->op == TOKthis) ex = NULL; else ex = dve->e1; } else f = NULL; Ptrait p; p.exps = exps; p.e1 = ex; p.ident = e->ident; overloadApply(f, &p, &fptraits); TupleExp *tup = new TupleExp(e->loc, exps); return tup->semantic(sc); } else assert(0); } else if (e->ident == Id::classInstanceSize) { if (dim != 1) goto Ldimerror; RootObject *o = (*e->args)[0]; Dsymbol *s = getDsymbol(o); ClassDeclaration *cd; if (!s || (cd = s->isClassDeclaration()) == NULL) { e->error("first argument is not a class"); goto Lfalse; } if (cd->sizeok == SIZEOKnone) { if (cd->scope) cd->semantic(cd->scope); } if (cd->sizeok != SIZEOKdone) { e->error("%s %s is forward referenced", cd->kind(), cd->toChars()); goto Lfalse; } return new IntegerExp(e->loc, cd->structsize, Type::tsize_t); } else if (e->ident == Id::getAliasThis) { if (dim != 1) goto Ldimerror; RootObject *o = (*e->args)[0]; Dsymbol *s = getDsymbol(o); AggregateDeclaration *ad; if (!s || (ad = s->isAggregateDeclaration()) == NULL) { e->error("argument is not an aggregate type"); goto Lfalse; } Expressions *exps = new Expressions(); if (ad->aliasthis) exps->push(new StringExp(e->loc, ad->aliasthis->ident->toChars())); Expression *ex = new TupleExp(e->loc, exps); ex = ex->semantic(sc); return ex; } else if (e->ident == Id::getAttributes) { if (dim != 1) goto Ldimerror; RootObject *o = (*e->args)[0]; Dsymbol *s = getDsymbol(o); if (!s) { #if 0 Expression *x = isExpression(o); Type *t = isType(o); if (x) printf("e = %s %s\n", Token::toChars(x->op), x->toChars()); if (t) printf("t = %d %s\n", t->ty, t->toChars()); #endif e->error("first argument is not a symbol"); goto Lfalse; } //printf("getAttributes %s, attrs = %p, scope = %p\n", s->toChars(), s->userAttributes, s->userAttributesScope); UserAttributeDeclaration *udad = s->userAttribDecl; TupleExp *tup = new TupleExp(e->loc, udad ? udad->getAttributes() : new Expressions()); return tup->semantic(sc); } else if (e->ident == Id::getFunctionAttributes) { /// extract all function attributes as a tuple (const/shared/inout/pure/nothrow/etc) except UDAs. if (dim != 1) goto Ldimerror; RootObject *o = (*e->args)[0]; Dsymbol *s = getDsymbol(o); Type *t = isType(o); TypeFunction *tf = NULL; if (s) { if (FuncDeclaration *f = s->isFuncDeclaration()) t = f->type; else if (VarDeclaration *v = s->isVarDeclaration()) t = v->type; } if (t) { if (t->ty == Tfunction) tf = (TypeFunction *)t; else if (t->ty == Tdelegate) tf = (TypeFunction *)t->nextOf(); else if (t->ty == Tpointer && t->nextOf()->ty == Tfunction) tf = (TypeFunction *)t->nextOf(); } if (!tf) { e->error("first argument is not a function"); goto Lfalse; } Expressions *mods = new Expressions(); PushAttributes pa; pa.mods = mods; tf->modifiersApply(&pa, &PushAttributes::fp); tf->attributesApply(&pa, &PushAttributes::fp, TRUSTformatSystem); TupleExp *tup = new TupleExp(e->loc, mods); return tup->semantic(sc); } else if (e->ident == Id::allMembers || e->ident == Id::derivedMembers) { if (dim != 1) goto Ldimerror; RootObject *o = (*e->args)[0]; Dsymbol *s = getDsymbol(o); ScopeDsymbol *sds; if (!s) { e->error("argument has no members"); goto Lfalse; } Import *import; if ((import = s->isImport()) != NULL) { // Bugzilla 9692 sds = import->mod; } else if ((sds = s->isScopeDsymbol()) == NULL) { e->error("%s %s has no members", s->kind(), s->toChars()); goto Lfalse; } // use a struct as local function struct PushIdentsDg { static int dg(void *ctx, size_t n, Dsymbol *sm) { if (!sm) return 1; //printf("\t[%i] %s %s\n", i, sm->kind(), sm->toChars()); if (sm->ident) { if (sm->ident != Id::ctor && sm->ident != Id::dtor && sm->ident != Id::_postblit && memcmp(sm->ident->string, "__", 2) == 0) { return 0; } //printf("\t%s\n", sm->ident->toChars()); Identifiers *idents = (Identifiers *)ctx; /* Skip if already present in idents[] */ for (size_t j = 0; j < idents->dim; j++) { Identifier *id = (*idents)[j]; if (id == sm->ident) return 0; #ifdef DEBUG // Avoid using strcmp in the first place due to the performance impact in an O(N^2) loop. assert(strcmp(id->toChars(), sm->ident->toChars()) != 0); #endif } idents->push(sm->ident); } else { EnumDeclaration *ed = sm->isEnumDeclaration(); if (ed) { ScopeDsymbol::foreach(NULL, ed->members, &PushIdentsDg::dg, (Identifiers *)ctx); } } return 0; } }; Identifiers *idents = new Identifiers; ScopeDsymbol::foreach(sc, sds->members, &PushIdentsDg::dg, idents); ClassDeclaration *cd = sds->isClassDeclaration(); if (cd && e->ident == Id::allMembers) { struct PushBaseMembers { static void dg(ClassDeclaration *cd, Identifiers *idents) { for (size_t i = 0; i < cd->baseclasses->dim; i++) { ClassDeclaration *cb = (*cd->baseclasses)[i]->base; ScopeDsymbol::foreach(NULL, cb->members, &PushIdentsDg::dg, idents); if (cb->baseclasses->dim) dg(cb, idents); } } }; PushBaseMembers::dg(cd, idents); } // Turn Identifiers into StringExps reusing the allocated array assert(sizeof(Expressions) == sizeof(Identifiers)); Expressions *exps = (Expressions *)idents; for (size_t i = 0; i < idents->dim; i++) { Identifier *id = (*idents)[i]; StringExp *se = new StringExp(e->loc, id->toChars()); (*exps)[i] = se; } /* Making this a tuple is more flexible, as it can be statically unrolled. * To make an array literal, enclose __traits in [ ]: * [ __traits(allMembers, ...) ] */ Expression *ex = new TupleExp(e->loc, exps); ex = ex->semantic(sc); return ex; } else if (e->ident == Id::compiles) { /* Determine if all the objects - types, expressions, or symbols - * compile without error */ if (!dim) goto Lfalse; for (size_t i = 0; i < dim; i++) { unsigned errors = global.startGagging(); unsigned oldspec = global.speculativeGag; global.speculativeGag = global.gag; Scope *sc2 = sc->push(); sc2->speculative = true; sc2->flags = sc->flags & ~SCOPEctfe | SCOPEcompile; bool err = false; RootObject *o = (*e->args)[i]; Type *t = isType(o); Expression *ex = t ? t->toExpression() : isExpression(o); if (!ex && t) { Dsymbol *s; t->resolve(e->loc, sc2, &ex, &t, &s); if (t) { t->semantic(e->loc, sc2); if (t->ty == Terror) err = true; } else if (s && s->errors) err = true; } if (ex) { ex = ex->semantic(sc2); ex = resolvePropertiesOnly(sc2, ex); ex = ex->optimize(WANTvalue); ex = checkGC(sc2, ex); if (ex->op == TOKerror) err = true; } sc2->pop(); global.speculativeGag = oldspec; if (global.endGagging(errors) || err) { goto Lfalse; } } goto Ltrue; } else if (e->ident == Id::isSame) { /* Determine if two symbols are the same */ if (dim != 2) goto Ldimerror; if (!TemplateInstance::semanticTiargs(e->loc, sc, e->args, 0)) return new ErrorExp(); RootObject *o1 = (*e->args)[0]; RootObject *o2 = (*e->args)[1]; Dsymbol *s1 = getDsymbol(o1); Dsymbol *s2 = getDsymbol(o2); //printf("isSame: %s, %s\n", o1->toChars(), o2->toChars()); #if 0 printf("o1: %p\n", o1); printf("o2: %p\n", o2); if (!s1) { Expression *ea = isExpression(o1); if (ea) printf("%s\n", ea->toChars()); Type *ta = isType(o1); if (ta) printf("%s\n", ta->toChars()); goto Lfalse; } else printf("%s %s\n", s1->kind(), s1->toChars()); #endif if (!s1 && !s2) { Expression *ea1 = isExpression(o1); Expression *ea2 = isExpression(o2); if (ea1 && ea2) { if (ea1->equals(ea2)) goto Ltrue; } } if (!s1 || !s2) goto Lfalse; s1 = s1->toAlias(); s2 = s2->toAlias(); if (s1->isFuncAliasDeclaration()) s1 = ((FuncAliasDeclaration *)s1)->toAliasFunc(); if (s2->isFuncAliasDeclaration()) s2 = ((FuncAliasDeclaration *)s2)->toAliasFunc(); if (s1 == s2) goto Ltrue; else goto Lfalse; } else if (e->ident == Id::getUnitTests) { if (dim != 1) goto Ldimerror; RootObject *o = (*e->args)[0]; Dsymbol *s = getDsymbol(o); if (!s) { e->error("argument %s to __traits(getUnitTests) must be a module or aggregate", o->toChars()); goto Lfalse; } Import *imp = s->isImport(); if (imp) // Bugzilla 10990 s = imp->mod; ScopeDsymbol* scope = s->isScopeDsymbol(); if (!scope) { e->error("argument %s to __traits(getUnitTests) must be a module or aggregate, not a %s", s->toChars(), s->kind()); goto Lfalse; } Expressions* unitTests = new Expressions(); Dsymbols* symbols = scope->members; if (global.params.useUnitTests && symbols) { // Should actually be a set AA* uniqueUnitTests = NULL; collectUnitTests(symbols, uniqueUnitTests, unitTests); } TupleExp *tup = new TupleExp(e->loc, unitTests); return tup->semantic(sc); } else if(e->ident == Id::getVirtualIndex) { if (dim != 1) goto Ldimerror; RootObject *o = (*e->args)[0]; Dsymbol *s = getDsymbol(o); FuncDeclaration *fd; if (!s || (fd = s->isFuncDeclaration()) == NULL) { e->error("first argument to __traits(getVirtualIndex) must be a function"); goto Lfalse; } fd = fd->toAliasFunc(); // Neccessary to support multiple overloads. return new IntegerExp(e->loc, fd->vtblIndex, Type::tptrdiff_t); } else { if (const char *sub = (const char *)speller(e->ident->toChars(), &trait_search_fp, NULL, idchars)) e->error("unrecognized trait '%s', did you mean '%s'?", e->ident->toChars(), sub); else e->error("unrecognized trait '%s'", e->ident->toChars()); goto Lfalse; } return NULL; Ldimerror: e->error("wrong number of arguments %d", (int)dim); goto Lfalse; Lfalse: return new IntegerExp(e->loc, 0, Type::tbool); Ltrue: return new IntegerExp(e->loc, 1, Type::tbool); }
llvm::GlobalVariable * IrStruct::getInterfaceVtbl(BaseClass * b, bool new_instance, size_t interfaces_index) { ClassGlobalMap::iterator it = interfaceVtblMap.find(b->base); if (it != interfaceVtblMap.end()) return it->second; IF_LOG Logger::println("Building vtbl for implementation of interface %s in class %s", b->base->toPrettyChars(), aggrdecl->toPrettyChars()); LOG_SCOPE; ClassDeclaration* cd = aggrdecl->isClassDeclaration(); assert(cd && "not a class aggregate"); FuncDeclarations vtbl_array; b->fillVtbl(cd, &vtbl_array, new_instance); std::vector<llvm::Constant*> constants; constants.reserve(vtbl_array.dim); // start with the interface info VarDeclarationIter interfaces_idx(ClassDeclaration::classinfo->fields, 3); // index into the interfaces array llvm::Constant* idxs[2] = { DtoConstSize_t(0), DtoConstSize_t(interfaces_index) }; llvm::Constant* c = llvm::ConstantExpr::getGetElementPtr( getInterfaceArraySymbol(), idxs, true); constants.push_back(c); // add virtual function pointers size_t n = vtbl_array.dim; for (size_t i = 1; i < n; i++) { Dsymbol* dsym = static_cast<Dsymbol*>(vtbl_array.data[i]); if (dsym == NULL) { // FIXME // why is this null? // happens for mini/s.d constants.push_back(getNullValue(getVoidPtrType())); continue; } FuncDeclaration* fd = dsym->isFuncDeclaration(); assert(fd && "vtbl entry not a function"); assert((!fd->isAbstract() || fd->fbody) && "null symbol in interface implementation vtable"); fd->codegen(Type::sir); assert(fd->ir.irFunc && "invalid vtbl function"); constants.push_back(fd->ir.irFunc->func); } // build the vtbl constant llvm::Constant* vtbl_constant = LLConstantStruct::getAnon(gIR->context(), constants, false); // create the global variable to hold it llvm::GlobalValue::LinkageTypes _linkage = DtoExternalLinkage(aggrdecl); std::string mangle("_D"); mangle.append(cd->mangle()); mangle.append("11__interface"); mangle.append(b->base->mangle()); mangle.append("6__vtblZ"); llvm::GlobalVariable* GV = new llvm::GlobalVariable( *gIR->module, vtbl_constant->getType(), true, _linkage, vtbl_constant, mangle ); // insert into the vtbl map interfaceVtblMap.insert(std::make_pair(b->base, GV)); return GV; }
/********************************************** * Determine if it is @safe to cast e from tfrom to tto. * Params: * e = expression to be cast * tfrom = type of e * tto = type to cast e to * Returns: * true if @safe */ bool isSafeCast(Expression *e, Type *tfrom, Type *tto) { // Implicit conversions are always safe if (tfrom->implicitConvTo(tto)) return true; if (!tto->hasPointers()) return true; Type *ttob = tto->toBasetype(); if (ttob->ty == Tclass && tfrom->ty == Tclass) { ClassDeclaration *cdfrom = tfrom->isClassHandle(); ClassDeclaration *cdto = ttob->isClassHandle(); int offset; if (!cdfrom->isBaseOf(cdto, &offset)) return false; if (cdfrom->isCPPinterface() || cdto->isCPPinterface()) return false; if (!MODimplicitConv(tfrom->mod, ttob->mod)) return false; return true; } if (ttob->ty == Tarray && tfrom->ty == Tsarray) // Bugzilla 12502 tfrom = tfrom->nextOf()->arrayOf(); if ((ttob->ty == Tarray && tfrom->ty == Tarray) || (ttob->ty == Tpointer && tfrom->ty == Tpointer)) { Type *ttobn = ttob->nextOf()->toBasetype(); Type *tfromn = tfrom->nextOf()->toBasetype(); /* From void[] to anything mutable is unsafe because: * int*[] api; * void[] av = api; * int[] ai = cast(int[]) av; * ai[0] = 7; * *api[0] crash! */ if (tfromn->ty == Tvoid && ttobn->isMutable()) { if (ttob->ty == Tarray && e->op == TOKarrayliteral) return true; return false; } // If the struct is opaque we don't know about the struct members then the cast becomes unsafe if ((ttobn->ty == Tstruct && !((TypeStruct *)ttobn)->sym->members) || (tfromn->ty == Tstruct && !((TypeStruct *)tfromn)->sym->members)) return false; const bool frompointers = tfromn->hasPointers(); const bool topointers = ttobn->hasPointers(); if (frompointers && !topointers && ttobn->isMutable()) return false; if (!frompointers && topointers) return false; if (!topointers && ttobn->ty != Tfunction && tfromn->ty != Tfunction && (ttob->ty == Tarray || ttobn->size() <= tfromn->size()) && MODimplicitConv(tfromn->mod, ttobn->mod)) { return true; } } return false; }
void FuncDeclaration::toObjFile(int multiobj) { Symbol *s; func_t *f; Symbol *senter; Symbol *sexit; FuncDeclaration *func = this; ClassDeclaration *cd = func->parent->isClassDeclaration(); int reverse; int i; int has_arguments; //printf("FuncDeclaration::toObjFile(%p, %s.%s)\n", func, parent->toChars(), func->toChars()); #if 0 //printf("line = %d\n",func->getWhere() / LINEINC); EEcontext *ee = env->getEEcontext(); if (ee->EEcompile == 2) { if (ee->EElinnum < (func->getWhere() / LINEINC) || ee->EElinnum > (func->endwhere / LINEINC) ) return; // don't compile this function ee->EEfunc = func->toSymbol(); } #endif if (multiobj && !isStaticDtorDeclaration() && !isStaticCtorDeclaration()) { obj_append(this); return; } if (semanticRun >= 5) // if toObjFile() already run return; semanticRun = 5; if (!func->fbody) { return; } if (func->isUnitTestDeclaration() && !global.params.useUnitTests) return; if (global.params.verbose) printf("function %s\n",func->toChars()); s = func->toSymbol(); f = s->Sfunc; #if TARGET_WINDOS /* This is done so that the 'this' pointer on the stack is the same * distance away from the function parameters, so that an overriding * function can call the nested fdensure or fdrequire of its overridden function * and the stack offsets are the same. */ if (isVirtual() && (fensure || frequire)) f->Fflags3 |= Ffakeeh; #endif #if TARGET_OSX s->Sclass = SCcomdat; #else s->Sclass = SCglobal; #endif for (Dsymbol *p = parent; p; p = p->parent) { if (p->isTemplateInstance()) { s->Sclass = SCcomdat; break; } } if (isNested()) { // if (!(config.flags3 & CFG3pic)) // s->Sclass = SCstatic; f->Fflags3 |= Fnested; } else { const char *libname = (global.params.symdebug) ? global.params.debuglibname : global.params.defaultlibname; // Pull in RTL startup code if (func->isMain()) { objextdef("_main"); #if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_SOLARIS obj_ehsections(); // initialize exception handling sections #else objextdef("__acrtused_con"); #endif obj_includelib(libname); s->Sclass = SCglobal; } else if (strcmp(s->Sident, "main") == 0 && linkage == LINKc) s->Sclass = SCglobal; else if (func->isWinMain()) { objextdef("__acrtused"); obj_includelib(libname); s->Sclass = SCglobal; } // Pull in RTL startup code else if (func->isDllMain()) { objextdef("__acrtused_dll"); obj_includelib(libname); s->Sclass = SCglobal; } } cstate.CSpsymtab = &f->Flocsym; // Find module m for this function Module *m = NULL; for (Dsymbol *p = parent; p; p = p->parent) { m = p->isModule(); if (m) break; } IRState irs(m, func); Array deferToObj; // write these to OBJ file later irs.deferToObj = &deferToObj; TypeFunction *tf; enum RET retmethod; symbol *shidden = NULL; Symbol *sthis = NULL; tym_t tyf; tyf = tybasic(s->Stype->Tty); //printf("linkage = %d, tyf = x%x\n", linkage, tyf); reverse = tyrevfunc(s->Stype->Tty); assert(func->type->ty == Tfunction); tf = (TypeFunction *)(func->type); has_arguments = (tf->linkage == LINKd) && (tf->varargs == 1); retmethod = tf->retStyle(); if (retmethod == RETstack) { // If function returns a struct, put a pointer to that // as the first argument ::type *thidden = tf->next->pointerTo()->toCtype(); char hiddenparam[5+4+1]; static int hiddenparami; // how many we've generated so far sprintf(hiddenparam,"__HID%d",++hiddenparami); shidden = symbol_name(hiddenparam,SCparameter,thidden); shidden->Sflags |= SFLtrue | SFLfree; #if DMDV1 if (func->nrvo_can && func->nrvo_var && func->nrvo_var->nestedref) #else if (func->nrvo_can && func->nrvo_var && func->nrvo_var->nestedrefs.dim) #endif type_setcv(&shidden->Stype, shidden->Stype->Tty | mTYvolatile); irs.shidden = shidden; this->shidden = shidden; } if (vthis) { assert(!vthis->csym); sthis = vthis->toSymbol(); irs.sthis = sthis; if (!(f->Fflags3 & Fnested)) f->Fflags3 |= Fmember; } Symbol **params; unsigned pi; // Estimate number of parameters, pi pi = (v_arguments != NULL); if (parameters) pi += parameters->dim; // Allow extra 2 for sthis and shidden params = (Symbol **)alloca((pi + 2) * sizeof(Symbol *)); // Get the actual number of parameters, pi, and fill in the params[] pi = 0; if (v_arguments) { params[pi] = v_arguments->toSymbol(); pi += 1; } if (parameters) { for (i = 0; i < parameters->dim; i++) { VarDeclaration *v = (VarDeclaration *)parameters->data[i]; if (v->csym) { error("compiler error, parameter '%s', bugzilla 2962?", v->toChars()); assert(0); } params[pi + i] = v->toSymbol(); } pi += i; } if (reverse) { // Reverse params[] entries for (i = 0; i < pi/2; i++) { Symbol *sptmp; sptmp = params[i]; params[i] = params[pi - 1 - i]; params[pi - 1 - i] = sptmp; } } if (shidden) { #if 0 // shidden becomes last parameter params[pi] = shidden; #else // shidden becomes first parameter memmove(params + 1, params, pi * sizeof(params[0])); params[0] = shidden; #endif pi++; } if (sthis) { #if 0 // sthis becomes last parameter params[pi] = sthis; #else // sthis becomes first parameter memmove(params + 1, params, pi * sizeof(params[0])); params[0] = sthis; #endif pi++; } if ((global.params.isLinux || global.params.isOSX || global.params.isFreeBSD || global.params.isSolaris) && linkage != LINKd && shidden && sthis) { /* swap shidden and sthis */ Symbol *sp = params[0]; params[0] = params[1]; params[1] = sp; } for (i = 0; i < pi; i++) { Symbol *sp = params[i]; sp->Sclass = SCparameter; sp->Sflags &= ~SFLspill; sp->Sfl = FLpara; symbol_add(sp); } // First parameter goes in register if (pi) { Symbol *sp = params[0]; if ((tyf == TYjfunc || tyf == TYmfunc) && type_jparam(sp->Stype)) { sp->Sclass = SCfastpar; sp->Spreg = (tyf == TYjfunc) ? AX : CX; sp->Sfl = FLauto; //printf("'%s' is SCfastpar\n",sp->Sident); } } if (func->fbody) { block *b; Blockx bx; Statement *sbody; localgot = NULL; sbody = func->fbody; memset(&bx,0,sizeof(bx)); bx.startblock = block_calloc(); bx.curblock = bx.startblock; bx.funcsym = s; bx.scope_index = -1; bx.classdec = cd; bx.member = func; bx.module = getModule(); irs.blx = &bx; buildClosure(&irs); #if 0 if (func->isSynchronized()) { if (cd) { elem *esync; if (func->isStatic()) { // monitor is in ClassInfo esync = el_ptr(cd->toSymbol()); } else { // 'this' is the monitor esync = el_var(sthis); } if (func->isStatic() || sbody->usesEH() || !(config.flags2 & CFG2seh)) { // BUG: what if frequire or fensure uses EH? sbody = new SynchronizedStatement(func->loc, esync, sbody); } else { #if TARGET_WINDOS if (config.flags2 & CFG2seh) { /* The "jmonitor" uses an optimized exception handling frame * which is a little shorter than the more general EH frame. * It isn't strictly necessary. */ s->Sfunc->Fflags3 |= Fjmonitor; } #endif el_free(esync); } } else { error("synchronized function %s must be a member of a class", func->toChars()); } } #elif TARGET_WINDOS if (func->isSynchronized() && cd && config.flags2 & CFG2seh && !func->isStatic() && !sbody->usesEH()) { /* The "jmonitor" hack uses an optimized exception handling frame * which is a little shorter than the more general EH frame. */ s->Sfunc->Fflags3 |= Fjmonitor; } #endif sbody->toIR(&irs); bx.curblock->BC = BCret; f->Fstartblock = bx.startblock; // einit = el_combine(einit,bx.init); if (isCtorDeclaration()) { assert(sthis); for (b = f->Fstartblock; b; b = b->Bnext) { if (b->BC == BCret) { b->BC = BCretexp; b->Belem = el_combine(b->Belem, el_var(sthis)); } } } } // If static constructor if (isStaticConstructor()) { elem *e = el_una(OPucall, TYvoid, el_var(s)); ector = el_combine(ector, e); } // If static destructor if (isStaticDestructor()) { elem *e; #if STATICCTOR e = el_bin(OPcall, TYvoid, el_var(rtlsym[RTLSYM_FATEXIT]), el_ptr(s)); ector = el_combine(ector, e); dtorcount++; #else StaticDtorDeclaration *f = isStaticDtorDeclaration(); assert(f); if (f->vgate) { /* Increment destructor's vgate at construction time */ ectorgates.push(f); } e = el_una(OPucall, TYvoid, el_var(s)); edtor = el_combine(e, edtor); #endif } // If unit test if (isUnitTestDeclaration()) { elem *e = el_una(OPucall, TYvoid, el_var(s)); etest = el_combine(etest, e); } if (global.errors) return; writefunc(s); if (isExport()) obj_export(s, Poffset); for (i = 0; i < irs.deferToObj->dim; i++) { Dsymbol *s = (Dsymbol *)irs.deferToObj->data[i]; s->toObjFile(0); } #if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_SOLARIS // A hack to get a pointer to this function put in the .dtors segment if (ident && memcmp(ident->toChars(), "_STD", 4) == 0) obj_staticdtor(s); #endif #if DMDV2 if (irs.startaddress) { printf("Setting start address\n"); obj_startaddress(irs.startaddress); } #endif }
LLConstant * IrStruct::getClassInfoInterfaces() { IF_LOG Logger::println("Building ClassInfo.interfaces"); LOG_SCOPE; ClassDeclaration* cd = aggrdecl->isClassDeclaration(); assert(cd); size_t n = interfacesWithVtbls.size(); assert(stripModifiers(type)->irtype->isClass()->getNumInterfaceVtbls() == n && "inconsistent number of interface vtables in this class"); VarDeclarationIter interfaces_idx(ClassDeclaration::classinfo->fields, 3); if (n == 0) return getNullValue(DtoType(interfaces_idx->type)); // Build array of: // // struct Interface // { // ClassInfo classinfo; // void*[] vtbl; // ptrdiff_t offset; // } LLSmallVector<LLConstant*, 6> constants; constants.reserve(cd->vtblInterfaces->dim); LLType* classinfo_type = DtoType(ClassDeclaration::classinfo->type); LLType* voidptrptr_type = DtoType( Type::tvoid->pointerTo()->pointerTo()); VarDeclarationIter idx(ClassDeclaration::classinfo->fields, 3); LLStructType* interface_type = isaStruct(DtoType(idx->type->nextOf())); assert(interface_type); for (size_t i = 0; i < n; ++i) { BaseClass* it = interfacesWithVtbls[i]; IF_LOG Logger::println("Adding interface %s", it->base->toPrettyChars()); IrStruct* irinter = it->base->ir.irStruct; assert(irinter && "interface has null IrStruct"); IrTypeClass* itc = stripModifiers(irinter->type)->irtype->isClass(); assert(itc && "null interface IrTypeClass"); // classinfo LLConstant* ci = irinter->getClassInfoSymbol(); ci = DtoBitCast(ci, classinfo_type); // vtbl LLConstant* vtb; // interface get a null if (cd->isInterfaceDeclaration()) { vtb = DtoConstSlice(DtoConstSize_t(0), getNullValue(voidptrptr_type)); } else { ClassGlobalMap::iterator itv = interfaceVtblMap.find(it->base); assert(itv != interfaceVtblMap.end() && "interface vtbl not found"); vtb = itv->second; vtb = DtoBitCast(vtb, voidptrptr_type); vtb = DtoConstSlice(DtoConstSize_t(itc->getVtblSize()), vtb); } // offset LLConstant* off = DtoConstSize_t(it->offset); // create Interface struct LLConstant* inits[3] = { ci, vtb, off }; LLConstant* entry = LLConstantStruct::get(interface_type, llvm::makeArrayRef(inits, 3)); constants.push_back(entry); } // create Interface[N] LLArrayType* array_type = llvm::ArrayType::get(interface_type, n); // create and apply initializer LLConstant* arr = LLConstantArray::get(array_type, constants); classInterfacesArray->setInitializer(arr); // return null, only baseclass provide interfaces if (cd->vtblInterfaces->dim == 0) { return getNullValue(DtoType(interfaces_idx->type)); } // only the interface explicitly implemented by this class // (not super classes) should show in ClassInfo LLConstant* idxs[2] = { DtoConstSize_t(0), DtoConstSize_t(n - cd->vtblInterfaces->dim) }; LLConstant* ptr = llvm::ConstantExpr::getGetElementPtr( classInterfacesArray, idxs, true); // return as a slice return DtoConstSlice( DtoConstSize_t(cd->vtblInterfaces->dim), ptr ); }