| //===--- ParseableInterfaceSupport.cpp - swiftinterface files ------------===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 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 |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #define DEBUG_TYPE "textual-module-interface" |
| #include "swift/AST/ASTContext.h" |
| #include "swift/AST/Decl.h" |
| #include "swift/AST/DiagnosticsFrontend.h" |
| #include "swift/AST/DiagnosticsSema.h" |
| #include "swift/AST/ExistentialLayout.h" |
| #include "swift/AST/FileSystem.h" |
| #include "swift/AST/Module.h" |
| #include "swift/AST/ProtocolConformance.h" |
| #include "swift/Basic/STLExtras.h" |
| #include "swift/Frontend/Frontend.h" |
| #include "swift/Frontend/ParseableInterfaceSupport.h" |
| #include "swift/Frontend/PrintingDiagnosticConsumer.h" |
| #include "swift/SILOptimizer/PassManager/Passes.h" |
| #include "swift/Serialization/SerializationOptions.h" |
| #include "clang/Basic/Module.h" |
| #include "clang/Frontend/CompilerInstance.h" |
| #include "clang/Lex/Preprocessor.h" |
| #include "clang/Lex/HeaderSearch.h" |
| #include "llvm/ADT/Hashing.h" |
| #include "llvm/ADT/StringSet.h" |
| #include "llvm/Support/xxhash.h" |
| #include "llvm/Support/Debug.h" |
| #include "llvm/Support/CommandLine.h" |
| #include "llvm/Support/CrashRecoveryContext.h" |
| #include "llvm/Support/Path.h" |
| #include "llvm/Support/Regex.h" |
| #include "llvm/Support/StringSaver.h" |
| |
| using namespace swift; |
| using FileDependency = SerializationOptions::FileDependency; |
| |
| #define SWIFT_INTERFACE_FORMAT_VERSION_KEY "swift-interface-format-version" |
| #define SWIFT_TOOLS_VERSION_KEY "swift-tools-version" |
| #define SWIFT_MODULE_FLAGS_KEY "swift-module-flags" |
| |
| static swift::version::Version InterfaceFormatVersion({1, 0}); |
| |
| static bool |
| extractSwiftInterfaceVersionAndArgs(DiagnosticEngine &Diags, SourceLoc DiagLoc, |
| clang::vfs::FileSystem &FS, |
| StringRef SwiftInterfacePathIn, |
| swift::version::Version &Vers, |
| llvm::StringSaver &SubArgSaver, |
| SmallVectorImpl<const char *> &SubArgs) { |
| auto FileOrError = swift::vfs::getFileOrSTDIN(FS, SwiftInterfacePathIn); |
| if (!FileOrError) { |
| Diags.diagnose(DiagLoc, diag::error_open_input_file, |
| SwiftInterfacePathIn, FileOrError.getError().message()); |
| return true; |
| } |
| auto SB = FileOrError.get()->getBuffer(); |
| auto VersRe = getSwiftInterfaceFormatVersionRegex(); |
| auto FlagRe = getSwiftInterfaceModuleFlagsRegex(); |
| SmallVector<StringRef, 1> VersMatches, FlagMatches; |
| if (!VersRe.match(SB, &VersMatches)) { |
| Diags.diagnose(DiagLoc, |
| diag::error_extracting_version_from_parseable_interface); |
| return true; |
| } |
| if (!FlagRe.match(SB, &FlagMatches)) { |
| Diags.diagnose(DiagLoc, |
| diag::error_extracting_flags_from_parseable_interface); |
| return true; |
| } |
| assert(VersMatches.size() == 2); |
| assert(FlagMatches.size() == 2); |
| Vers = swift::version::Version(VersMatches[1], SourceLoc(), &Diags); |
| llvm::cl::TokenizeGNUCommandLine(FlagMatches[1], SubArgSaver, SubArgs); |
| return false; |
| } |
| |
| static std::unique_ptr<llvm::MemoryBuffer> |
| getBufferOfDependency(clang::vfs::FileSystem &FS, |
| StringRef ModulePath, StringRef DepPath, |
| DiagnosticEngine &Diags, SourceLoc DiagLoc) { |
| auto DepBuf = FS.getBufferForFile(DepPath, /*FileSize=*/-1, |
| /*RequiresNullTerminator=*/false); |
| if (!DepBuf) { |
| Diags.diagnose(DiagLoc, |
| diag::missing_dependency_of_parseable_module_interface, |
| DepPath, ModulePath, DepBuf.getError().message()); |
| return nullptr; |
| } |
| return std::move(DepBuf.get()); |
| } |
| |
| /// Construct a cache key for the .swiftmodule being generated. There is a |
| /// balance to be struck here between things that go in the cache key and |
| /// things that go in the "up to date" check of the cache entry. We want to |
| /// avoid fighting over a single cache entry too much when (say) running |
| /// different compiler versions on the same machine or different inputs |
| /// that happen to have the same short module name, so we will disambiguate |
| /// those in the key. But we want to invalidate and rebuild a cache entry |
| /// -- rather than making a new one and potentially filling up the cache |
| /// with dead entries -- when other factors change, such as the contents of |
| /// the .swiftinterface input or its dependencies. |
| static std::string getCacheHash(ASTContext &Ctx, |
| const CompilerInvocation &SubInvocation, |
| StringRef InPath) { |
| // Start with the compiler version (which will be either tag names or revs). |
| std::string vers = swift::version::getSwiftFullVersion( |
| Ctx.LangOpts.EffectiveLanguageVersion); |
| llvm::hash_code H = llvm::hash_value(vers); |
| |
| // Simplest representation of input "identity" (not content) is just a |
| // pathname, and probably all we can get from the VFS in this regard anyways. |
| H = llvm::hash_combine(H, InPath); |
| |
| // ClangImporterOpts does include the target CPU, which is redundant: we |
| // already have separate .swiftinterface files per target due to expanding |
| // preprocessing directives, but further specializing the cache key to that |
| // target is harmless and will not make any extra cache entries, so allow it. |
| H = llvm::hash_combine( |
| H, SubInvocation.getClangImporterOptions().getPCHHashComponents()); |
| |
| return llvm::APInt(64, H).toString(36, /*Signed=*/false); |
| } |
| |
| static CompilerInvocation |
| createInvocationForBuildingFromInterface(ASTContext &Ctx, StringRef ModuleName, |
| StringRef CacheDir, |
| StringRef PrebuiltCacheDir) { |
| auto &SearchPathOpts = Ctx.SearchPathOpts; |
| auto &LangOpts = Ctx.LangOpts; |
| |
| CompilerInvocation SubInvocation; |
| |
| // Start with a SubInvocation that copies various state from our |
| // invoking ASTContext. |
| SubInvocation.setImportSearchPaths(SearchPathOpts.ImportSearchPaths); |
| SubInvocation.setFrameworkSearchPaths(SearchPathOpts.FrameworkSearchPaths); |
| SubInvocation.setSDKPath(SearchPathOpts.SDKPath); |
| SubInvocation.setInputKind(InputFileKind::SwiftModuleInterface); |
| SubInvocation.setRuntimeResourcePath(SearchPathOpts.RuntimeResourcePath); |
| SubInvocation.setTargetTriple(LangOpts.Target); |
| |
| SubInvocation.setModuleName(ModuleName); |
| SubInvocation.setClangModuleCachePath(CacheDir); |
| SubInvocation.getFrontendOptions().PrebuiltModuleCachePath = PrebuiltCacheDir; |
| |
| // Inhibit warnings from the SubInvocation since we are assuming the user |
| // is not in a position to fix them. |
| SubInvocation.getDiagnosticOptions().SuppressWarnings = true; |
| |
| // Inherit this setting down so that it can affect error diagnostics (mostly |
| // by making them non-fatal). |
| SubInvocation.getLangOptions().DebuggerSupport = LangOpts.DebuggerSupport; |
| |
| // Disable this; deinitializers always get printed with `@objc` even in |
| // modules that don't import Foundation. |
| SubInvocation.getLangOptions().EnableObjCAttrRequiresFoundation = false; |
| |
| return SubInvocation; |
| } |
| |
| /// Calculate an output filename in \p SubInvocation's cache path that includes |
| /// a hash of relevant key data. |
| static void computeCachedOutputPath(ASTContext &Ctx, |
| const CompilerInvocation &SubInvocation, |
| StringRef InPath, |
| llvm::SmallString<256> &OutPath) { |
| OutPath = SubInvocation.getClangModuleCachePath(); |
| llvm::sys::path::append(OutPath, SubInvocation.getModuleName()); |
| OutPath.append("-"); |
| OutPath.append(getCacheHash(Ctx, SubInvocation, InPath)); |
| OutPath.append("."); |
| auto OutExt = file_types::getExtension(file_types::TY_SwiftModuleFile); |
| OutPath.append(OutExt); |
| } |
| |
| void ParseableInterfaceModuleLoader::configureSubInvocationInputsAndOutputs( |
| CompilerInvocation &SubInvocation, StringRef InPath, StringRef OutPath) { |
| auto &SubFEOpts = SubInvocation.getFrontendOptions(); |
| SubFEOpts.RequestedAction = FrontendOptions::ActionType::EmitModuleOnly; |
| SubFEOpts.EnableParseableModuleInterface = true; |
| SubFEOpts.InputsAndOutputs.addPrimaryInputFile(InPath); |
| SupplementaryOutputPaths SOPs; |
| SOPs.ModuleOutputPath = OutPath.str(); |
| |
| // Pick a primary output path that will cause problems to use. |
| StringRef MainOut = "/<unused>"; |
| SubFEOpts.InputsAndOutputs.setMainAndSupplementaryOutputs({MainOut}, {SOPs}); |
| } |
| |
| // Check that the output .swiftmodule file is at least as new as all the |
| // dependencies it read when it was built last time. |
| static bool |
| swiftModuleIsUpToDate(clang::vfs::FileSystem &FS, |
| std::pair<Identifier, SourceLoc> ModuleID, |
| StringRef OutPath, |
| DiagnosticEngine &Diags, |
| DependencyTracker *OuterTracker) { |
| |
| auto OutBuf = FS.getBufferForFile(OutPath); |
| if (!OutBuf) |
| return false; |
| |
| LLVM_DEBUG(llvm::dbgs() << "Validating deps of " << OutPath << "\n"); |
| SmallVector<FileDependency, 16> AllDeps; |
| auto VI = serialization::validateSerializedAST( |
| OutBuf.get()->getBuffer(), |
| /*ExtendedValidationInfo=*/nullptr, &AllDeps); |
| |
| if (VI.status != serialization::Status::Valid) |
| return false; |
| |
| assert(VI.name == ModuleID.first.str() && |
| "we built a module at this path with a different name?"); |
| |
| for (auto In : AllDeps) { |
| if (OuterTracker) |
| OuterTracker->addDependency(In.Path, /*IsSystem=*/false); |
| auto DepBuf = getBufferOfDependency(FS, OutPath, In.Path, Diags, |
| ModuleID.second); |
| if (!DepBuf || |
| DepBuf->getBufferSize() != In.Size || |
| xxHash64(DepBuf->getBuffer()) != In.Hash) { |
| LLVM_DEBUG(llvm::dbgs() << "Dep " << In.Path |
| << " is directly out of date\n"); |
| return false; |
| } |
| LLVM_DEBUG(llvm::dbgs() << "Dep " << In.Path << " is up to date\n"); |
| } |
| return true; |
| } |
| |
| /// Populate the provided \p Deps with \c FileDependency entries including: |
| /// |
| /// - \p InPath - The .swiftinterface input file |
| /// |
| /// - All the dependencies mentioned by \p SubInstance's DependencyTracker, |
| /// that were read while compiling the module. |
| /// |
| /// - For any file in the latter set that is itself a .swiftmodule |
| /// living in \p ModuleCachePath, all of _its_ dependencies, copied |
| /// out to avoid having to do recursive scanning when rechecking this |
| /// dependency in the future. |
| static bool |
| collectDepsForSerialization(clang::vfs::FileSystem &FS, |
| CompilerInstance &SubInstance, |
| StringRef InPath, StringRef ModuleCachePath, |
| SmallVectorImpl<FileDependency> &Deps, |
| DiagnosticEngine &Diags, SourceLoc DiagLoc, |
| DependencyTracker *OuterTracker) { |
| auto DTDeps = SubInstance.getDependencyTracker()->getDependencies(); |
| SmallVector<StringRef, 16> InitialDepNames(DTDeps.begin(), DTDeps.end()); |
| InitialDepNames.push_back(InPath); |
| llvm::StringSet<> AllDepNames; |
| for (auto const &DepName : InitialDepNames) { |
| if (AllDepNames.insert(DepName).second && OuterTracker) { |
| OuterTracker->addDependency(DepName, /*IsSystem=*/false); |
| } |
| auto DepBuf = getBufferOfDependency(FS, InPath, DepName, Diags, DiagLoc); |
| if (!DepBuf) { |
| return true; |
| } |
| uint64_t Size = DepBuf->getBufferSize(); |
| uint64_t Hash = xxHash64(DepBuf->getBuffer()); |
| Deps.push_back(FileDependency{Size, Hash, DepName}); |
| |
| if (ModuleCachePath.empty()) |
| continue; |
| |
| // If Dep is itself a .swiftmodule in the cache dir, pull out its deps |
| // and include them in our own, so we have a single-file view of |
| // transitive deps: removes redundancies, and avoids opening and reading |
| // multiple swiftmodules during future loads. |
| auto Ext = llvm::sys::path::extension(DepName); |
| auto Ty = file_types::lookupTypeForExtension(Ext); |
| if (Ty == file_types::TY_SwiftModuleFile && |
| DepName.startswith(ModuleCachePath)) { |
| SmallVector<FileDependency, 16> SubDeps; |
| auto VI = serialization::validateSerializedAST( |
| DepBuf->getBuffer(), |
| /*ExtendedValidationInfo=*/nullptr, &SubDeps); |
| if (VI.status != serialization::Status::Valid) { |
| Diags.diagnose(DiagLoc, |
| diag::error_extracting_dependencies_from_cached_module, |
| DepName); |
| return true; |
| } |
| for (auto const &SubDep : SubDeps) { |
| if (AllDepNames.insert(SubDep.Path).second) { |
| Deps.push_back(SubDep); |
| if (OuterTracker) |
| OuterTracker->addDependency(SubDep.Path, /*IsSystem=*/false); |
| } |
| } |
| } |
| } |
| return false; |
| } |
| |
| bool ParseableInterfaceModuleLoader::buildSwiftModuleFromSwiftInterface( |
| clang::vfs::FileSystem &FS, DiagnosticEngine &Diags, SourceLoc DiagLoc, |
| CompilerInvocation &SubInvocation, StringRef InPath, StringRef OutPath, |
| StringRef ModuleCachePath, DependencyTracker *OuterTracker, |
| bool ShouldSerializeDeps) { |
| bool SubError = false; |
| bool RunSuccess = llvm::CrashRecoveryContext().RunSafelyOnThread([&] { |
| // Note that we don't assume ModuleCachePath is the same as the Clang |
| // module cache path at this point. |
| if (!ModuleCachePath.empty()) |
| (void)llvm::sys::fs::create_directory(ModuleCachePath); |
| |
| configureSubInvocationInputsAndOutputs(SubInvocation, InPath, OutPath); |
| |
| FrontendOptions &FEOpts = SubInvocation.getFrontendOptions(); |
| const auto &InputInfo = FEOpts.InputsAndOutputs.firstInput(); |
| StringRef InPath = InputInfo.file(); |
| const auto &OutputInfo = |
| InputInfo.getPrimarySpecificPaths().SupplementaryOutputs; |
| StringRef OutPath = OutputInfo.ModuleOutputPath; |
| |
| llvm::BumpPtrAllocator SubArgsAlloc; |
| llvm::StringSaver SubArgSaver(SubArgsAlloc); |
| SmallVector<const char *, 16> SubArgs; |
| swift::version::Version Vers; |
| if (extractSwiftInterfaceVersionAndArgs(Diags, DiagLoc, FS, InPath, Vers, |
| SubArgSaver, SubArgs)) { |
| SubError = true; |
| return; |
| } |
| |
| // For now: we support anything with the same "major version" and assume |
| // minor versions might be interesting for debugging, or special-casing a |
| // compatible field variant. |
| if (Vers.asMajorVersion() != InterfaceFormatVersion.asMajorVersion()) { |
| Diags.diagnose(DiagLoc, |
| diag::unsupported_version_of_parseable_interface, |
| InPath, Vers); |
| SubError = true; |
| return; |
| } |
| |
| SmallString<32> ExpectedModuleName = SubInvocation.getModuleName(); |
| if (SubInvocation.parseArgs(SubArgs, Diags)) { |
| SubError = true; |
| return; |
| } |
| |
| if (SubInvocation.getModuleName() != ExpectedModuleName) { |
| auto DiagKind = diag::serialization_name_mismatch; |
| if (SubInvocation.getLangOptions().DebuggerSupport) |
| DiagKind = diag::serialization_name_mismatch_repl; |
| Diags.diagnose(DiagLoc, DiagKind, SubInvocation.getModuleName(), |
| ExpectedModuleName); |
| SubError = true; |
| return; |
| } |
| |
| // Optimize emitted modules. This has to happen after we parse arguments, |
| // because parseSILOpts would override the current optimization mode. |
| SubInvocation.getSILOptions().OptMode = OptimizationMode::ForSpeed; |
| |
| // Build the .swiftmodule; this is a _very_ abridged version of the logic in |
| // performCompile in libFrontendTool, specialized, to just the one |
| // module-serialization task we're trying to do here. |
| LLVM_DEBUG(llvm::dbgs() << "Setting up instance to compile " |
| << InPath << " to " << OutPath << "\n"); |
| CompilerInstance SubInstance; |
| SubInstance.getSourceMgr().setFileSystem(&FS); |
| |
| ForwardingDiagnosticConsumer FDC(Diags); |
| SubInstance.addDiagnosticConsumer(&FDC); |
| |
| SubInstance.createDependencyTracker(/*TrackSystemDeps=*/false); |
| if (SubInstance.setup(SubInvocation)) { |
| SubError = true; |
| return; |
| } |
| |
| LLVM_DEBUG(llvm::dbgs() << "Performing sema\n"); |
| SubInstance.performSema(); |
| if (SubInstance.getASTContext().hadError()) { |
| LLVM_DEBUG(llvm::dbgs() << "encountered errors\n"); |
| SubError = true; |
| return; |
| } |
| |
| SILOptions &SILOpts = SubInvocation.getSILOptions(); |
| auto Mod = SubInstance.getMainModule(); |
| auto SILMod = performSILGeneration(Mod, SILOpts); |
| if (!SILMod) { |
| LLVM_DEBUG(llvm::dbgs() << "SILGen did not produce a module\n"); |
| SubError = true; |
| return; |
| } |
| |
| // Setup the callbacks for serialization, which can occur during the |
| // optimization pipeline. |
| SerializationOptions SerializationOpts; |
| std::string OutPathStr = OutPath; |
| SerializationOpts.OutputPath = OutPathStr.c_str(); |
| SerializationOpts.ModuleLinkName = FEOpts.ModuleLinkName; |
| SmallVector<FileDependency, 16> Deps; |
| if (collectDepsForSerialization(FS, SubInstance, InPath, ModuleCachePath, |
| Deps, Diags, DiagLoc, OuterTracker)) { |
| SubError = true; |
| return; |
| } |
| if (ShouldSerializeDeps) |
| SerializationOpts.Dependencies = Deps; |
| SILMod->setSerializeSILAction([&]() { |
| serialize(Mod, SerializationOpts, SILMod.get()); |
| }); |
| |
| LLVM_DEBUG(llvm::dbgs() << "Running SIL processing passes\n"); |
| if (SubInstance.performSILProcessing(SILMod.get())) { |
| LLVM_DEBUG(llvm::dbgs() << "encountered errors\n"); |
| SubError = true; |
| return; |
| } |
| |
| SubError = Diags.hadAnyError(); |
| }); |
| return !RunSuccess || SubError; |
| } |
| |
| static bool serializedASTLooksValidOrCannotBeRead(clang::vfs::FileSystem &FS, |
| StringRef ModPath) { |
| auto ModBuf = FS.getBufferForFile(ModPath, /*FileSize=*/-1, |
| /*RequiresNullTerminator=*/false); |
| if (!ModBuf) |
| return ModBuf.getError() != std::errc::no_such_file_or_directory; |
| |
| auto VI = serialization::validateSerializedAST(ModBuf.get()->getBuffer()); |
| return VI.status == serialization::Status::Valid; |
| } |
| |
| /// Load a .swiftmodule associated with a .swiftinterface either from a |
| /// cache or by converting it in a subordinate \c CompilerInstance, caching |
| /// the results. |
| std::error_code ParseableInterfaceModuleLoader::findModuleFilesInDirectory( |
| AccessPathElem ModuleID, StringRef DirPath, StringRef ModuleFilename, |
| StringRef ModuleDocFilename, |
| std::unique_ptr<llvm::MemoryBuffer> *ModuleBuffer, |
| std::unique_ptr<llvm::MemoryBuffer> *ModuleDocBuffer) { |
| |
| namespace path = llvm::sys::path; |
| |
| // If running in OnlySerialized mode, ParseableInterfaceModuleLoader |
| // should not have been constructed at all. |
| assert(LoadMode != ModuleLoadingMode::OnlySerialized); |
| |
| auto &FS = *Ctx.SourceMgr.getFileSystem(); |
| auto &Diags = Ctx.Diags; |
| llvm::SmallString<256> ModPath, InPath, OutPath; |
| |
| // First check to see if the .swiftinterface exists at all. Bail if not. |
| ModPath = DirPath; |
| path::append(ModPath, ModuleFilename); |
| |
| auto Ext = file_types::getExtension(file_types::TY_SwiftParseableInterfaceFile); |
| InPath = ModPath; |
| path::replace_extension(InPath, Ext); |
| if (!FS.exists(InPath)) |
| return std::make_error_code(std::errc::no_such_file_or_directory); |
| |
| // Next, if we're in the load mode that prefers .swiftmodules, see if there's |
| // one here we can _likely_ load (validates OK). If so, bail early with |
| // errc::not_supported, so the next (serialized) loader in the chain will load |
| // it. Alternately, if there's a .swiftmodule present but we can't even read |
| // it (for whatever reason), we should let the other module loader diagnose |
| // it. |
| if (LoadMode == ModuleLoadingMode::PreferSerialized && |
| serializedASTLooksValidOrCannotBeRead(FS, ModPath)) { |
| return std::make_error_code(std::errc::not_supported); |
| } |
| |
| // If we have a prebuilt cache path, check that too if the interface comes |
| // from the SDK. |
| if (!PrebuiltCacheDir.empty()) { |
| StringRef SDKPath = Ctx.SearchPathOpts.SDKPath; |
| if (!SDKPath.empty() && hasPrefix(path::begin(InPath), |
| path::end(InPath), |
| path::begin(SDKPath), |
| path::end(SDKPath))) { |
| // Assemble the expected path: $PREBUILT_CACHE/Foo.swiftmodule or |
| // $PREBUILT_CACHE/Foo.swiftmodule/arch.swiftmodule. Note that there's no |
| // cache key here. |
| OutPath = PrebuiltCacheDir; |
| |
| // FIXME: Would it be possible to only have architecture-specific names |
| // here? Then we could skip this check. |
| StringRef InParentDirName = path::filename(path::parent_path(InPath)); |
| if (path::extension(InParentDirName) == ".swiftmodule") { |
| assert(path::stem(InParentDirName) == ModuleID.first.str()); |
| path::append(OutPath, InParentDirName); |
| } |
| path::append(OutPath, ModuleFilename); |
| |
| if (!swiftModuleIsUpToDate(FS, ModuleID, OutPath, Diags, |
| dependencyTracker)) { |
| OutPath.clear(); |
| } |
| } |
| } |
| |
| if (OutPath.empty()) { |
| // At this point we're either in PreferParseable mode or there's no credible |
| // adjacent .swiftmodule so we'll go ahead and start trying to convert the |
| // .swiftinterface. |
| |
| // Set up a _potential_ sub-invocation to consume the .swiftinterface and |
| // emit the .swiftmodule. |
| CompilerInvocation SubInvocation = |
| createInvocationForBuildingFromInterface(Ctx, ModuleID.first.str(), |
| CacheDir, PrebuiltCacheDir); |
| computeCachedOutputPath(Ctx, SubInvocation, InPath, OutPath); |
| |
| // Evaluate if we need to run this sub-invocation, and if so run it. |
| if (!swiftModuleIsUpToDate(FS, ModuleID, OutPath, Diags, |
| dependencyTracker)) { |
| if (buildSwiftModuleFromSwiftInterface(FS, Diags, ModuleID.second, |
| SubInvocation, InPath, OutPath, |
| CacheDir, dependencyTracker, |
| /*ShouldSerializeDeps*/true)) |
| return std::make_error_code(std::errc::invalid_argument); |
| } |
| } |
| |
| // Finish off by delegating back up to the SerializedModuleLoaderBase |
| // routine that can load the recently-manufactured serialized module. |
| LLVM_DEBUG(llvm::dbgs() << "Loading " << OutPath |
| << " via normal module loader\n"); |
| llvm::SmallString<256> DocPath{DirPath}; |
| path::append(DocPath, ModuleDocFilename); |
| auto ErrorCode = SerializedModuleLoaderBase::openModuleFiles( |
| ModuleID, OutPath, DocPath, ModuleBuffer, ModuleDocBuffer); |
| LLVM_DEBUG(llvm::dbgs() << "Loaded " << OutPath |
| << " via normal module loader"); |
| if (ErrorCode) { |
| LLVM_DEBUG(llvm::dbgs() << " with error: " << ErrorCode.message()); |
| } |
| LLVM_DEBUG(llvm::dbgs() << "\n"); |
| return ErrorCode; |
| } |
| |
| bool |
| ParseableInterfaceModuleLoader::buildSwiftModuleFromSwiftInterface( |
| ASTContext &Ctx, StringRef CacheDir, StringRef PrebuiltCacheDir, |
| StringRef ModuleName, StringRef InPath, StringRef OutPath) { |
| CompilerInvocation SubInvocation = |
| createInvocationForBuildingFromInterface(Ctx, ModuleName, CacheDir, |
| PrebuiltCacheDir); |
| |
| auto &FS = *Ctx.SourceMgr.getFileSystem(); |
| auto &Diags = Ctx.Diags; |
| // FIXME: We don't really want to ignore dependencies here, but we have to |
| // identify which ones are important, and make them relocatable |
| // (SDK-relative) if we want to ship the built swiftmodules to another |
| // machine. Just leave them out for now. |
| return buildSwiftModuleFromSwiftInterface(FS, Diags, /*DiagLoc*/SourceLoc(), |
| SubInvocation, InPath, OutPath, |
| /*CachePath*/"", |
| /*OuterTracker*/nullptr, |
| /*ShouldSerializeDeps*/false); |
| } |
| |
| /// Diagnose any scoped imports in \p imports, i.e. those with a non-empty |
| /// access path. These are not yet supported by parseable interfaces, since the |
| /// information about the declaration kind is not preserved through the binary |
| /// serialization that happens as an intermediate step in non-whole-module |
| /// builds. |
| /// |
| /// These come from declarations like `import class FooKit.MainFooController`. |
| static void diagnoseScopedImports(DiagnosticEngine &diags, |
| ArrayRef<ModuleDecl::ImportedModule> imports){ |
| for (const ModuleDecl::ImportedModule &importPair : imports) { |
| if (importPair.first.empty()) |
| continue; |
| diags.diagnose(importPair.first.front().second, |
| diag::parseable_interface_scoped_import_unsupported); |
| } |
| } |
| |
| /// Prints to \p out a comment containing a format version number, tool version |
| /// string as well as any relevant command-line flags in \p Opts used to |
| /// construct \p M. |
| static void printToolVersionAndFlagsComment(raw_ostream &out, |
| ParseableInterfaceOptions const &Opts, |
| ModuleDecl *M) { |
| auto &Ctx = M->getASTContext(); |
| auto ToolsVersion = swift::version::getSwiftFullVersion( |
| Ctx.LangOpts.EffectiveLanguageVersion); |
| out << "// " SWIFT_INTERFACE_FORMAT_VERSION_KEY ": " |
| << InterfaceFormatVersion << "\n"; |
| out << "// " SWIFT_TOOLS_VERSION_KEY ": " |
| << ToolsVersion << "\n"; |
| out << "// " SWIFT_MODULE_FLAGS_KEY ": " |
| << Opts.ParseableInterfaceFlags << "\n"; |
| } |
| |
| llvm::Regex swift::getSwiftInterfaceFormatVersionRegex() { |
| return llvm::Regex("^// " SWIFT_INTERFACE_FORMAT_VERSION_KEY |
| ": ([0-9\\.]+)$", llvm::Regex::Newline); |
| } |
| |
| llvm::Regex swift::getSwiftInterfaceModuleFlagsRegex() { |
| return llvm::Regex("^// " SWIFT_MODULE_FLAGS_KEY ":(.*)$", |
| llvm::Regex::Newline); |
| } |
| |
| /// Extract the specified-or-defaulted -module-cache-path that winds up in |
| /// the clang importer, for reuse as the .swiftmodule cache path when |
| /// building a ParseableInterfaceModuleLoader. |
| std::string |
| swift::getModuleCachePathFromClang(const clang::CompilerInstance &Clang) { |
| if (!Clang.hasPreprocessor()) |
| return ""; |
| std::string SpecificModuleCachePath = Clang.getPreprocessor() |
| .getHeaderSearchInfo() |
| .getModuleCachePath(); |
| |
| // The returned-from-clang module cache path includes a suffix directory |
| // that is specific to the clang version and invocation; we want the |
| // directory above that. |
| return llvm::sys::path::parent_path(SpecificModuleCachePath); |
| } |
| |
| /// Prints the imported modules in \p M to \p out in the form of \c import |
| /// source declarations. |
| static void printImports(raw_ostream &out, ModuleDecl *M) { |
| // FIXME: This is very similar to what's in Serializer::writeInputBlock, but |
| // it's not obvious what higher-level optimization would be factored out here. |
| SmallVector<ModuleDecl::ImportedModule, 8> allImports; |
| M->getImportedModules(allImports, ModuleDecl::ImportFilter::All); |
| ModuleDecl::removeDuplicateImports(allImports); |
| diagnoseScopedImports(M->getASTContext().Diags, allImports); |
| |
| // Collect the public imports as a subset so that we can mark them with |
| // '@_exported'. |
| SmallVector<ModuleDecl::ImportedModule, 8> publicImports; |
| M->getImportedModules(publicImports, ModuleDecl::ImportFilter::Public); |
| llvm::SmallSet<ModuleDecl::ImportedModule, 8, |
| ModuleDecl::OrderImportedModules> publicImportSet; |
| publicImportSet.insert(publicImports.begin(), publicImports.end()); |
| |
| for (auto import : allImports) { |
| if (import.second->isOnoneSupportModule() || |
| import.second->isBuiltinModule()) { |
| continue; |
| } |
| |
| if (publicImportSet.count(import)) |
| out << "@_exported "; |
| out << "import "; |
| import.second->getReverseFullModuleName().printForward(out); |
| |
| // Write the access path we should be honoring but aren't. |
| // (See diagnoseScopedImports above.) |
| if (!import.first.empty()) { |
| out << "/*"; |
| for (const auto &accessPathElem : import.first) |
| out << "." << accessPathElem.first; |
| out << "*/"; |
| } |
| |
| out << "\n"; |
| } |
| } |
| |
| // FIXME: Copied from ASTPrinter.cpp... |
| static bool isPublicOrUsableFromInline(const ValueDecl *VD) { |
| AccessScope scope = |
| VD->getFormalAccessScope(/*useDC*/nullptr, |
| /*treatUsableFromInlineAsPublic*/true); |
| return scope.isPublic(); |
| } |
| |
| static bool isPublicOrUsableFromInline(Type ty) { |
| // Note the double negative here: we're looking for any referenced decls that |
| // are *not* public-or-usableFromInline. |
| return !ty.findIf([](Type typePart) -> bool { |
| // FIXME: If we have an internal typealias for a non-internal type, we ought |
| // to be able to print it by desugaring. |
| if (auto *aliasTy = dyn_cast<TypeAliasType>(typePart.getPointer())) |
| return !isPublicOrUsableFromInline(aliasTy->getDecl()); |
| if (auto *nominal = typePart->getAnyNominal()) |
| return !isPublicOrUsableFromInline(nominal); |
| return false; |
| }); |
| } |
| |
| namespace { |
| /// Collects protocols that are conformed to by a particular nominal. Since |
| /// ASTPrinter will only print the public ones, the non-public ones get left by |
| /// the wayside. This is a problem when a non-public protocol inherits from a |
| /// public protocol; the generated parseable interface still needs to make that |
| /// dependency public. |
| /// |
| /// The solution implemented here is to generate synthetic extensions that |
| /// declare the extra conformances. This isn't perfect (it loses the sugared |
| /// spelling of the protocol type, as well as the locality in the file), but it |
| /// does work. |
| class InheritedProtocolCollector { |
| static const StringLiteral DummyProtocolName; |
| |
| /// Protocols that will be included by the ASTPrinter without any extra work. |
| SmallVector<ProtocolDecl *, 8> IncludedProtocols; |
| /// Protocols that will not be printed by the ASTPrinter. |
| SmallVector<ProtocolDecl *, 8> ExtraProtocols; |
| /// Protocols that can be printed, but whose conformances are constrained with |
| /// something that \e can't be printed. |
| SmallVector<const ProtocolType *, 8> ConditionalConformanceProtocols; |
| |
| /// For each type in \p directlyInherited, classify the protocols it refers to |
| /// as included for printing or not, and record them in the appropriate |
| /// vectors. |
| void recordProtocols(ArrayRef<TypeLoc> directlyInherited) { |
| for (TypeLoc inherited : directlyInherited) { |
| Type inheritedTy = inherited.getType(); |
| if (!inheritedTy || !inheritedTy->isExistentialType()) |
| continue; |
| |
| bool canPrintNormally = isPublicOrUsableFromInline(inheritedTy); |
| SmallVectorImpl<ProtocolDecl *> &whichProtocols = |
| canPrintNormally ? IncludedProtocols : ExtraProtocols; |
| |
| ExistentialLayout layout = inheritedTy->getExistentialLayout(); |
| for (ProtocolType *protoTy : layout.getProtocols()) |
| whichProtocols.push_back(protoTy->getDecl()); |
| // FIXME: This ignores layout constraints, but currently we don't support |
| // any of those besides 'AnyObject'. |
| } |
| } |
| |
| /// For each type in \p directlyInherited, record any protocols that we would |
| /// have printed in ConditionalConformanceProtocols. |
| void recordConditionalConformances(ArrayRef<TypeLoc> directlyInherited) { |
| for (TypeLoc inherited : directlyInherited) { |
| Type inheritedTy = inherited.getType(); |
| if (!inheritedTy || !inheritedTy->isExistentialType()) |
| continue; |
| |
| ExistentialLayout layout = inheritedTy->getExistentialLayout(); |
| for (ProtocolType *protoTy : layout.getProtocols()) |
| if (isPublicOrUsableFromInline(protoTy)) |
| ConditionalConformanceProtocols.push_back(protoTy); |
| // FIXME: This ignores layout constraints, but currently we don't support |
| // any of those besides 'AnyObject'. |
| } |
| } |
| |
| public: |
| using PerTypeMap = llvm::MapVector<const NominalTypeDecl *, |
| InheritedProtocolCollector>; |
| |
| /// Given that we're about to print \p D, record its protocols in \p map. |
| /// |
| /// \sa recordProtocols |
| static void collectProtocols(PerTypeMap &map, const Decl *D) { |
| ArrayRef<TypeLoc> directlyInherited; |
| const NominalTypeDecl *nominal; |
| const IterableDeclContext *memberContext; |
| |
| if ((nominal = dyn_cast<NominalTypeDecl>(D))) { |
| directlyInherited = nominal->getInherited(); |
| memberContext = nominal; |
| |
| } else if (auto *extension = dyn_cast<ExtensionDecl>(D)) { |
| if (extension->isConstrainedExtension()) { |
| // Conditional conformances never apply to inherited protocols, nor |
| // can they provide unconditional conformances that might be used in |
| // other extensions. |
| return; |
| } |
| nominal = extension->getExtendedNominal(); |
| directlyInherited = extension->getInherited(); |
| memberContext = extension; |
| |
| } else { |
| return; |
| } |
| |
| if (!isPublicOrUsableFromInline(nominal)) |
| return; |
| |
| map[nominal].recordProtocols(directlyInherited); |
| |
| // Recurse to find any nested types. |
| for (const Decl *member : memberContext->getMembers()) |
| collectProtocols(map, member); |
| } |
| |
| /// If \p D is an extension providing conditional conformances, record those |
| /// in \p map. |
| /// |
| /// \sa recordConditionalConformances |
| static void collectSkippedConditionalConformances(PerTypeMap &map, |
| const Decl *D) { |
| auto *extension = dyn_cast<ExtensionDecl>(D); |
| if (!extension || !extension->isConstrainedExtension()) |
| return; |
| |
| const NominalTypeDecl *nominal = extension->getExtendedNominal(); |
| if (!isPublicOrUsableFromInline(nominal)) |
| return; |
| |
| map[nominal].recordConditionalConformances(extension->getInherited()); |
| // No recursion here because extensions are never nested. |
| } |
| |
| /// Returns true if the conformance of \p nominal to \p proto is declared in |
| /// module \p M. |
| static bool conformanceDeclaredInModule(ModuleDecl *M, |
| const NominalTypeDecl *nominal, |
| ProtocolDecl *proto) { |
| SmallVector<ProtocolConformance *, 4> conformances; |
| nominal->lookupConformance(M, proto, conformances); |
| return llvm::all_of(conformances, |
| [M](const ProtocolConformance *conformance) -> bool { |
| return M == conformance->getDeclContext()->getParentModule(); |
| }); |
| } |
| |
| /// If there were any public protocols that need to be printed (i.e. they |
| /// weren't conformed to explicitly or inherited by another printed protocol), |
| /// do so now by printing a dummy extension on \p nominal to \p out. |
| void |
| printSynthesizedExtensionIfNeeded(raw_ostream &out, |
| const PrintOptions &printOptions, |
| ModuleDecl *M, |
| const NominalTypeDecl *nominal) const { |
| if (ExtraProtocols.empty()) |
| return; |
| |
| SmallPtrSet<ProtocolDecl *, 16> handledProtocols; |
| |
| // First record all protocols that have already been handled. |
| for (ProtocolDecl *proto : IncludedProtocols) { |
| proto->walkInheritedProtocols( |
| [&handledProtocols](ProtocolDecl *inherited) -> TypeWalker::Action { |
| handledProtocols.insert(inherited); |
| return TypeWalker::Action::Continue; |
| }); |
| } |
| |
| // Then walk the remaining ones, and see what we need to print. |
| // Note: We could do this in one pass, but the logic is easier to |
| // understand if we build up the list and then print it, even if it takes |
| // a bit more memory. |
| SmallVector<ProtocolDecl *, 16> protocolsToPrint; |
| for (ProtocolDecl *proto : ExtraProtocols) { |
| proto->walkInheritedProtocols( |
| [&](ProtocolDecl *inherited) -> TypeWalker::Action { |
| if (!handledProtocols.insert(inherited).second) |
| return TypeWalker::Action::SkipChildren; |
| |
| if (isPublicOrUsableFromInline(inherited) && |
| conformanceDeclaredInModule(M, nominal, inherited)) { |
| protocolsToPrint.push_back(inherited); |
| return TypeWalker::Action::SkipChildren; |
| } |
| |
| return TypeWalker::Action::Continue; |
| }); |
| } |
| if (protocolsToPrint.empty()) |
| return; |
| |
| out << "extension "; |
| nominal->getDeclaredType().print(out, printOptions); |
| out << " : "; |
| swift::interleave(protocolsToPrint, |
| [&out, &printOptions](ProtocolDecl *proto) { |
| proto->getDeclaredType()->print(out, printOptions); |
| }, [&out] { out << ", "; }); |
| out << " {}\n"; |
| } |
| |
| /// If there were any conditional conformances that couldn't be printed, |
| /// make a dummy extension that conforms to all of them, constrained by a |
| /// fake protocol. |
| bool printInaccessibleConformanceExtensionIfNeeded( |
| raw_ostream &out, const PrintOptions &printOptions, |
| const NominalTypeDecl *nominal) const { |
| if (ConditionalConformanceProtocols.empty()) |
| return false; |
| assert(nominal->isGenericContext()); |
| |
| out << "extension "; |
| nominal->getDeclaredType().print(out, printOptions); |
| out << " : "; |
| swift::interleave(ConditionalConformanceProtocols, |
| [&out, &printOptions](const ProtocolType *protoTy) { |
| protoTy->print(out, printOptions); |
| }, [&out] { out << ", "; }); |
| out << " where " |
| << nominal->getGenericSignature()->getGenericParams().front()->getName() |
| << " : " << DummyProtocolName << " {}\n"; |
| return true; |
| } |
| |
| /// Print a fake protocol declaration for use by |
| /// #printInaccessibleConformanceExtensionIfNeeded. |
| static void printDummyProtocolDeclaration(raw_ostream &out) { |
| out << "\n@usableFromInline\ninternal protocol " << DummyProtocolName |
| << " {}\n"; |
| } |
| }; |
| |
| const StringLiteral InheritedProtocolCollector::DummyProtocolName = |
| "_ConstraintThatIsNotPartOfTheAPIOfThisLibrary"; |
| } // end anonymous namespace |
| |
| bool swift::emitParseableInterface(raw_ostream &out, |
| ParseableInterfaceOptions const &Opts, |
| ModuleDecl *M) { |
| assert(M); |
| |
| printToolVersionAndFlagsComment(out, Opts, M); |
| printImports(out, M); |
| |
| const PrintOptions printOptions = PrintOptions::printParseableInterfaceFile(); |
| InheritedProtocolCollector::PerTypeMap inheritedProtocolMap; |
| |
| SmallVector<Decl *, 16> topLevelDecls; |
| M->getTopLevelDecls(topLevelDecls); |
| for (const Decl *D : topLevelDecls) { |
| if (!D->shouldPrintInContext(printOptions) || |
| !printOptions.CurrentPrintabilityChecker->shouldPrint(D, printOptions)){ |
| InheritedProtocolCollector::collectSkippedConditionalConformances( |
| inheritedProtocolMap, D); |
| continue; |
| } |
| |
| D->print(out, printOptions); |
| out << "\n"; |
| |
| InheritedProtocolCollector::collectProtocols(inheritedProtocolMap, D); |
| } |
| |
| // Print dummy extensions for any protocols that were indirectly conformed to. |
| bool needDummyProtocolDeclaration = false; |
| for (const auto &nominalAndCollector : inheritedProtocolMap) { |
| const NominalTypeDecl *nominal = nominalAndCollector.first; |
| const InheritedProtocolCollector &collector = nominalAndCollector.second; |
| collector.printSynthesizedExtensionIfNeeded(out, printOptions, M, nominal); |
| needDummyProtocolDeclaration |= |
| collector.printInaccessibleConformanceExtensionIfNeeded(out, |
| printOptions, |
| nominal); |
| } |
| if (needDummyProtocolDeclaration) |
| InheritedProtocolCollector::printDummyProtocolDeclaration(out); |
| |
| return false; |
| } |