//===-------- FrontendSourceFileDepGraphFactory.cpp -----------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2018 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 // //===----------------------------------------------------------------------===// // This file holds the code to build a SourceFileDepGraph in the frontend. // This graph captures relationships between definitions and uses, and // it is written to a file which is read by the driver in order to decide which // source files require recompilation. #include "FrontendSourceFileDepGraphFactory.h" // may not all be needed #include "swift/AST/ASTContext.h" #include "swift/AST/ASTMangler.h" #include "swift/AST/AbstractSourceFileDepGraphFactory.h" #include "swift/AST/Decl.h" #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/DiagnosticsFrontend.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/FileSystem.h" #include "swift/AST/FineGrainedDependencies.h" #include "swift/AST/FineGrainedDependencyFormat.h" #include "swift/AST/Module.h" #include "swift/AST/ModuleLoader.h" #include "swift/AST/NameLookup.h" #include "swift/AST/SourceFile.h" #include "swift/AST/Types.h" #include "swift/Basic/FileSystem.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/ReferenceDependencyKeys.h" #include "swift/Demangling/Demangle.h" #include "swift/Frontend/FrontendOptions.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/YAMLParser.h" using namespace swift; using namespace fine_grained_dependencies; //============================================================================== // MARK: Constructing from a SourceFile //============================================================================== //============================================================================== // MARK: Helpers for key construction that must be in frontend //============================================================================== template static std::string getBaseName(const DeclT *decl) { return decl->getBaseName().userFacingName().str(); } static std::string mangleTypeAsContext(const NominalTypeDecl *NTD) { Mangle::ASTMangler Mangler; return !NTD ? "" : Mangler.mangleTypeAsContextUSR(NTD); } //============================================================================== // MARK: DependencyKey - creation for Decls //============================================================================== template DependencyKey DependencyKey::createForProvidedEntityInterface(Entity entity) { return DependencyKey( kindArg, DeclAspect::interface, DependencyKey::computeContextForProvidedEntity(entity), DependencyKey::computeNameForProvidedEntity(entity)); } //============================================================================== // MARK: computeContextForProvidedEntity //============================================================================== template std::string DependencyKey::computeContextForProvidedEntity(Entity) { // Context field is not used for most kinds return ""; } // \ref nominal dependencies are created from a Decl and use the context field. template <> std::string DependencyKey::computeContextForProvidedEntity< NodeKind::nominal, NominalTypeDecl const *>(NominalTypeDecl const *D) { return mangleTypeAsContext(D); } /// \ref potentialMember dependencies are created from a Decl and use the /// context field. template <> std::string DependencyKey::computeContextForProvidedEntity( const NominalTypeDecl *D) { return mangleTypeAsContext(D); } template <> std::string DependencyKey::computeContextForProvidedEntity< NodeKind::member, const NominalTypeDecl *>(const NominalTypeDecl *holder) { return mangleTypeAsContext(holder); } /// \ref member dependencies are created from a pair and use the context field. template <> std::string DependencyKey::computeContextForProvidedEntity< NodeKind::member, std::pair>( std::pair holderAndMember) { return computeContextForProvidedEntity( holderAndMember.first); } // Linux compiler requires the following: template std::string DependencyKey::computeContextForProvidedEntity(StringRef); //============================================================================== // MARK: computeNameForProvidedEntity //============================================================================== template <> std::string DependencyKey::computeNameForProvidedEntity(StringRef swiftDeps) { assert(!swiftDeps.empty()); return swiftDeps.str(); } template <> std::string DependencyKey::computeNameForProvidedEntity( const PrecedenceGroupDecl *D) { return D->getName().str().str(); } template <> std::string DependencyKey::computeNameForProvidedEntity< NodeKind::topLevel, FuncDecl const *>(const FuncDecl *D) { return getBaseName(D); } template <> std::string DependencyKey::computeNameForProvidedEntity< NodeKind::topLevel, OperatorDecl const *>(const OperatorDecl *D) { return D->getName().str().str(); } template <> std::string DependencyKey::computeNameForProvidedEntity< NodeKind::topLevel, NominalTypeDecl const *>(const NominalTypeDecl *D) { return D->getName().str().str(); } template <> std::string DependencyKey::computeNameForProvidedEntity< NodeKind::topLevel, ValueDecl const *>(const ValueDecl *D) { return getBaseName(D); } template <> std::string DependencyKey::computeNameForProvidedEntity< NodeKind::dynamicLookup, ValueDecl const *>(const ValueDecl *D) { return getBaseName(D); } template <> std::string DependencyKey::computeNameForProvidedEntity< NodeKind::nominal, NominalTypeDecl const *>(const NominalTypeDecl *D) { return ""; } template <> std::string DependencyKey::computeNameForProvidedEntity( const NominalTypeDecl *D) { return ""; } template <> std::string DependencyKey::computeNameForProvidedEntity< NodeKind::member, std::pair>( std::pair holderAndMember) { return getBaseName(holderAndMember.second); } //============================================================================== // MARK: Entry point into frontend graph construction //============================================================================== bool fine_grained_dependencies::withReferenceDependencies( llvm::PointerUnion MSF, const DependencyTracker &depTracker, StringRef outputPath, bool alsoEmitDotFile, llvm::function_ref cont) { if (auto *MD = MSF.dyn_cast()) { SourceFileDepGraph g = ModuleDepGraphFactory(MD, alsoEmitDotFile).construct(); return cont(std::move(g)); } else { auto *SF = MSF.get(); SourceFileDepGraph g = FrontendSourceFileDepGraphFactory( SF, outputPath, depTracker, alsoEmitDotFile) .construct(); return cont(std::move(g)); } } //============================================================================== // MARK: FrontendSourceFileDepGraphFactory //============================================================================== FrontendSourceFileDepGraphFactory::FrontendSourceFileDepGraphFactory( const SourceFile *SF, StringRef outputPath, const DependencyTracker &depTracker, const bool alsoEmitDotFile) : AbstractSourceFileDepGraphFactory( SF->getASTContext().hadError(), outputPath, SF->getInterfaceHash(), alsoEmitDotFile, SF->getASTContext().Diags), SF(SF), depTracker(depTracker) {} //============================================================================== // MARK: FrontendSourceFileDepGraphFactory - adding collections of defined Decls //============================================================================== //============================================================================== // MARK: DeclFinder //============================================================================== namespace { /// Takes all the Decls in a SourceFile, and collects them into buckets by /// groups of DeclKinds. Also casts them to more specific types struct DeclFinder { // The extracted Decls: ConstPtrVec extensions; ConstPtrVec operators; ConstPtrVec precedenceGroups; ConstPtrVec topNominals; ConstPtrVec topValues; ConstPtrVec allNominals; ConstPtrVec potentialMemberHolders; ConstPtrVec memberOperatorDecls; ConstPtrPairVec valuesInExtensions; ConstPtrVec classMembers; using LookupClassMember = llvm::function_ref; public: /// Construct me and separates the Decls. // clang-format off DeclFinder(ArrayRef topLevelDecls, LookupClassMember lookupClassMember) { for (const Decl *const D : topLevelDecls) { select(D, extensions) || select(D, operators) || select( D, precedenceGroups) || select(D, topNominals) || select(D, topValues); } // clang-format on // The order is important because some of these use instance variables // computed by others. findNominalsFromExtensions(); findNominalsInTopNominals(); findValuesInExtensions(); findClassMembers(lookupClassMember); } private: /// Extensions may contain nominals and operators. void findNominalsFromExtensions() { for (auto *ED : extensions) { const auto *const NTD = ED->getExtendedNominal(); if (NTD) findNominalsAndOperatorsIn(NTD, ED); } } /// Top-level nominals may contain nominals and operators. void findNominalsInTopNominals() { for (const auto *const NTD : topNominals) findNominalsAndOperatorsIn(NTD); } /// Any nominal may contain nominals and operators. /// (indirectly recursive) void findNominalsAndOperatorsIn(const NominalTypeDecl *const NTD, const ExtensionDecl *ED = nullptr) { allNominals.push_back(NTD); potentialMemberHolders.push_back(NTD); findNominalsAndOperatorsInMembers(ED ? ED->getMembers() : NTD->getMembers()); } /// Search through the members to find nominals and operators. /// (indirectly recursive) /// TODO: clean this up, maybe recurse separately for each purpose. void findNominalsAndOperatorsInMembers(const DeclRange members) { for (const Decl *const D : members) { auto *VD = dyn_cast(D); if (!VD) continue; if (VD->getName().isOperator()) memberOperatorDecls.push_back(cast(D)); else if (const auto *const NTD = dyn_cast(D)) findNominalsAndOperatorsIn(NTD); } } /// Extensions may contain ValueDecls. void findValuesInExtensions() { for (const auto *ED : extensions) { const auto *const NTD = ED->getExtendedNominal(); if (!NTD) { continue; } for (const auto *member : ED->getMembers()) { const auto *VD = dyn_cast(member); if (!VD || !VD->hasName()) { continue; } if (const auto *const NTD = ED->getExtendedNominal()) { valuesInExtensions.push_back(std::make_pair(NTD, VD)); } } } } /// Class members are needed for dynamic lookup dependency nodes. void findClassMembers(LookupClassMember lookup) { struct Collector : public VisibleDeclConsumer { ConstPtrVec &classMembers; Collector(ConstPtrVec &classMembers) : classMembers(classMembers) {} void foundDecl(ValueDecl *VD, DeclVisibilityKind, DynamicLookupInfo) override { classMembers.push_back(VD); } } collector{classMembers}; lookup(collector); } /// Check \p D to see if it is one of the DeclKinds in the template /// arguments. If so, cast it to DesiredDeclType and add it to foundDecls. /// \returns true if successful. template bool select(const Decl *const D, ConstPtrVec &foundDecls) { if (D->getKind() == firstKind) { foundDecls.push_back(cast(D)); return true; } return select(D, foundDecls); } /// Terminate the template recursion. template bool select(const Decl *const D, ConstPtrVec &foundDecls) { return false; } }; } // namespace void FrontendSourceFileDepGraphFactory::addAllDefinedDecls() { // TODO: express the multiple provides and depends streams with variadic // templates // Many kinds of Decls become top-level depends. DeclFinder declFinder(SF->getTopLevelDecls(), [this](VisibleDeclConsumer &consumer) { SF->lookupClassMembers({}, consumer); }); addAllDefinedDeclsOfAGivenType( declFinder.precedenceGroups); addAllDefinedDeclsOfAGivenType( declFinder.memberOperatorDecls); addAllDefinedDeclsOfAGivenType(declFinder.operators); addAllDefinedDeclsOfAGivenType(declFinder.topNominals); addAllDefinedDeclsOfAGivenType(declFinder.topValues); addAllDefinedDeclsOfAGivenType(declFinder.allNominals); addAllDefinedDeclsOfAGivenType( declFinder.potentialMemberHolders); addAllDefinedDeclsOfAGivenType( declFinder.valuesInExtensions); addAllDefinedDeclsOfAGivenType( declFinder.classMembers); } //============================================================================== // MARK: FrontendSourceFileDepGraphFactory - adding collections of used Decls //============================================================================== namespace { /// Extracts uses out of a SourceFile class UsedDeclEnumerator { const SourceFile *SF; const DependencyTracker &depTracker; StringRef swiftDeps; /// Cache these for efficiency const DependencyKey sourceFileInterface; const DependencyKey sourceFileImplementation; function_ref createDefUse; public: UsedDeclEnumerator( const SourceFile *SF, const DependencyTracker &depTracker, StringRef swiftDeps, function_ref createDefUse) : SF(SF), depTracker(depTracker), swiftDeps(swiftDeps), sourceFileInterface(DependencyKey::createKeyForWholeSourceFile( DeclAspect::interface, swiftDeps)), sourceFileImplementation(DependencyKey::createKeyForWholeSourceFile( DeclAspect::implementation, swiftDeps)), createDefUse(createDefUse) {} public: void enumerateAllUses() { auto &Ctx = SF->getASTContext(); Ctx.evaluator.enumerateReferencesInFile(SF, [&](const auto &ref) { std::string name = ref.name.userFacingName().str(); const auto *nominal = ref.subject; using Kind = evaluator::DependencyCollector::Reference::Kind; switch (ref.kind) { case Kind::Empty: case Kind::Tombstone: llvm_unreachable("Cannot enumerate dead reference!"); case Kind::TopLevel: return enumerateUse("", name); case Kind::Dynamic: return enumerateUse("", name); case Kind::PotentialMember: { std::string context = DependencyKey::computeContextForProvidedEntity< NodeKind::potentialMember>(nominal); return enumerateUse(context, ""); } case Kind::UsedMember: { std::string context = DependencyKey::computeContextForProvidedEntity( nominal); return enumerateUse(context, name); } } }); enumerateExternalUses(); enumerateNominalUses(); } private: template void enumerateUse(StringRef context, StringRef name) { // Assume that what is depended-upon is the interface createDefUse( DependencyKey(kind, DeclAspect::interface, context.str(), name.str()), sourceFileImplementation); } void enumerateNominalUses() { auto &Ctx = SF->getASTContext(); Ctx.evaluator.enumerateReferencesInFile(SF, [&](const auto &ref) { const NominalTypeDecl *subject = ref.subject; if (!subject) { return; } std::string context = DependencyKey::computeContextForProvidedEntity( subject); enumerateUse(context, ""); }); } void enumerateExternalUses() { for (StringRef s : depTracker.getIncrementalDependencies()) enumerateUse("", s); for (StringRef s : depTracker.getDependencies()) enumerateUse("", s); } }; } // end namespace void FrontendSourceFileDepGraphFactory::addAllUsedDecls() { UsedDeclEnumerator(SF, depTracker, swiftDeps, [&](const DependencyKey &def, const DependencyKey &use) { addAUsedDecl(def, use); }) .enumerateAllUses(); } //============================================================================== // MARK: ModuleDepGraphFactory //============================================================================== ModuleDepGraphFactory::ModuleDepGraphFactory(const ModuleDecl *Mod, bool emitDot) : AbstractSourceFileDepGraphFactory(Mod->getASTContext().hadError(), Mod->getNameStr(), Fingerprint::ZERO(), emitDot, Mod->getASTContext().Diags), Mod(Mod) { // Since a fingerprint only summarizes the state of the module but not // the state of its fingerprinted sub-declarations, and since a module // contains no state other than sub-declarations, its fingerprint does not // matter and can just be some arbitrary value. Should it be the case that a // change in a declaration that does not have a fingerprint must cause // a rebuild of a file outside of the module, this assumption will need // to be revisited. } void ModuleDepGraphFactory::addAllDefinedDecls() { // TODO: express the multiple provides and depends streams with variadic // templates // Many kinds of Decls become top-level depends. SmallVector TopLevelDecls; Mod->getTopLevelDecls(TopLevelDecls); DeclFinder declFinder(TopLevelDecls, [this](VisibleDeclConsumer &consumer) { return Mod->lookupClassMembers({}, consumer); }); addAllDefinedDeclsOfAGivenType( declFinder.precedenceGroups); addAllDefinedDeclsOfAGivenType( declFinder.memberOperatorDecls); addAllDefinedDeclsOfAGivenType(declFinder.operators); addAllDefinedDeclsOfAGivenType(declFinder.topNominals); addAllDefinedDeclsOfAGivenType(declFinder.topValues); addAllDefinedDeclsOfAGivenType(declFinder.allNominals); addAllDefinedDeclsOfAGivenType( declFinder.potentialMemberHolders); addAllDefinedDeclsOfAGivenType( declFinder.valuesInExtensions); addAllDefinedDeclsOfAGivenType( declFinder.classMembers); }