diff --git a/include/swift/AST/PrintOptions.h b/include/swift/AST/PrintOptions.h index ebdf2db947a5e..61f54478e2ff9 100644 --- a/include/swift/AST/PrintOptions.h +++ b/include/swift/AST/PrintOptions.h @@ -550,7 +550,8 @@ struct PrintOptions { /// attributes. /// /// \see swift::emitSwiftInterface - static PrintOptions printSwiftInterfaceFile(bool preferTypeRepr, + static PrintOptions printSwiftInterfaceFile(ModuleDecl *ModuleToPrint, + bool preferTypeRepr, bool printFullConvention, bool printSPIs); diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 5d212a5d044b8..cdcee16fc1e38 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -107,7 +107,8 @@ static bool contributesToParentTypeStorage(const AbstractStorageDecl *ASD) { return !ND->isResilient() && ASD->hasStorage() && !ASD->isStatic(); } -PrintOptions PrintOptions::printSwiftInterfaceFile(bool preferTypeRepr, +PrintOptions PrintOptions::printSwiftInterfaceFile(ModuleDecl *ModuleToPrint, + bool preferTypeRepr, bool printFullConvention, bool printSPIs) { PrintOptions result; @@ -115,6 +116,7 @@ PrintOptions PrintOptions::printSwiftInterfaceFile(bool preferTypeRepr, result.PrintLongAttrsOnSeparateLines = true; result.TypeDefinitions = true; result.PrintIfConfig = false; + result.CurrentModule = ModuleToPrint; result.FullyQualifiedTypes = true; result.UseExportedModuleNames = true; result.AllowNullTypes = false; @@ -3648,6 +3650,8 @@ class TypePrinter : public TypeVisitor { ASTPrinter &Printer; const PrintOptions &Options; + Optional> + VisibleClangModules; void printGenericArgs(ArrayRef Args) { if (Args.empty()) @@ -3678,7 +3682,7 @@ class TypePrinter : public TypeVisitor { } } - /// Determinee whether the given type has a simple representation + /// Determine whether the given type has a simple representation /// under the current print options. bool isSimpleUnderPrintOptions(Type T) { if (auto typealias = dyn_cast(T.getPointer())) { @@ -3700,10 +3704,90 @@ class TypePrinter : public TypeVisitor { return T->hasSimpleTypeRepr(); } + /// Computes the map that is cached by `getVisibleClangModules()`. + /// Do not call directly. + llvm::DenseMap + computeVisibleClangModules() { + assert(Options.CurrentModule && + "CurrentModule needs to be set to determine imported Clang modules"); + + llvm::DenseMap Result; + + // For the current module, consider both private and public imports. + ModuleDecl::ImportFilter Filter = ModuleDecl::ImportFilterKind::Exported; + Filter |= ModuleDecl::ImportFilterKind::Default; + Filter |= ModuleDecl::ImportFilterKind::SPIAccessControl; + SmallVector Imports; + Options.CurrentModule->getImportedModules(Imports, Filter); + + SmallVector ModulesToProcess; + for (const auto &Import : Imports) { + ModulesToProcess.push_back(Import.importedModule); + } + + SmallPtrSet Processed; + while (!ModulesToProcess.empty()) { + ModuleDecl *Mod = ModulesToProcess.back(); + ModulesToProcess.pop_back(); + + if (!Processed.insert(Mod).second) + continue; + + if (const clang::Module *ClangModule = Mod->findUnderlyingClangModule()) + Result[ClangModule] = Mod; + + // For transitive imports, consider only public imports. + Imports.clear(); + Mod->getImportedModules(Imports, ModuleDecl::ImportFilterKind::Exported); + for (const auto &Import : Imports) { + ModulesToProcess.push_back(Import.importedModule); + } + } + + return Result; + } + + /// Returns all Clang modules that are visible from `Options.CurrentModule`. + /// This includes any modules that are imported transitively through public + /// (`@_exported`) imports. + /// + /// The returned map associates each visible Clang module with the + /// corresponding Swift module. + const llvm::DenseMap & + getVisibleClangModules() { + if (!VisibleClangModules) { + VisibleClangModules = computeVisibleClangModules(); + } + return *VisibleClangModules; + } + template void printModuleContext(T *Ty) { FileUnit *File = cast(Ty->getDecl()->getModuleScopeContext()); ModuleDecl *Mod = File->getParentModule(); + StringRef ExportedModuleName = File->getExportedModuleName(); + + // Clang declarations need special treatment: Multiple Clang modules can + // contain the same declarations from a textually included header, but not + // all of these modules may be visible. We therefore need to make sure we + // choose a module that is visible from the current module. This is possible + // only if we know what the current module is. + const clang::Decl *ClangDecl = Ty->getDecl()->getClangDecl(); + if (ClangDecl && Options.CurrentModule) { + for (auto *Redecl : ClangDecl->redecls()) { + clang::Module *ClangModule = + Redecl->getOwningModule()->getTopLevelModule(); + if (!ClangModule) + continue; + + if (ModuleDecl *VisibleModule = + getVisibleClangModules().lookup(ClangModule)) { + Mod = VisibleModule; + ExportedModuleName = ClangModule->ExportAsModule; + break; + } + } + } if (Options.MapCrossImportOverlaysToDeclaringModule) { if (ModuleDecl *Declaring = Mod->getDeclaringModuleIfCrossImportOverlay()) @@ -3711,8 +3795,9 @@ class TypePrinter : public TypeVisitor { } Identifier Name = Mod->getName(); - if (Options.UseExportedModuleNames) - Name = Mod->getASTContext().getIdentifier(File->getExportedModuleName()); + if (Options.UseExportedModuleNames && !ExportedModuleName.empty()) { + Name = Mod->getASTContext().getIdentifier(ExportedModuleName); + } Printer.printModuleRef(Mod, Name); Printer << "."; diff --git a/lib/Frontend/ModuleInterfaceSupport.cpp b/lib/Frontend/ModuleInterfaceSupport.cpp index ea04e971aef0a..a3c1f14f5a54a 100644 --- a/lib/Frontend/ModuleInterfaceSupport.cpp +++ b/lib/Frontend/ModuleInterfaceSupport.cpp @@ -517,7 +517,7 @@ bool swift::emitSwiftInterface(raw_ostream &out, printImports(out, Opts, M); const PrintOptions printOptions = PrintOptions::printSwiftInterfaceFile( - Opts.PreserveTypesAsWritten, Opts.PrintFullConvention, Opts.PrintSPIs); + M, Opts.PreserveTypesAsWritten, Opts.PrintFullConvention, Opts.PrintSPIs); InheritedProtocolCollector::PerTypeMap inheritedProtocolMap; SmallVector topLevelDecls; diff --git a/test/Interop/C/modules/print-qualified-clang-types/Inputs/SpiMainModule.swift b/test/Interop/C/modules/print-qualified-clang-types/Inputs/SpiMainModule.swift new file mode 100644 index 0000000000000..2980e40a28450 --- /dev/null +++ b/test/Interop/C/modules/print-qualified-clang-types/Inputs/SpiMainModule.swift @@ -0,0 +1,3 @@ +@_spi(dummy) import HelperModule + +public func funcTakingForeignStruct(_ param: ForeignStruct) {} diff --git a/test/Interop/C/modules/print-qualified-clang-types/print-qualified-clang-types-spi.swift b/test/Interop/C/modules/print-qualified-clang-types/print-qualified-clang-types-spi.swift new file mode 100644 index 0000000000000..d3481e2505eb4 --- /dev/null +++ b/test/Interop/C/modules/print-qualified-clang-types/print-qualified-clang-types-spi.swift @@ -0,0 +1,14 @@ +// This tests verifies that SPI visible modules are understood by the machinery +// that produces .swiftinterface files. +// +// See the documentation for the print-qualified-clang-types.swift test next to +// this one for more context. + +// RUN: %empty-directory(%t) +// RUN: mkdir %t/helper_module %t/spi_main_module +// RUN: %target-swift-frontend -enable-library-evolution -swift-version 5 -emit-module -o %t/helper_module/HelperModule.swiftmodule %S/Inputs/HelperModule.swift -I %S/Inputs +// RUN: %target-swift-frontend -enable-library-evolution -swift-version 5 -emit-module -o %t/spi_main_module/SpiMainModule.swiftmodule -emit-module-interface-path %t/spi_main_module/SpiMainModule.swiftinterface -I %t/helper_module %S/Inputs/SpiMainModule.swift -I %S/Inputs +// RUN: %FileCheck --input-file=%t/spi_main_module/SpiMainModule.swiftinterface %s +// RUN: %target-swift-frontend -typecheck -swift-version 5 %t/spi_main_module/SpiMainModule.swiftinterface -I %t/helper_module -I %S/Inputs + +// CHECK: public func funcTakingForeignStruct(_ param: ForeignB.ForeignStruct) diff --git a/test/Interop/C/modules/print-qualified-clang-types/print-qualified-clang-types.swift b/test/Interop/C/modules/print-qualified-clang-types/print-qualified-clang-types.swift index a14102af9b264..13a78745481e8 100644 --- a/test/Interop/C/modules/print-qualified-clang-types/print-qualified-clang-types.swift +++ b/test/Interop/C/modules/print-qualified-clang-types/print-qualified-clang-types.swift @@ -34,5 +34,3 @@ // RUN: %target-swift-frontend -typecheck -swift-version 5 %t/main_module/MainModule.swiftinterface -I %t/helper_module -I %S/Inputs // CHECK: public func funcTakingForeignStruct(_ param: ForeignB.ForeignStruct) - -// REQUIRES: SR-13032