diff --git a/include/swift/AST/DiagnosticsClangImporter.def b/include/swift/AST/DiagnosticsClangImporter.def index 9bbbc8314dbd0..762fbb55408b7 100644 --- a/include/swift/AST/DiagnosticsClangImporter.def +++ b/include/swift/AST/DiagnosticsClangImporter.def @@ -53,6 +53,9 @@ ERROR(bridging_header_error,Fatal, WARNING(could_not_rewrite_bridging_header,none, "failed to serialize bridging header; " "target may not be debuggable outside of its original project", ()) +ERROR(bridging_header_pch_error,Fatal, + "failed to emit PCH file '%0' for bridging header '%1'", + (StringRef, StringRef)) WARNING(invalid_swift_name_method,none, "too %select{few|many}0 parameters in swift_name attribute (expected %1; " @@ -76,6 +79,11 @@ WARNING(unresolvable_clang_decl,none, "imported declaration '%0' could not be mapped to '%1'", (StringRef, StringRef)) +WARNING(implicit_bridging_header_imported_from_module,none, + "implicit import of bridging header '%0' via module %1 " + "is deprecated and will be removed in a later version of Swift", + (StringRef, Identifier)) + #ifndef DIAG_NO_UNDEF # if defined(DIAG) # undef DIAG diff --git a/include/swift/ClangImporter/ClangImporter.h b/include/swift/ClangImporter/ClangImporter.h index 955e5077a30e8..3f8b746a34465 100644 --- a/include/swift/ClangImporter/ClangImporter.h +++ b/include/swift/ClangImporter/ClangImporter.h @@ -201,13 +201,16 @@ class ClangImporter final : public ClangModuleLoader { /// \param diagLoc A location to attach any diagnostics to if import fails. /// \param trackParsedSymbols If true, tracks decls and macros that were /// parsed from the bridging header. + /// \param implicitImport If true, indicates that this import was implicit + /// from a reference in a module file (deprecated behaviour). /// /// \returns true if there was an error importing the header. /// /// \sa getImportedHeaderModule bool importBridgingHeader(StringRef header, ModuleDecl *adapter, SourceLoc diagLoc = {}, - bool trackParsedSymbols = false); + bool trackParsedSymbols = false, + bool implicitImport = false); /// Returns the module that contains imports and declarations from all loaded /// Objective-C header files. @@ -218,6 +221,14 @@ class ClangImporter final : public ClangModuleLoader { std::string getBridgingHeaderContents(StringRef headerPath, off_t &fileSize, time_t &fileModTime); + /// Makes a temporary replica of the ClangImporter's CompilerInstance, reads + /// an Objective-C header file into the replica and emits a PCH file of its + /// content. Delegates to clang for everything except construction of the + /// replica. + /// + /// \sa clang::GeneratePCHAction + bool emitBridgingPCH(StringRef headerPath, StringRef outputPCHPath); + const clang::Module *getClangOwningModule(ClangNode Node) const; bool hasTypedef(const clang::Decl *typeDecl) const; diff --git a/include/swift/ClangImporter/ClangImporterOptions.h b/include/swift/ClangImporter/ClangImporterOptions.h index a53d497079574..0e7f662b346e4 100644 --- a/include/swift/ClangImporter/ClangImporterOptions.h +++ b/include/swift/ClangImporter/ClangImporterOptions.h @@ -35,6 +35,9 @@ class ClangImporterOptions { /// Equivalent to Clang's -mcpu=. std::string TargetCPU; + // The bridging header or PCH that will be imported. + std::string BridgingHeader; + /// \see Mode enum class Modes { /// Set up Clang for importing modules into Swift and generating IR from diff --git a/include/swift/Driver/Action.h b/include/swift/Driver/Action.h index 105f649611001..e34a3eedde316 100644 --- a/include/swift/Driver/Action.h +++ b/include/swift/Driver/Action.h @@ -48,9 +48,10 @@ class Action { REPLJob, LinkJob, GenerateDSYMJob, + GeneratePCHJob, JobFirst=CompileJob, - JobLast=GenerateDSYMJob + JobLast=GeneratePCHJob }; static const char *getClassName(ActionClass AC); @@ -268,6 +269,17 @@ class GenerateDSYMJobAction : public JobAction { } }; +class GeneratePCHJobAction : public JobAction { + virtual void anchor(); +public: + explicit GeneratePCHJobAction(Action *Input) + : JobAction(Action::GeneratePCHJob, Input, types::TY_PCH) {} + + static bool classof(const Action *A) { + return A->getKind() == Action::GeneratePCHJob; + } +}; + class LinkJobAction : public JobAction { virtual void anchor(); LinkKind Kind; diff --git a/include/swift/Driver/ToolChain.h b/include/swift/Driver/ToolChain.h index 046d71582ef6c..676d79e409a2b 100644 --- a/include/swift/Driver/ToolChain.h +++ b/include/swift/Driver/ToolChain.h @@ -122,6 +122,9 @@ class ToolChain { constructInvocation(const GenerateDSYMJobAction &job, const JobContext &context) const; virtual InvocationInfo + constructInvocation(const GeneratePCHJobAction &job, + const JobContext &context) const; + virtual InvocationInfo constructInvocation(const AutolinkExtractJobAction &job, const JobContext &context) const; virtual InvocationInfo diff --git a/include/swift/Driver/Types.def b/include/swift/Driver/Types.def index 8590abc8e2412..7c6925d9984fc 100644 --- a/include/swift/Driver/Types.def +++ b/include/swift/Driver/Types.def @@ -60,6 +60,7 @@ TYPE("remap", Remapping, "remap", "") // Misc types TYPE("pcm", ClangModuleFile, "pcm", "") +TYPE("pch", PCH, "pch", "") TYPE("none", Nothing, "", "") #undef TYPE diff --git a/include/swift/Frontend/FrontendOptions.h b/include/swift/Frontend/FrontendOptions.h index 20fb24e585442..2b96f0ceaa6b3 100644 --- a/include/swift/Frontend/FrontendOptions.h +++ b/include/swift/Frontend/FrontendOptions.h @@ -149,6 +149,8 @@ class FrontendOptions { /// Parse, type-check, and dump type refinement context hierarchy DumpTypeRefinementContexts, + EmitPCH, ///< Emit PCH of imported bridging header + EmitSILGen, ///< Emit raw SIL EmitSIL, ///< Emit canonical SIL diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 50f201dd0c94d..adc7f75ac46ca 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -233,6 +233,9 @@ def disable_modules_validate_system_headers : Flag<["-"], "disable-modules-valid def emit_verbose_sil : Flag<["-"], "emit-verbose-sil">, HelpText<"Emit locations during SIL emission">; +def emit_pch : Flag<["-"], "emit-pch">, + HelpText<"Emit PCH for imported Objective-C header file">, ModeOpt; + def enable_sil_ownership : Flag<["-"], "enable-sil-ownership">, HelpText<"Enable the SIL Ownership Model">; diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index 09d312070a00a..75f2a649bbfcb 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -237,6 +237,14 @@ def disable_swift_bridge_attr : Flag<["-"], "disable-swift-bridge-attr">, Flags<[FrontendOption, HelpHidden]>, HelpText<"Disable using the swift bridge attribute">; +def enable_bridging_pch : Flag<["-"], "enable-bridging-pch">, + Flags<[HelpHidden]>, + HelpText<"Enable automatic generation of bridging PCH files">; + +def disable_bridging_pch : Flag<["-"], "disable-bridging-pch">, + Flags<[HelpHidden]>, + HelpText<"Disable automatic generation of bridging PCH files">; + // Diagnostic control options def suppress_warnings : Flag<["-"], "suppress-warnings">, Flags<[FrontendOption]>, diff --git a/include/swift/Strings.h b/include/swift/Strings.h index 286d3edf727a8..8de45e1598ca2 100644 --- a/include/swift/Strings.h +++ b/include/swift/Strings.h @@ -18,6 +18,8 @@ namespace swift { static const char SERIALIZED_MODULE_EXTENSION[] = "swiftmodule"; /// The extension for serialized documentation comments. static const char SERIALIZED_MODULE_DOC_EXTENSION[] = "swiftdoc"; + /// The extension for PCH files. + static const char PCH_EXTENSION[] = "pch"; /// The extension for SIL files. static const char SIL_EXTENSION[] = "sil"; /// The extension for SIB files. diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index a5d5c105d720f..76839239aea39 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -35,6 +35,7 @@ #include "swift/Parse/Lexer.h" #include "swift/Parse/Parser.h" #include "swift/Config.h" +#include "swift/Strings.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Mangle.h" #include "clang/Basic/CharInfo.h" @@ -123,9 +124,25 @@ namespace { } }; + class PCHDeserializationCallbacks : public clang::ASTDeserializationListener { + ClangImporter::Implementation &Impl; + public: + explicit PCHDeserializationCallbacks(ClangImporter::Implementation &impl) + : Impl(impl) {} + void ModuleImportRead(clang::serialization::SubmoduleID ID, + clang::SourceLocation ImportLoc) { + if (Impl.IsReadingBridgingPCH) { + Impl.PCHImportedSubmodules.push_back(ID); + } + } + }; + class HeaderParsingASTConsumer : public clang::ASTConsumer { SmallVector DeclGroups; + PCHDeserializationCallbacks PCHCallbacks; public: + explicit HeaderParsingASTConsumer(ClangImporter::Implementation &impl) + : PCHCallbacks(impl) {} void HandleTopLevelDeclInObjCContainer(clang::DeclGroupRef decls) override { DeclGroups.push_back(decls); @@ -135,15 +152,40 @@ namespace { return DeclGroups; } + clang::ASTDeserializationListener *GetASTDeserializationListener() override { + return &PCHCallbacks; + } + void reset() { DeclGroups.clear(); } }; class ParsingAction : public clang::ASTFrontendAction { + ASTContext &Ctx; + ClangImporter &Importer; + ClangImporter::Implementation &Impl; + public: + explicit ParsingAction(ASTContext &ctx, + ClangImporter &importer, + ClangImporter::Implementation &impl) + : Ctx(ctx), Importer(importer), Impl(impl) {} std::unique_ptr CreateASTConsumer(clang::CompilerInstance &CI, StringRef InFile) override { - return llvm::make_unique(); + return llvm::make_unique(Impl); + } + bool BeginSourceFileAction(CompilerInstance &CI, + StringRef Filename) override { + // Prefer frameworks over plain headers. + // We add search paths here instead of when building the initial invocation + // so that (a) we use the same code as search paths for imported modules, + // and (b) search paths are always added after -Xcc options. + SearchPathOptions &searchPathOpts = Ctx.SearchPathOpts; + for (auto path : searchPathOpts.FrameworkSearchPaths) + Importer.addSearchPath(path, /*isFramework*/true); + for (auto path : searchPathOpts.ImportSearchPaths) + Importer.addSearchPath(path, /*isFramework*/false); + return true; } }; @@ -318,6 +360,14 @@ getNormalInvocationArguments(std::vector &invocationArgStrs, auto languageVersion = ctx.LangOpts.EffectiveLanguageVersion; + if (llvm::sys::path::extension(importerOpts.BridgingHeader).endswith( + PCH_EXTENSION)) { + invocationArgStrs.insert( + invocationArgStrs.end(), + { "-include-pch", importerOpts.BridgingHeader } + ); + } + // Construct the invocation arguments for the current target. // Add target-independent options first. invocationArgStrs.insert( @@ -622,6 +672,11 @@ ClangImporter::create(ASTContext &ctx, for (auto &argStr : invocationArgStrs) invocationArgs.push_back(argStr.c_str()); + if (llvm::sys::path::extension(importerOpts.BridgingHeader).endswith( + PCH_EXTENSION)) { + importer->Impl.IsReadingBridgingPCH = true; + } + // FIXME: These can't be controlled from the command line. llvm::IntrusiveRefCntPtr diagnosticOpts{ new clang::DiagnosticOptions @@ -662,7 +717,8 @@ ClangImporter::create(ASTContext &ctx, // Install a Clang module file extension to build Swift name lookup tables. invocation->getFrontendOpts().ModuleFileExtensions.push_back( - new SwiftNameLookupExtension(importer->Impl.LookupTables, + new SwiftNameLookupExtension(importer->Impl.BridgingHeaderLookupTable, + importer->Impl.LookupTables, importer->Impl.SwiftContext, importer->Impl.platformAvailability, importer->Impl.InferImportAsMember)); @@ -680,7 +736,8 @@ ClangImporter::create(ASTContext &ctx, instance.setInvocation(&*invocation); // Create the associated action. - importer->Impl.Action.reset(new ParsingAction); + importer->Impl.Action.reset(new ParsingAction(ctx, *importer, + importer->Impl)); auto *action = importer->Impl.Action.get(); // Execute the action. We effectively inline most of @@ -735,16 +792,6 @@ ClangImporter::create(ASTContext &ctx, importer->Impl.SwiftContext, importer->Impl.platformAvailability, importer->Impl.getClangSema(), importer->Impl.InferImportAsMember)); - // Prefer frameworks over plain headers. - // We add search paths here instead of when building the initial invocation - // so that (a) we use the same code as search paths for imported modules, - // and (b) search paths are always added after -Xcc options. - SearchPathOptions &searchPathOpts = ctx.SearchPathOpts; - for (auto path : searchPathOpts.FrameworkSearchPaths) - importer->addSearchPath(path, /*isFramework*/true); - for (auto path : searchPathOpts.ImportSearchPaths) - importer->addSearchPath(path, /*isFramework*/false); - // FIXME: These decls are not being parsed correctly since (a) some of the // callbacks are still being added, and (b) the logic to parse them has // changed. @@ -754,7 +801,7 @@ ClangImporter::create(ASTContext &ctx, importer->Impl.addBridgeHeaderTopLevelDecls(D); if (auto named = dyn_cast(D)) { - addEntryToLookupTable(importer->Impl.BridgingHeaderLookupTable, named, + addEntryToLookupTable(*importer->Impl.BridgingHeaderLookupTable, named, *importer->Impl.nameImporter); } } @@ -791,6 +838,8 @@ ClangImporter::create(ASTContext &ctx, new (ctx) ClangModuleUnit(*importedHeaderModule, *importer, nullptr); importedHeaderModule->addFile(*importer->Impl.ImportedHeaderUnit); + importer->Impl.IsReadingBridgingPCH = false; + return importer; } @@ -816,7 +865,8 @@ bool ClangImporter::addSearchPath(StringRef newSearchPath, bool isFramework) { bool ClangImporter::Implementation::importHeader( Module *adapter, StringRef headerName, SourceLoc diagLoc, bool trackParsedSymbols, - std::unique_ptr sourceBuffer) { + std::unique_ptr sourceBuffer, + bool implicitImport) { // Don't even try to load the bridging header if the Clang AST is in a bad // state. It could cause a crash. auto &clangDiags = getClangASTContext().getDiagnostics(); @@ -868,19 +918,40 @@ bool ClangImporter::Implementation::importHeader( consumer.reset(); } + // We're trying to discourage (and eventually deprecate) the use of implicit + // bridging-header imports triggered by IMPORTED_HEADER blocks in + // modules. There are two sub-cases to consider: + // + // #1 The implicit import actually occurred. + // + // #2 The user explicitly -import-objc-header'ed some header or PCH that + // makes the implicit import redundant. + // + // It's not obvious how to exactly differentiate these cases given the + // interface clang gives us, but we only want to warn on case #1, and the + // non-emptiness of allParsedDecls is a _definite_ sign that we're in case + // #1. So we treat that as an approximation of the condition we're after, and + // accept that we might fail to warn in the odd case where "the import + // occurred" but didn't introduce any new decls. + if (implicitImport && !allParsedDecls.empty()) { + SwiftContext.Diags.diagnose( + diagLoc, diag::implicit_bridging_header_imported_from_module, + llvm::sys::path::filename(headerName), adapter->getName()); + } + // We can't do this as we're parsing because we may want to resolve naming // conflicts between the things we've parsed. for (auto group : allParsedDecls) for (auto *D : group) if (auto named = dyn_cast(D)) - addEntryToLookupTable(BridgingHeaderLookupTable, named, + addEntryToLookupTable(*BridgingHeaderLookupTable, named, getNameImporter()); pp.EndSourceFile(); bumpGeneration(); // Add any defined macros to the bridging header lookup table. - addMacrosToLookupTable(BridgingHeaderLookupTable, getNameImporter()); + addMacrosToLookupTable(*BridgingHeaderLookupTable, getNameImporter()); // Wrap all Clang imports under a Swift import decl. for (auto &Import : BridgeHeaderTopLevelImports) { @@ -890,7 +961,7 @@ bool ClangImporter::Implementation::importHeader( } // Finalize the lookup table, which may fail. - finalizeLookupTable(BridgingHeaderLookupTable, getNameImporter()); + finalizeLookupTable(*BridgingHeaderLookupTable, getNameImporter()); // FIXME: What do we do if there was already an error? if (!hadError && clangDiags.hasErrorOccurred()) { @@ -910,7 +981,7 @@ bool ClangImporter::importHeader(StringRef header, Module *adapter, /*OpenFile=*/true); if (headerFile && headerFile->getSize() == expectedSize && headerFile->getModificationTime() == expectedModTime) { - return importBridgingHeader(header, adapter, diagLoc); + return importBridgingHeader(header, adapter, diagLoc, false, true); } if (!cachedContents.empty() && cachedContents.back() == '\0') @@ -919,12 +990,26 @@ bool ClangImporter::importHeader(StringRef header, Module *adapter, llvm::MemoryBuffer::getMemBuffer(cachedContents, header) }; return Impl.importHeader(adapter, header, diagLoc, /*trackParsedSymbols=*/false, - std::move(sourceBuffer)); + std::move(sourceBuffer), true); } bool ClangImporter::importBridgingHeader(StringRef header, Module *adapter, SourceLoc diagLoc, - bool trackParsedSymbols) { + bool trackParsedSymbols, + bool implicitImport) { + if (llvm::sys::path::extension(header).endswith(PCH_EXTENSION)) { + // We already imported this with -include-pch above, so we should have + // collected a bunch of PCH-encoded module imports that we need to + // replay to the HeaderImportCallbacks for processing. + Impl.ImportedHeaderOwners.push_back(adapter); + clang::ASTReader &R = *Impl.Instance->getModuleManager(); + HeaderImportCallbacks CB(*this, Impl); + for (auto ID : Impl.PCHImportedSubmodules) { + CB.handleImport(R.getSubmodule(ID)); + } + Impl.PCHImportedSubmodules.clear(); + return false; + } clang::FileManager &fileManager = Impl.Instance->getFileManager(); const clang::FileEntry *headerFile = fileManager.getFile(header, /*OpenFile=*/true); @@ -944,7 +1029,7 @@ bool ClangImporter::importBridgingHeader(StringRef header, Module *adapter, }; return Impl.importHeader(adapter, header, diagLoc, trackParsedSymbols, - std::move(sourceBuffer)); + std::move(sourceBuffer), implicitImport); } std::string ClangImporter::getBridgingHeaderContents(StringRef headerPath, @@ -994,6 +1079,41 @@ std::string ClangImporter::getBridgingHeaderContents(StringRef headerPath, return result; } +bool +ClangImporter::emitBridgingPCH(StringRef headerPath, + StringRef outputPCHPath) { + llvm::IntrusiveRefCntPtr invocation{ + new clang::CompilerInvocation(*Impl.Invocation) + }; + invocation->getFrontendOpts().DisableFree = false; + invocation->getFrontendOpts().Inputs.clear(); + invocation->getFrontendOpts().Inputs.push_back( + clang::FrontendInputFile(headerPath, clang::IK_ObjC)); + invocation->getFrontendOpts().OutputFile = outputPCHPath; + invocation->getPreprocessorOpts().resetNonModularOptions(); + + clang::CompilerInstance emitInstance( + Impl.Instance->getPCHContainerOperations()); + emitInstance.setInvocation(&*invocation); + emitInstance.createDiagnostics(&Impl.Instance->getDiagnosticClient(), + false); + + clang::FileManager &fileManager = Impl.Instance->getFileManager(); + emitInstance.setFileManager(&fileManager); + emitInstance.createSourceManager(fileManager); + emitInstance.setTarget(&Impl.Instance->getTarget()); + + clang::GeneratePCHAction action; + emitInstance.ExecuteAction(action); + if (emitInstance.getDiagnostics().hasErrorOccurred()) { + Impl.SwiftContext.Diags.diagnose({}, + diag::bridging_header_pch_error, + outputPCHPath, headerPath); + return true; + } + return false; +} + void ClangImporter::collectSubModuleNames( ArrayRef> path, std::vector &names) { @@ -1237,8 +1357,10 @@ ClangImporter::Implementation::Implementation(ASTContext &ctx, ImportForwardDeclarations(opts.ImportForwardDeclarations), InferImportAsMember(opts.InferImportAsMember), DisableSwiftBridgeAttr(opts.DisableSwiftBridgeAttr), + IsReadingBridgingPCH(false), CurrentVersion(nameVersionFromOptions(ctx.LangOpts)), - BridgingHeaderLookupTable(nullptr), platformAvailability(ctx.LangOpts), + BridgingHeaderLookupTable(new SwiftLookupTable(nullptr)), + platformAvailability(ctx.LangOpts), nameImporter() {} ClangImporter::Implementation::~Implementation() { @@ -2535,7 +2657,7 @@ SwiftLookupTable *ClangImporter::Implementation::findLookupTable( const clang::Module *clangModule) { // If the Clang module is null, use the bridging header lookup table. if (!clangModule) - return &BridgingHeaderLookupTable; + return BridgingHeaderLookupTable.get(); // Submodules share lookup tables with their parents. if (clangModule->isSubModule()) @@ -2551,7 +2673,7 @@ SwiftLookupTable *ClangImporter::Implementation::findLookupTable( bool ClangImporter::Implementation::forEachLookupTable( llvm::function_ref fn) { // Visit the bridging header's lookup table. - if (fn(BridgingHeaderLookupTable)) return true; + if (fn(*BridgingHeaderLookupTable)) return true; // Collect and sort the set of module names. SmallVector moduleNames; @@ -2769,5 +2891,5 @@ void ClangImporter::Implementation::dumpSwiftLookupTables() { } llvm::errs() << "<>\n"; - BridgingHeaderLookupTable.dump(); + BridgingHeaderLookupTable->dump(); } diff --git a/lib/ClangImporter/ImporterImpl.h b/lib/ClangImporter/ImporterImpl.h index b0ef4a6146e04..bb5a435dc9afc 100644 --- a/lib/ClangImporter/ImporterImpl.h +++ b/lib/ClangImporter/ImporterImpl.h @@ -272,6 +272,9 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation const bool InferImportAsMember; const bool DisableSwiftBridgeAttr; + bool IsReadingBridgingPCH; + llvm::SmallVector PCHImportedSubmodules; + const Version CurrentVersion; constexpr static const char * const moduleImportBufferName = @@ -281,7 +284,7 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation private: /// The Swift lookup table for the bridging header. - SwiftLookupTable BridgingHeaderLookupTable; + std::unique_ptr BridgingHeaderLookupTable; /// The Swift lookup tables, per module. /// @@ -596,7 +599,8 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation /// Imports the given header contents into the Clang context. bool importHeader(Module *adapter, StringRef headerName, SourceLoc diagLoc, bool trackParsedSymbols, - std::unique_ptr contents); + std::unique_ptr contents, + bool implicitImport); /// \brief Retrieve the imported module that should contain the given /// Clang decl. @@ -1165,16 +1169,18 @@ namespace importer { bool shouldSuppressDeclImport(const clang::Decl *decl); class SwiftNameLookupExtension : public clang::ModuleFileExtension { + std::unique_ptr &pchLookupTable; LookupTableMap &lookupTables; ASTContext &swiftCtx; const PlatformAvailability &availability; const bool inferImportAsMember; public: - SwiftNameLookupExtension(LookupTableMap &tables, ASTContext &ctx, + SwiftNameLookupExtension(std::unique_ptr &pchLookupTable, + LookupTableMap &tables, ASTContext &ctx, const PlatformAvailability &avail, bool inferIAM) - : lookupTables(tables), swiftCtx(ctx), availability(avail), - inferImportAsMember(inferIAM) {} + : pchLookupTable(pchLookupTable), lookupTables(tables), swiftCtx(ctx), + availability(avail), inferImportAsMember(inferIAM) {} clang::ModuleFileExtensionMetadata getExtensionMetadata() const override; llvm::hash_code hashExtension(llvm::hash_code code) const override; diff --git a/lib/ClangImporter/SwiftLookupTable.cpp b/lib/ClangImporter/SwiftLookupTable.cpp index b62180839b6e5..0014e4a5f6985 100644 --- a/lib/ClangImporter/SwiftLookupTable.cpp +++ b/lib/ClangImporter/SwiftLookupTable.cpp @@ -322,7 +322,8 @@ clang::NamedDecl *SwiftLookupTable::resolveContext(StringRef unresolvedName) { } void SwiftLookupTable::addCategory(clang::ObjCCategoryDecl *category) { - assert(!Reader && "Cannot modify a lookup table stored on disk"); + // Force deserialization to occur before appending. + (void) categories(); // Add the category. Categories.push_back(category); @@ -467,8 +468,6 @@ bool SwiftLookupTable::addLocalEntry(SingleEntry newEntry, void SwiftLookupTable::addEntry(DeclName name, SingleEntry newEntry, EffectiveClangContext effectiveContext, const clang::Preprocessor *PP) { - assert(!Reader && "Cannot modify a lookup table stored on disk"); - // Translate the context. auto contextOpt = translateContext(effectiveContext); if (!contextOpt) { @@ -484,6 +483,9 @@ void SwiftLookupTable::addEntry(DeclName name, SingleEntry newEntry, return; } + // Populate cache from reader if necessary. + findOrCreate(name.getBaseName().str()); + auto context = *contextOpt; // If this is a global imported as a member, record is as such. @@ -1688,15 +1690,23 @@ SwiftNameLookupExtension::createExtensionReader( assert(metadata.MajorVersion == SWIFT_LOOKUP_TABLE_VERSION_MAJOR); assert(metadata.MinorVersion == SWIFT_LOOKUP_TABLE_VERSION_MINOR); - // Check whether we already have an entry in the set of lookup tables. - auto &entry = lookupTables[mod.ModuleName]; - if (entry) return nullptr; + std::function onRemove = [](){}; + std::unique_ptr *target = nullptr; - // Local function used to remove this entry when the reader goes away. - std::string moduleName = mod.ModuleName; - auto onRemove = [this, moduleName]() { - lookupTables.erase(moduleName); - }; + if (mod.Kind == clang::serialization::MK_PCH) { + // PCH imports unconditionally overwrite the provided pchLookupTable. + target = &pchLookupTable; + } else { + // Check whether we already have an entry in the set of lookup tables. + target = &lookupTables[mod.ModuleName]; + if (*target) return nullptr; + + // Local function used to remove this entry when the reader goes away. + std::string moduleName = mod.ModuleName; + onRemove = [this, moduleName]() { + lookupTables.erase(moduleName); + }; + } // Create the reader. auto tableReader = SwiftLookupTableReader::create(this, reader, mod, onRemove, @@ -1704,7 +1714,7 @@ SwiftNameLookupExtension::createExtensionReader( if (!tableReader) return nullptr; // Create the lookup table. - entry.reset(new SwiftLookupTable(tableReader.get())); + target->reset(new SwiftLookupTable(tableReader.get())); // Return the new reader. return std::move(tableReader); diff --git a/lib/Driver/Action.cpp b/lib/Driver/Action.cpp index 08b4596c76cdc..43dad8e2c88b0 100644 --- a/lib/Driver/Action.cpp +++ b/lib/Driver/Action.cpp @@ -36,6 +36,7 @@ const char *Action::getClassName(ActionClass AC) { case REPLJob: return "repl"; case LinkJob: return "link"; case GenerateDSYMJob: return "generate-dSYM"; + case GeneratePCHJob: return "generate-pch"; } llvm_unreachable("invalid class"); @@ -62,3 +63,5 @@ void REPLJobAction::anchor() {} void LinkJobAction::anchor() {} void GenerateDSYMJobAction::anchor() {} + +void GeneratePCHJobAction::anchor() {} diff --git a/lib/Driver/Compilation.cpp b/lib/Driver/Compilation.cpp index e512a66b5ed2e..2699ae8d208bc 100644 --- a/lib/Driver/Compilation.cpp +++ b/lib/Driver/Compilation.cpp @@ -115,7 +115,9 @@ static void populateInputInfoMap(InputInfoMap &inputs, const PerformJobsState &endState) { for (auto &entry : endState.UnfinishedCommands) { for (auto *action : entry.first->getSource().getInputs()) { - auto inputFile = cast(action); + auto inputFile = dyn_cast(action); + if (!inputFile) + continue; CompileJobAction::InputInfo info; info.previousModTime = entry.first->getInputModTime(); @@ -132,7 +134,9 @@ static void populateInputInfoMap(InputInfoMap &inputs, continue; for (auto *action : compileAction->getInputs()) { - auto inputFile = cast(action); + auto inputFile = dyn_cast(action); + if (!inputFile) + continue; CompileJobAction::InputInfo info; info.previousModTime = entry->getInputModTime(); diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index 963b475d0241d..878c2d44c3cce 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -1073,6 +1073,11 @@ void Driver::buildOutputInfo(const ToolChain &TC, const DerivedArgList &Args, OI.CompilerOutputType = types::TY_LLVM_BC; break; + case options::OPT_emit_pch: + OI.CompilerMode = OutputInfo::Mode::SingleCompile; + OI.CompilerOutputType = types::TY_PCH; + break; + case options::OPT_parse: case options::OPT_typecheck: case options::OPT_dump_parse: @@ -1265,6 +1270,26 @@ void Driver::buildActions(const ToolChain &TC, switch (OI.CompilerMode) { case OutputInfo::Mode::StandardCompile: case OutputInfo::Mode::UpdateCode: { + + // If the user is importing a textual (.h) bridging header and we're in + // standard-compile (non-WMO) mode, we take the opportunity to precompile + // the header into a temporary PCH, and replace the import argument with the + // PCH in the subsequent frontend jobs. + JobAction *PCH = nullptr; + if (Args.hasFlag(options::OPT_enable_bridging_pch, + options::OPT_disable_bridging_pch, + false)) { + if (Arg *A = Args.getLastArg(options::OPT_import_objc_header)) { + StringRef Value = A->getValue(); + auto Ty = TC.lookupTypeForExtension(llvm::sys::path::extension(Value)); + if (Ty == types::TY_ObjCHeader) { + std::unique_ptr HeaderInput(new InputAction(*A, Ty)); + PCH = new GeneratePCHJobAction(HeaderInput.release()); + Actions.push_back(PCH); + } + } + } + for (const InputPair &Input : Inputs) { types::ID InputType = Input.first; const Arg *InputArg = Input.second; @@ -1296,6 +1321,21 @@ void Driver::buildActions(const ToolChain &TC, previousBuildState)); AllModuleInputs.push_back(Current.get()); } + if (PCH) { + // FIXME: When we have a PCH job, it's officially owned by the Actions + // array; but it's also a secondary input to each of the current + // JobActions, which means that we need to flip the "owns inputs" bit + // on the JobActions so they don't try to free it. That in turn means + // we need to transfer ownership of all the JobActions' existing + // inputs to the Actions array, since the JobActions either own or + // don't own _all_ of their inputs. Ownership can't vary + // input-by-input. + auto *job = cast(Current.get()); + auto inputs = job->getInputs(); + Actions.append(inputs.begin(), inputs.end()); + job->setOwnsInputs(false); + job->addInput(PCH); + } AllLinkerInputs.push_back(Current.release()); break; } @@ -1328,6 +1368,7 @@ void Driver::buildActions(const ToolChain &TC, case types::TY_ClangModuleFile: case types::TY_SwiftDeps: case types::TY_Remapping: + case types::TY_PCH: // We could in theory handle assembly or LLVM input, but let's not. // FIXME: What about LTO? Diags.diagnose(SourceLoc(), diag::error_unexpected_input_file, @@ -1548,6 +1589,12 @@ void Driver::buildJobs(const ActionList &Actions, const OutputInfo &OI, unsigned NumOutputs = 0; for (const Action *A : Actions) { types::ID Type = A->getType(); + + // Skip any GeneratePCHJobActions or InputActions incidentally stored in + // Actions (for ownership), as a result of PCH-generation. + if (isa(A) || isa(A)) + continue; + // Only increment NumOutputs if this is an output which must have its // path specified using -o. // (Module outputs can be specified using -module-output-path, or will @@ -1574,8 +1621,10 @@ void Driver::buildJobs(const ActionList &Actions, const OutputInfo &OI, } for (const Action *A : Actions) { - (void)buildJobsForAction(C, cast(A), OI, OFM, TC, - /*TopLevel*/true, JobCache); + if (auto *JA = dyn_cast(A)) { + (void)buildJobsForAction(C, JA, OI, OFM, TC, + /*TopLevel*/true, JobCache); + } } } @@ -1634,6 +1683,13 @@ static StringRef getOutputFilename(Compilation &C, return Buffer.str(); } + // FIXME: Treat GeneratePCHJobAction as non-top-level (to get tempfile and not + // use the -o arg) even though, based on ownership considerations within the + // driver, it is stored as a "top level" JobAction. + if (isa(JA)) { + AtTopLevel = false; + } + // We don't have an output from an Action-specific command line option, // so figure one out using the defaults. if (AtTopLevel) { @@ -1654,8 +1710,8 @@ static StringRef getOutputFilename(Compilation &C, // We don't yet have a name, assign one. if (!AtTopLevel) { - // We should output to a temporary file, since we're not at - // the top level. + // We should output to a temporary file, since we're not at the top level + // (or are generating a bridging PCH, which is currently always a temp). StringRef Stem = llvm::sys::path::stem(BaseName); StringRef Suffix = types::getTypeTempSuffix(JA->getType()); std::error_code EC = diff --git a/lib/Driver/ToolChain.cpp b/lib/Driver/ToolChain.cpp index 934fdc2d14575..baefc4f32fdfa 100644 --- a/lib/Driver/ToolChain.cpp +++ b/lib/Driver/ToolChain.cpp @@ -83,6 +83,7 @@ ToolChain::constructJob(const JobAction &JA, CASE(ModuleWrapJob) CASE(LinkJob) CASE(GenerateDSYMJob) + CASE(GeneratePCHJob) CASE(AutolinkExtractJob) CASE(REPLJob) #undef CASE diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index 9c03138a9fc51..556fb656ab86e 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -133,7 +133,6 @@ static void addCommonFrontendArgs(const ToolChain &TC, inputArgs.AddLastArg(arguments, options::OPT_enable_app_extension); inputArgs.AddLastArg(arguments, options::OPT_enable_testing); inputArgs.AddLastArg(arguments, options::OPT_g_Group); - inputArgs.AddLastArg(arguments, options::OPT_import_objc_header); inputArgs.AddLastArg(arguments, options::OPT_import_underlying_module); inputArgs.AddLastArg(arguments, options::OPT_module_cache_path); inputArgs.AddLastArg(arguments, options::OPT_module_link_name); @@ -191,6 +190,9 @@ ToolChain::constructInvocation(const CompileJobAction &job, case types::TY_Object: FrontendModeOption = "-c"; break; + case types::TY_PCH: + FrontendModeOption = "-emit-pch"; + break; case types::TY_RawSIL: FrontendModeOption = "-emit-silgen"; break; @@ -255,9 +257,6 @@ ToolChain::constructInvocation(const CompileJobAction &job, Arguments.push_back(FrontendModeOption); - assert(context.Inputs.empty() && - "The Swift frontend does not expect to be fed any input Jobs!"); - // Add input arguments. switch (context.OI.CompilerMode) { case OutputInfo::Mode::StandardCompile: @@ -315,6 +314,23 @@ ToolChain::constructInvocation(const CompileJobAction &job, addCommonFrontendArgs(*this, context.OI, context.Output, context.Args, Arguments); + // Pass along an -import-objc-header arg, replacing the argument with the name + // of any input PCH to the current action if one is present. + if (context.Args.hasArgNoClaim(options::OPT_import_objc_header)) { + bool ForwardAsIs = true; + for (auto *IJ : context.Inputs) { + if (!IJ->getOutput().getAnyOutputForType(types::TY_PCH).empty()) { + Arguments.push_back("-import-objc-header"); + addInputsOfType(Arguments, context.Inputs, types::TY_PCH); + ForwardAsIs = false; + break; + } + } + if (ForwardAsIs) { + context.Args.AddLastArg(Arguments, options::OPT_import_objc_header); + } + } + // Pass the optimization level down to the frontend. context.Args.AddLastArg(Arguments, options::OPT_O_Group); @@ -423,6 +439,7 @@ ToolChain::constructInvocation(const InterpretJobAction &job, addCommonFrontendArgs(*this, context.OI, context.Output, context.Args, Arguments); + context.Args.AddLastArg(Arguments, options::OPT_import_objc_header); // Pass the optimization level down to the frontend. context.Args.AddLastArg(Arguments, options::OPT_O_Group); @@ -480,6 +497,7 @@ ToolChain::constructInvocation(const BackendJobAction &job, case types::TY_RawSIB: case types::TY_SIL: case types::TY_SIB: + case types::TY_PCH: llvm_unreachable("Cannot be output from backend job"); case types::TY_Swift: case types::TY_dSYM: @@ -614,6 +632,7 @@ ToolChain::constructInvocation(const MergeModuleJobAction &job, addCommonFrontendArgs(*this, context.OI, context.Output, context.Args, Arguments); + context.Args.AddLastArg(Arguments, options::OPT_import_objc_header); Arguments.push_back("-module-name"); Arguments.push_back(context.Args.MakeArgString(context.OI.ModuleName)); @@ -683,6 +702,7 @@ ToolChain::constructInvocation(const REPLJobAction &job, ArgStringList FrontendArgs; addCommonFrontendArgs(*this, context.OI, context.Output, context.Args, FrontendArgs); + context.Args.AddLastArg(FrontendArgs, options::OPT_import_objc_header); context.Args.AddAllArgs(FrontendArgs, options::OPT_l, options::OPT_framework, options::OPT_L); @@ -727,6 +747,30 @@ ToolChain::constructInvocation(const GenerateDSYMJobAction &job, return {"dsymutil", Arguments}; } +ToolChain::InvocationInfo +ToolChain::constructInvocation(const GeneratePCHJobAction &job, + const JobContext &context) const { + assert(context.Inputs.empty()); + assert(context.InputActions.size() == 1); + assert(context.Output.getPrimaryOutputType() == types::TY_PCH); + + ArgStringList Arguments; + + Arguments.push_back("-frontend"); + + addCommonFrontendArgs(*this, context.OI, context.Output, context.Args, + Arguments); + + addInputsOfType(Arguments, context.InputActions, types::TY_ObjCHeader); + + Arguments.push_back("-emit-pch"); + Arguments.push_back("-o"); + Arguments.push_back( + context.Args.MakeArgString(context.Output.getPrimaryOutputFilename())); + + return {SWIFT_EXECUTABLE_NAME, Arguments}; +} + ToolChain::InvocationInfo ToolChain::constructInvocation(const AutolinkExtractJobAction &job, const JobContext &context) const { diff --git a/lib/Driver/Types.cpp b/lib/Driver/Types.cpp index 3f524134e9520..8748e590182d4 100644 --- a/lib/Driver/Types.cpp +++ b/lib/Driver/Types.cpp @@ -78,6 +78,7 @@ bool types::isTextual(ID Id) { case types::TY_Image: case types::TY_Object: case types::TY_dSYM: + case types::TY_PCH: case types::TY_SIB: case types::TY_RawSIB: case types::TY_SwiftModuleFile: @@ -105,6 +106,7 @@ bool types::isAfterLLVM(ID Id) { case types::TY_Object: return true; case types::TY_Swift: + case types::TY_PCH: case types::TY_SIL: case types::TY_Dependencies: case types::TY_RawSIL: @@ -145,6 +147,7 @@ bool types::isPartOfSwiftCompilation(ID Id) { case types::TY_Dependencies: case types::TY_ObjCHeader: case types::TY_AutolinkFile: + case types::TY_PCH: case types::TY_Image: case types::TY_dSYM: case types::TY_SwiftModuleFile: diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index d40c2598d17bf..ec15e92fe37e0 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -256,6 +256,8 @@ static bool ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, Action = FrontendOptions::EmitSIB; } else if (Opt.matches(OPT_emit_sibgen)) { Action = FrontendOptions::EmitSIBGen; + } else if (Opt.matches(OPT_emit_pch)) { + Action = FrontendOptions::EmitPCH; } else if (Opt.matches(OPT_parse)) { Action = FrontendOptions::Parse; } else if (Opt.matches(OPT_typecheck)) { @@ -482,6 +484,10 @@ static bool ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, Opts.setSingleOutputFilename("-"); break; + case FrontendOptions::EmitPCH: + Suffix = PCH_EXTENSION; + break; + case FrontendOptions::EmitSILGen: case FrontendOptions::EmitSIL: { if (Opts.OutputFilenames.empty()) @@ -675,6 +681,7 @@ static bool ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, case FrontendOptions::Parse: case FrontendOptions::Typecheck: case FrontendOptions::EmitModuleOnly: + case FrontendOptions::EmitPCH: case FrontendOptions::EmitSILGen: case FrontendOptions::EmitSIL: case FrontendOptions::EmitSIBGen: @@ -694,6 +701,7 @@ static bool ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, case FrontendOptions::DumpInterfaceHash: case FrontendOptions::DumpAST: case FrontendOptions::PrintAST: + case FrontendOptions::EmitPCH: case FrontendOptions::DumpScopeMaps: case FrontendOptions::DumpTypeRefinementContexts: case FrontendOptions::Immediate: @@ -725,6 +733,7 @@ static bool ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, case FrontendOptions::DumpInterfaceHash: case FrontendOptions::DumpAST: case FrontendOptions::PrintAST: + case FrontendOptions::EmitPCH: case FrontendOptions::DumpScopeMaps: case FrontendOptions::DumpTypeRefinementContexts: case FrontendOptions::EmitSILGen: @@ -757,7 +766,7 @@ static bool ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, Opts.ImportUnderlyingModule |= Args.hasArg(OPT_import_underlying_module); Opts.SILSerializeAll |= Args.hasArg(OPT_sil_serialize_all); - if (const Arg *A = Args.getLastArg(OPT_import_objc_header)) { + if (const Arg *A = Args.getLastArgNoClaim(OPT_import_objc_header)) { Opts.ImplicitObjCHeaderPath = A->getValue(); Opts.SerializeBridgingHeader |= !Opts.PrimaryInput && !Opts.ModuleOutputPath.empty(); @@ -984,7 +993,8 @@ static bool ParseClangImporterArgs(ClangImporterOptions &Opts, if (Args.hasArg(OPT_embed_bitcode)) Opts.Mode = ClangImporterOptions::Modes::EmbedBitcode; - + if (auto *A = Args.getLastArg(OPT_import_objc_header)) + Opts.BridgingHeader = A->getValue(); Opts.DisableSwiftBridgeAttr |= Args.hasArg(OPT_disable_swift_bridge_attr); Opts.DisableModulesValidateSystemHeaders |= Args.hasArg(OPT_disable_modules_validate_system_headers); diff --git a/lib/Frontend/FrontendOptions.cpp b/lib/Frontend/FrontendOptions.cpp index 13529bae2695d..75284c6dd2e8c 100644 --- a/lib/Frontend/FrontendOptions.cpp +++ b/lib/Frontend/FrontendOptions.cpp @@ -28,6 +28,7 @@ bool FrontendOptions::actionHasOutput() const { case DumpScopeMaps: case DumpTypeRefinementContexts: return false; + case EmitPCH: case EmitSILGen: case EmitSIL: case EmitSIBGen: @@ -57,6 +58,7 @@ bool FrontendOptions::actionIsImmediate() const { case PrintAST: case DumpScopeMaps: case DumpTypeRefinementContexts: + case EmitPCH: case EmitSILGen: case EmitSIL: case EmitSIBGen: diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index 99a3cec28fae3..7973b5279d280 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -335,6 +335,15 @@ static bool performCompile(std::unique_ptr &Instance, FrontendOptions opts = Invocation.getFrontendOptions(); FrontendOptions::ActionType Action = opts.RequestedAction; + // We've been asked to precompile a bridging header; we want to + // avoid touching any other inputs and just parse, emit and exit. + if (Action == FrontendOptions::EmitPCH) { + auto clangImporter = static_cast( + Instance->getASTContext().getClangModuleLoader()); + return clangImporter->emitBridgingPCH( + Invocation.getInputFilenames()[0], opts.getSingleOutputFilename()); + } + IRGenOptions &IRGenOpts = Invocation.getIRGenOptions(); bool inputIsLLVMIr = Invocation.getInputKind() == InputFileKind::IFK_LLVM_IR; diff --git a/test/ClangImporter/Inputs/app-bridging-header-to-pch.h b/test/ClangImporter/Inputs/app-bridging-header-to-pch.h new file mode 100644 index 0000000000000..f8d56b71f18a6 --- /dev/null +++ b/test/ClangImporter/Inputs/app-bridging-header-to-pch.h @@ -0,0 +1,3 @@ +static inline int app_function(int x) { + return x + 27; +} diff --git a/test/ClangImporter/Inputs/app-that-uses-pch-bridging-header.swift b/test/ClangImporter/Inputs/app-that-uses-pch-bridging-header.swift new file mode 100644 index 0000000000000..93ec6f9680990 --- /dev/null +++ b/test/ClangImporter/Inputs/app-that-uses-pch-bridging-header.swift @@ -0,0 +1,6 @@ +import Foundation + +public func AppFunc() -> Int32 { + return app_function(10) +} + diff --git a/test/ClangImporter/Inputs/chained-unit-test-bridging-header-to-pch.h b/test/ClangImporter/Inputs/chained-unit-test-bridging-header-to-pch.h new file mode 100644 index 0000000000000..c91801c22ed41 --- /dev/null +++ b/test/ClangImporter/Inputs/chained-unit-test-bridging-header-to-pch.h @@ -0,0 +1,5 @@ +#import "app-bridging-header-to-pch.h" + +static inline int unit_test_function(int x) { + return x + 28; +} diff --git a/test/ClangImporter/Inputs/unit-test-bridging-header-to-pch.h b/test/ClangImporter/Inputs/unit-test-bridging-header-to-pch.h new file mode 100644 index 0000000000000..2397cf125706f --- /dev/null +++ b/test/ClangImporter/Inputs/unit-test-bridging-header-to-pch.h @@ -0,0 +1,3 @@ +static inline int unit_test_function(int x) { + return x + 28; +} diff --git a/test/ClangImporter/MixedSource/broken-bridging-header.swift b/test/ClangImporter/MixedSource/broken-bridging-header.swift index 17b7577d17804..9d53a3b93a879 100644 --- a/test/ClangImporter/MixedSource/broken-bridging-header.swift +++ b/test/ClangImporter/MixedSource/broken-bridging-header.swift @@ -19,7 +19,7 @@ // REQUIRES: objc_interop -import HasBridgingHeader // expected-error {{failed to import bridging header}} expected-error {{failed to load module 'HasBridgingHeader'}} +import HasBridgingHeader // expected-error {{failed to import bridging header}} expected-error {{failed to load module 'HasBridgingHeader'}} expected-warning {{implicit import of bridging header}} // MISSING-HEADER: error: bridging header '{{.*}}/fake.h' does not exist // MISSING-HEADER-NOT: error: diff --git a/test/ClangImporter/MixedSource/import-mixed-with-header-twice.swift b/test/ClangImporter/MixedSource/import-mixed-with-header-twice.swift index 0038ed002de4d..2ae217480fa76 100644 --- a/test/ClangImporter/MixedSource/import-mixed-with-header-twice.swift +++ b/test/ClangImporter/MixedSource/import-mixed-with-header-twice.swift @@ -13,7 +13,7 @@ // USE-SERIALIZED-HEADER: redefinition of 'Point2D' // USE-SERIALIZED-HEADER: previous definition is here -import MixedWithHeaderAgain +import MixedWithHeaderAgain // expected-warning {{implicit import of bridging header 'header-again.h' via module 'MixedWithHeaderAgain' is deprecated and will be removed in a later version of Swift}} func testLine(line: Line) { testLineImpl(line) diff --git a/test/ClangImporter/MixedSource/import-mixed-with-header.swift b/test/ClangImporter/MixedSource/import-mixed-with-header.swift index db0a848415aad..38ca2cc11d70c 100644 --- a/test/ClangImporter/MixedSource/import-mixed-with-header.swift +++ b/test/ClangImporter/MixedSource/import-mixed-with-header.swift @@ -14,7 +14,7 @@ // XFAIL: linux -import MixedWithHeader +import MixedWithHeader // expected-warning {{implicit import of bridging header 'header.h' via module 'MixedWithHeader' is deprecated and will be removed in a later version of Swift}} func testReexportedClangModules(_ foo : FooProto) { _ = foo.bar as CInt diff --git a/test/ClangImporter/pch-bridging-header-unittest-ok.swift b/test/ClangImporter/pch-bridging-header-unittest-ok.swift new file mode 100644 index 0000000000000..fb1dbf08f9857 --- /dev/null +++ b/test/ClangImporter/pch-bridging-header-unittest-ok.swift @@ -0,0 +1,21 @@ +// REQUIRES: objc_interop +// RUN: rm -rf %t && mkdir -p %t/tmp +// RUN: %target-swiftc_driver -emit-module -import-objc-header %S/Inputs/app-bridging-header-to-pch.h -module-name App -emit-module-path %t/App.swiftmodule %S/Inputs/app-that-uses-pch-bridging-header.swift +// RUN: llvm-bcanalyzer -dump %t/App.swiftmodule | %FileCheck %s +// CHECK: IMPORTED_HEADER{{.*}}Inputs/app-bridging-header-to-pch.h + +// Should get no warnings when we PCH-in the chained unit-test bridging header (thereby suppressing implicit import) +// RUN: %target-swiftc_driver -D UNIT_TESTS -typecheck -Xfrontend -verify -enable-bridging-pch -import-objc-header %S/Inputs/chained-unit-test-bridging-header-to-pch.h -I %S/Inputs -I %t %s + +// Should get no warnings when we PCH-in the app bridging header (thereby suppressing implicit import) +// RUN: %target-swiftc_driver -typecheck -Xfrontend -verify -enable-bridging-pch -import-objc-header %S/Inputs/app-bridging-header-to-pch.h -I %t %s + +import App + +func test_all() { +#if UNIT_TESTS + let _ = unit_test_function(AppFunc()) +#else + let _ = app_function(AppFunc()) +#endif +} diff --git a/test/ClangImporter/pch-bridging-header-unittest-warn.swift b/test/ClangImporter/pch-bridging-header-unittest-warn.swift new file mode 100644 index 0000000000000..0d97db65849a0 --- /dev/null +++ b/test/ClangImporter/pch-bridging-header-unittest-warn.swift @@ -0,0 +1,21 @@ +// REQUIRES: objc_interop +// RUN: rm -rf %t && mkdir -p %t/tmp +// RUN: %target-swiftc_driver -emit-module -import-objc-header %S/Inputs/app-bridging-header-to-pch.h -module-name App -emit-module-path %t/App.swiftmodule %S/Inputs/app-that-uses-pch-bridging-header.swift +// RUN: llvm-bcanalyzer -dump %t/App.swiftmodule | %FileCheck %s +// CHECK: IMPORTED_HEADER{{.*}}Inputs/app-bridging-header-to-pch.h + +// Should get a warning when we PCH-in the unit test header and then implicitly import the app header. +// RUN: %target-swiftc_driver -D UNIT_TESTS -typecheck -Xfrontend -verify -enable-bridging-pch -import-objc-header %S/Inputs/unit-test-bridging-header-to-pch.h -I %t %s + +// Should get a warning when skip the unit test header entirely and implicitly import the app header. +// RUN: %target-swiftc_driver -typecheck -Xfrontend -verify -I %t %s + +import App // expected-warning{{implicit import of bridging header 'app-bridging-header-to-pch.h' via module 'App' is deprecated and will be removed in a later version of Swift}} + +func test_all() { +#if UNIT_TESTS + let _ = unit_test_function(AppFunc()) +#else + let _ = app_function(AppFunc()) +#endif +} diff --git a/test/ClangImporter/pch-bridging-header.swift b/test/ClangImporter/pch-bridging-header.swift new file mode 100644 index 0000000000000..cc92bc9776c44 --- /dev/null +++ b/test/ClangImporter/pch-bridging-header.swift @@ -0,0 +1,28 @@ +// REQUIRES: objc_interop +// RUN: rm -rf %t && mkdir -p %t/tmp + +// First test the explicit frontend-based bridging PCH generation and use works +// RUN: %target-swift-frontend -emit-pch -o %t/sdk-bridging-header.pch %S/Inputs/sdk-bridging-header.h +// RUN: %target-swift-frontend -parse -verify %s -import-objc-header %t/sdk-bridging-header.pch + +// Now test the driver-automated version is inert when (default) disabled +// RUN: env TMPDIR=%t/tmp/ %target-swiftc_driver -parse -save-temps %s -import-objc-header %S/Inputs/sdk-bridging-header.h +// RUN: not ls %t/tmp/*.pch >/dev/null 2>&1 + +// Test the driver-automated version works when enabled +// RUN: env TMPDIR=%t/tmp/ %target-swiftc_driver -parse -save-temps %s -enable-bridging-pch -import-objc-header %S/Inputs/sdk-bridging-header.h +// RUN: ls %t/tmp/*.pch >/dev/null 2>&1 +// RUN: llvm-objdump -raw-clang-ast %t/tmp/*.pch | llvm-bcanalyzer -dump | %FileCheck %s +// CHECK: ORIGINAL_FILE{{.*}}Inputs/sdk-bridging-header.h + +// Test the driver-automated version deletes its PCH file when done +// RUN: rm %t/tmp/*.pch +// RUN: env TMPDIR=%t/tmp/ %target-swiftc_driver -parse %s -enable-bridging-pch -import-objc-header %S/Inputs/sdk-bridging-header.h +// RUN: not ls %t/tmp/*.pch >/dev/null 2>&1 + +import Foundation + +let not = MyPredicate.not() +let and = MyPredicate.and([]) +let or = MyPredicate.or([not, and]) + diff --git a/test/Driver/Inputs/bridging-header.h b/test/Driver/Inputs/bridging-header.h new file mode 100644 index 0000000000000..2497af651c2ea --- /dev/null +++ b/test/Driver/Inputs/bridging-header.h @@ -0,0 +1 @@ +extern int x; diff --git a/test/Driver/bridging-pch.swift b/test/Driver/bridging-pch.swift new file mode 100644 index 0000000000000..491a7ce24ce04 --- /dev/null +++ b/test/Driver/bridging-pch.swift @@ -0,0 +1,17 @@ +// RUN: %swiftc_driver -typecheck -enable-bridging-pch -driver-print-actions -import-objc-header %S/Inputs/bridging-header.h %s 2>&1 | %FileCheck %s -check-prefix=YESPCHACT +// YESPCHACT: 0: input, "{{.*}}Inputs/bridging-header.h", objc-header +// YESPCHACT: 1: generate-pch, {0}, pch +// YESPCHACT: 2: input, "{{.*}}bridging-pch.swift", swift +// YESPCHACT: 3: compile, {2, 1}, none + +// RUN: %swiftc_driver -typecheck -driver-print-actions -import-objc-header %S/Inputs/bridging-header.h %s 2>&1 | %FileCheck %s -check-prefix=NOPCHACT +// NOPCHACT: 0: input, "{{.*}}bridging-pch.swift", swift +// NOPCHACT: 1: compile, {0}, none + +// RUN: %swiftc_driver -typecheck -enable-bridging-pch -driver-print-jobs -import-objc-header %S/Inputs/bridging-header.h %s 2>&1 | %FileCheck %s -check-prefix=YESPCHJOB +// YESPCHJOB: {{.*}}swift -frontend {{.*}} -emit-pch -o {{.*}}bridging-header-{{.*}}.pch +// YESPCHJOB: {{.*}}swift -frontend {{.*}} -import-objc-header {{.*}}bridging-header-{{.*}}.pch + +// RUN: %swiftc_driver -typecheck -driver-print-jobs -import-objc-header %S/Inputs/bridging-header.h %s 2>&1 | %FileCheck %s -check-prefix=NOPCHJOB +// NOPCHJOB: {{.*}}swift -frontend {{.*}} -import-objc-header {{.*}}Inputs/bridging-header.h +