//===--- GenClangDecl.cpp - Swift IRGen for imported Clang declarations ---===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// #include "IRGenModule.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ClangModuleLoader.h" #include "swift/AST/Expr.h" #include "swift/AST/IRGenOptions.h" #include "swift/AST/Stmt.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclGroup.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/GlobalDecl.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/CodeGen/ModuleBuilder.h" #include "clang/Sema/Sema.h" #include "llvm/ADT/SmallPtrSet.h" using namespace swift; using namespace irgen; namespace { class ClangDeclFinder : public clang::RecursiveASTVisitor { std::function callback; ClangModuleLoader *clangModuleLoader; public: template explicit ClangDeclFinder(Fn fn, ClangModuleLoader *clangModuleLoader) : callback(fn), clangModuleLoader(clangModuleLoader) {} bool VisitDeclRefExpr(clang::DeclRefExpr *DRE) { if (isa(DRE->getDecl()) || isa(DRE->getDecl())) { callback(DRE->getDecl()); } return true; } bool VisitMemberExpr(clang::MemberExpr *ME) { if (isa(ME->getMemberDecl()) || isa(ME->getMemberDecl()) || isa(ME->getMemberDecl())) { callback(ME->getMemberDecl()); } return true; } bool VisitFunctionDecl(clang::FunctionDecl *functionDecl) { for (auto paramDecl : functionDecl->parameters()) { if (paramDecl->hasDefaultArg()) { if (FuncDecl *defaultArgGenerator = clangModuleLoader->getDefaultArgGenerator(paramDecl)) { // Deconstruct the Swift function that was created in // SwiftDeclSynthesizer::makeDefaultArgument and extract the // underlying Clang function that was also synthesized. BraceStmt *body = defaultArgGenerator->getTypecheckedBody(); auto returnStmt = cast(cast(body->getSingleActiveElement())); auto callExpr = cast(returnStmt->getResult()); auto calledFuncDecl = cast(callExpr->getCalledValue()); auto calledClangFuncDecl = cast(calledFuncDecl->getClangDecl()); callback(calledClangFuncDecl); } } } return true; } bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *CXXCD) { callback(CXXCD); for (clang::CXXCtorInitializer *CXXCI : CXXCD->inits()) { if (clang::FieldDecl *FD = CXXCI->getMember()) { callback(FD); // A throwing constructor might throw after the field is initialized, // emitting additional cleanup code that destroys the field. Make sure // we record the destructor of the field in that case as it might need // to be potentially emitted. if (auto *recordType = FD->getType()->getAsCXXRecordDecl()) { if (auto *destructor = recordType->getDestructor()) { if (!destructor->isDeleted()) callback(destructor); } } } } return true; } bool VisitCXXConstructExpr(clang::CXXConstructExpr *CXXCE) { callback(CXXCE->getConstructor()); return true; } bool VisitCXXDeleteExpr(clang::CXXDeleteExpr *deleteExpr) { if (auto cxxRecord = deleteExpr->getDestroyedType()->getAsCXXRecordDecl()) if (auto dtor = cxxRecord->getDestructor()) callback(dtor); return true; } bool VisitVarDecl(clang::VarDecl *VD) { if (auto cxxRecord = VD->getType()->getAsCXXRecordDecl()) if (auto dtor = cxxRecord->getDestructor()) callback(dtor); return true; } bool VisitCXXBindTemporaryExpr(clang::CXXBindTemporaryExpr *BTE) { // This is a temporary value with a custom destructor. C++ will implicitly // call the destructor at some point. Make sure we emit IR for it. callback(BTE->getTemporary()->getDestructor()); return true; } bool VisitCXXNewExpr(clang::CXXNewExpr *NE) { callback(NE->getOperatorNew()); return true; } bool VisitBindingDecl(clang::BindingDecl *BD) { if (auto *holdingVar = BD->getHoldingVar()) { if (holdingVar->hasInit()) TraverseStmt(holdingVar->getInit()); } return true; } bool VisitCXXInheritedCtorInitExpr(clang::CXXInheritedCtorInitExpr *CIE) { if (auto ctor = CIE->getConstructor()) callback(ctor); return true; } // Do not traverse unevaluated expressions. Doing to might result in compile // errors if we try to instantiate an un-instantiatable template. bool TraverseCXXNoexceptExpr(clang::CXXNoexceptExpr *NEE) { return true; } bool TraverseCXXTypeidExpr(clang::CXXTypeidExpr *TIE) { if (TIE->isPotentiallyEvaluated()) clang::RecursiveASTVisitor::TraverseCXXTypeidExpr(TIE); return true; } bool TraverseRequiresExpr(clang::RequiresExpr *RE) { return true; } // Do not traverse type locs, as they might contain expressions that reference // code that should not be instantiated and/or emitted. bool TraverseTypeLoc(clang::TypeLoc TL) { return true; } bool shouldVisitTemplateInstantiations() const { return true; } bool shouldVisitImplicitCode() const { return true; } }; // If any (re)declaration of `decl` contains executable code, returns that // redeclaration; otherwise, returns nullptr. // In the case of a function, executable code is contained in the function // definition. In the case of a variable, executable code can be contained in // the initializer of the variable. clang::Decl *getDeclWithExecutableCode(clang::Decl *decl) { if (auto fd = dyn_cast(decl)) { const clang::FunctionDecl *definition; if (fd->hasBody(definition)) { return const_cast(definition); } // If this is a potentially not-yet-instantiated template, we might // still have a body. if (fd->getTemplateInstantiationPattern()) return fd; } else if (auto vd = dyn_cast(decl)) { clang::VarDecl *initializingDecl = vd->getInitializingDeclaration(); if (initializingDecl) { return initializingDecl; } } else if (auto fd = dyn_cast(decl)) { if(fd->hasInClassInitializer()) { return fd; } } return nullptr; } } // end anonymous namespace void IRGenModule::emitClangDecl(const clang::Decl *decl) { // Ignore this decl if we've seen it before. if (!GlobalClangDecls.insert(decl->getCanonicalDecl()).second) return; // Fast path for the case where `decl` doesn't contain executable code, so it // can't reference any other declarations that we would need to emit. if (getDeclWithExecutableCode(const_cast(decl)) == nullptr) { ClangCodeGen->HandleTopLevelDecl( clang::DeclGroupRef(const_cast(decl))); return; } SmallVector stack; stack.push_back(decl); auto callback = [&](const clang::Decl *D) { for (auto *DC = D->getDeclContext();; DC = DC->getParent()) { // Check that this is not a local declaration inside a function. if (DC->isFunctionOrMethod()) { return; } if (DC->isFileContext()) { break; } if (isa(DC)) { break; } if (isa(DC)) { break; } D = cast(DC); } if (!GlobalClangDecls.insert(D->getCanonicalDecl()).second) { return; } stack.push_back(D); }; ClangModuleLoader *clangModuleLoader = Context.getClangModuleLoader(); ClangDeclFinder refFinder(callback, clangModuleLoader); auto &clangSema = clangModuleLoader->getClangSema(); while (!stack.empty()) { auto *next = const_cast(stack.pop_back_val()); // If this is a static member of a class, it might be defined out of line. // If the class is templated, the definition of its static member might be // templated as well. If it is, instantiate it here. if (auto var = dyn_cast(next)) { if (var->isStaticDataMember() && var->getTemplateSpecializationKind() == clang::TemplateSpecializationKind::TSK_ImplicitInstantiation) clangSema.InstantiateVariableDefinition(var->getLocation(), var); } // If a function calls another method in a class template specialization, we // need to instantiate that other function. Do that here. if (auto *fn = dyn_cast(next)) { // Make sure that this method is part of a class template specialization. if (fn->getTemplateInstantiationPattern()) clangSema.InstantiateFunctionDefinition(fn->getLocation(), fn); } if (clang::Decl *executableDecl = getDeclWithExecutableCode(next)) { refFinder.TraverseDecl(executableDecl); next = executableDecl; } // Unfortunately, implicitly defined CXXDestructorDecls don't have a real // body, so we need to traverse these manually. if (auto *dtor = dyn_cast(next)) { if (dtor->isImplicit() && dtor->isDefaulted() && !dtor->isDeleted() && !dtor->doesThisDeclarationHaveABody()) clangSema.DefineImplicitDestructor(dtor->getLocation(), dtor); if (dtor->isImplicit() || dtor->hasBody()) { auto cxxRecord = dtor->getParent(); for (auto field : cxxRecord->fields()) { if (auto fieldCxxRecord = field->getType()->getAsCXXRecordDecl()) if (auto *fieldDtor = fieldCxxRecord->getDestructor()) callback(fieldDtor); } for (auto base : cxxRecord->bases()) { if (auto baseCxxRecord = base.getType()->getAsCXXRecordDecl()) if (auto *baseDtor = baseCxxRecord->getDestructor()) callback(baseDtor); } } } // If something from a C++ class is used, emit all virtual methods of this // class because they might be emitted in the vtable even if not used // directly from Swift. if (auto *record = dyn_cast(next->getDeclContext())) { if (auto *destructor = record->getDestructor()) { // Ensure virtual destructors have the body defined, even if they're // not used directly, as they might be referenced by the emitted vtable. if (destructor->isVirtual() && !destructor->isDeleted()) ensureImplicitCXXDestructorBodyIsDefined(destructor); } for (auto *method : record->methods()) { if (method->isVirtual()) { callback(method); } } } if (auto var = dyn_cast(next)) if (!var->isFileVarDecl()) continue; if (isa(next)) { continue; } ClangCodeGen->HandleTopLevelDecl(clang::DeclGroupRef(next)); } } llvm::Constant * IRGenModule::getAddrOfClangGlobalDecl(clang::GlobalDecl global, ForDefinition_t forDefinition) { // Register the decl with the clang code generator. if (auto decl = global.getDecl()) emitClangDecl(decl); return ClangCodeGen->GetAddrOfGlobal(global, (bool) forDefinition); } void IRGenModule::finalizeClangCodeGen() { // FIXME: We try to avoid looking for PragmaCommentDecls unless we need to, // since clang::DeclContext::decls_begin() can trigger expensive // de-serialization. if (Triple.isWindowsMSVCEnvironment() || Triple.isWindowsItaniumEnvironment() || IRGen.Opts.LLVMLTOKind != IRGenLLVMLTOKind::None) { // Ensure that code is emitted for any `PragmaCommentDecl`s. (These are // always guaranteed to be directly below the TranslationUnitDecl.) // In Clang, this happens automatically during the Sema phase, but here we // need to take care of it manually because our Clang CodeGenerator is not // attached to Clang Sema as an ASTConsumer. for (const auto *D : ClangASTContext->getTranslationUnitDecl()->decls()) { if (const auto *PCD = dyn_cast(D)) { emitClangDecl(PCD); } } } ClangCodeGen->HandleTranslationUnit( *const_cast(ClangASTContext)); } void IRGenModule::ensureImplicitCXXDestructorBodyIsDefined( clang::CXXDestructorDecl *destructor) { if (destructor->isUserProvided() || destructor->doesThisDeclarationHaveABody()) return; assert(!destructor->isDeleted() && "Swift cannot handle a type with no known destructor."); // Make sure we define the destructor so we have something to call. auto &sema = Context.getClangModuleLoader()->getClangSema(); sema.DefineImplicitDestructor(clang::SourceLocation(), destructor); } /// Retrieves the base classes of a C++ struct/class ordered by their offset in /// the derived type's memory layout. SmallVector irgen::getBasesAndOffsets(const clang::CXXRecordDecl *decl) { auto &layout = decl->getASTContext().getASTRecordLayout(decl); // Collect the offsets and sizes of base classes within the memory layout // of the derived class. SmallVector baseOffsetsAndSizes; for (auto base : decl->bases()) { if (base.isVirtual()) continue; auto baseType = base.getType().getCanonicalType(); auto baseRecordType = cast(baseType); auto baseRecord = baseRecordType->getAsCXXRecordDecl(); assert(baseRecord && "expected a base C++ record"); if (baseRecord->isEmpty()) continue; auto offset = Size(layout.getBaseClassOffset(baseRecord).getQuantity()); // A base type might have different size and data size (sizeof != dsize). // Make sure we are using data size here, since fields of the derived type // might be packed into the base's tail padding. auto size = Size(decl->getASTContext() .getTypeInfoDataSizeInChars(baseType) .Width.getQuantity()); baseOffsetsAndSizes.push_back({baseRecord, offset, size}); } // In C++, base classes might get reordered if the primary base was not // the first base type on the declaration of the class. llvm::sort(baseOffsetsAndSizes, [](const CXXBaseRecordLayout &lhs, const CXXBaseRecordLayout &rhs) { return lhs.offset < rhs.offset; }); return baseOffsetsAndSizes; }