diff --git a/include/swift/AST/FileUnit.h b/include/swift/AST/FileUnit.h index 512e6b04f00a8..59f93166ac3d4 100644 --- a/include/swift/AST/FileUnit.h +++ b/include/swift/AST/FileUnit.h @@ -126,6 +126,13 @@ class FileUnit : public DeclContext, public ASTAllocated { const ModuleDecl *importedModule, llvm::SmallSetVector &spiGroups) const {}; + /// Returns true if any import of \p importedModule has the `@preconcurrency` + /// attribute. + virtual bool + isModuleImportedPreconcurrency(const ModuleDecl *importedModule) const { + return false; + }; + /// Find all availability domains defined in this module with the given /// identifier. /// diff --git a/include/swift/AST/Module.h b/include/swift/AST/Module.h index 9c78b062eb3f5..39091dcd3ad78 100644 --- a/include/swift/AST/Module.h +++ b/include/swift/AST/Module.h @@ -965,6 +965,10 @@ class ModuleDecl const ModuleDecl *importedModule, llvm::SmallSetVector &spiGroups) const; + /// Returns true if any import of \p importedModule has the `@preconcurrency` + /// attribute. + bool isModuleImportedPreconcurrency(const ModuleDecl *importedModule) const; + /// Finds the custom availability domain defined by this module with the /// given identifier and if one exists adds it to results. void diff --git a/include/swift/AST/SourceFile.h b/include/swift/AST/SourceFile.h index ba6da38e56d99..a4f663c2eea99 100644 --- a/include/swift/AST/SourceFile.h +++ b/include/swift/AST/SourceFile.h @@ -471,6 +471,11 @@ class SourceFile final : public FileUnit { const ModuleDecl *importedModule, llvm::SmallSetVector &spiGroups) const override; + /// Returns true if any import of \p importedModule has the `@preconcurrency` + /// attribute. + virtual bool isModuleImportedPreconcurrency( + const ModuleDecl *importedModule) const override; + // Is \p targetDecl accessible as an explicitly imported SPI from this file? bool isImportedAsSPI(const ValueDecl *targetDecl) const; diff --git a/include/swift/Serialization/SerializedModuleLoader.h b/include/swift/Serialization/SerializedModuleLoader.h index a44752905b787..6eb1a0d14779a 100644 --- a/include/swift/Serialization/SerializedModuleLoader.h +++ b/include/swift/Serialization/SerializedModuleLoader.h @@ -466,6 +466,9 @@ class SerializedASTFile final : public LoadedFile { const ModuleDecl *importedModule, llvm::SmallSetVector &spiGroups) const override; + virtual bool isModuleImportedPreconcurrency( + const ModuleDecl *importedModule) const override; + std::optional getCommentForDecl(const Decl *D) const override; bool hasLoadedSwiftDoc() const override; diff --git a/lib/AST/Module.cpp b/lib/AST/Module.cpp index d4098f394a284..93409fb5eed9b 100644 --- a/lib/AST/Module.cpp +++ b/lib/AST/Module.cpp @@ -1044,6 +1044,20 @@ void ModuleDecl::lookupImportedSPIGroups( FORWARD(lookupImportedSPIGroups, (importedModule, spiGroups)); } +bool ModuleDecl::isModuleImportedPreconcurrency( + const ModuleDecl *importedModule) const { + for (const FileUnit *file : getFiles()) { + if (file->isModuleImportedPreconcurrency(importedModule)) + return true; + + if (auto *synth = file->getSynthesizedFile()) { + if (synth->isModuleImportedPreconcurrency(importedModule)) + return true; + } + } + return false; +} + void ModuleDecl::lookupAvailabilityDomains( Identifier identifier, llvm::SmallVectorImpl &results) const { @@ -3050,6 +3064,20 @@ void SourceFile::lookupImportedSPIGroups( } } +bool SourceFile::isModuleImportedPreconcurrency( + const ModuleDecl *importedModule) const { + auto &imports = getASTContext().getImportCache(); + for (auto &import : *Imports) { + if (import.options.contains(ImportFlags::Preconcurrency) && + (importedModule == import.module.importedModule || + imports.isImportedByViaSwiftOnly(importedModule, + import.module.importedModule))) { + return true; + } + } + return false; +} + bool shouldImplicitImportAsSPI(ArrayRef spiGroups) { for (auto group : spiGroups) { if (group.empty()) diff --git a/lib/Frontend/ModuleInterfaceSupport.cpp b/lib/Frontend/ModuleInterfaceSupport.cpp index 229e7f031e343..1401092d142f9 100644 --- a/lib/Frontend/ModuleInterfaceSupport.cpp +++ b/lib/Frontend/ModuleInterfaceSupport.cpp @@ -271,6 +271,9 @@ static void printImports(raw_ostream &out, ModuleDecl::ImportFilterKind::Default, ModuleDecl::ImportFilterKind::ShadowedByCrossImportOverlay}; + // FIXME: Scan over all imports in the module once to build up the attribute + // set for printed imports, instead of repeatedly doing linear scans for each + // kind of attribute. using ImportSet = llvm::SmallSet; auto getImports = [M](ModuleDecl::ImportFilter filter) -> ImportSet { SmallVector matchingImports; @@ -355,6 +358,9 @@ static void printImports(raw_ostream &out, out << "@_spi(" << spiName << ") "; } + if (M->isModuleImportedPreconcurrency(importedModule)) + out << "@preconcurrency "; + if (Opts.printPackageInterface() && !publicImportSet.count(import) && packageOnlyImportSet.count(import)) diff --git a/lib/Serialization/SerializedModuleLoader.cpp b/lib/Serialization/SerializedModuleLoader.cpp index d1dca52d525c3..328345b9ffab7 100644 --- a/lib/Serialization/SerializedModuleLoader.cpp +++ b/lib/Serialization/SerializedModuleLoader.cpp @@ -1915,6 +1915,14 @@ void SerializedASTFile::lookupImportedSPIGroups( } } +bool SerializedASTFile::isModuleImportedPreconcurrency( + const ModuleDecl *importedModule) const { + // This method should only be queried during `-merge-modules` jobs, which are + // deprecated, and thus no effort has been made to answer this query correctly + // (@preconcurrency is not encoded on imports in serialized modules). + return false; +} + std::optional SerializedASTFile::getCommentForDecl(const Decl *D) const { return File.getCommentForDecl(D); diff --git a/test/ModuleInterface/preconcurrency_imports.swift b/test/ModuleInterface/preconcurrency_imports.swift new file mode 100644 index 0000000000000..238ba9a1ec17e --- /dev/null +++ b/test/ModuleInterface/preconcurrency_imports.swift @@ -0,0 +1,37 @@ +// RUN: %empty-directory(%t) +// RUN: split-file %s %t + +// RUN: %target-swift-frontend -emit-module %t/PreconcurrencyLib.swift -module-name PreconcurrencyLib -swift-version 5 -enable-library-evolution -emit-module-path %t/PreconcurrencyLib.swiftmodule -emit-module-interface-path %t/PreconcurrencyLib.swiftinterface +// RUN: %target-swift-frontend -emit-module %t/OtherLib.swift -module-name OtherLib -swift-version 5 -enable-library-evolution -emit-module-path %t/OtherLib.swiftmodule -emit-module-interface-path %t/OtherLib.swiftinterface + +// RUN: %target-swift-emit-module-interface(%t/ClientLib.swiftinterface) -swift-version 6 %t/ClientLib_file1.swift %t/ClientLib_file2.swift -module-name ClientLib -I %t +// RUN: %target-swift-typecheck-module-from-interface(%t/ClientLib.swiftinterface) -module-name ClientLib -I %t +// RUN: %FileCheck %s < %t/ClientLib.swiftinterface + +// CHECK: {{^}}@preconcurrency import OtherLib +// CHECK: {{^}}@preconcurrency import PreconcurrencyLib +// CHECK: public struct Struct1 : Swift.Sendable +// CHECK: public struct Struct2 + +//--- PreconcurrencyLib.swift + +public class C {} + +//--- OtherLib.swift +// Intentionally empty + +//--- ClientLib_file1.swift + +@preconcurrency public import PreconcurrencyLib +public import OtherLib + +public struct Struct1: Sendable { + public var c: C +} + +//--- ClientLib_file2.swift + +internal import PreconcurrencyLib +@preconcurrency internal import OtherLib + +public struct Struct2 {}