Skip to content

Commit c40a083

Browse files
authored
Merge pull request swiftlang#37219 from nkcsgexi/73992299-4
ModuleLoader: teach canImport to check Swift user module versions
2 parents bbf2ba7 + 8fa2e8a commit c40a083

File tree

7 files changed

+165
-27
lines changed

7 files changed

+165
-27
lines changed

include/swift/AST/ASTContext.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ class ASTContext final {
347347
DelayedPatternContexts;
348348

349349
/// Cache of module names that fail the 'canImport' test in this context.
350-
llvm::SmallPtrSet<Identifier, 8> FailedModuleImportNames;
350+
mutable llvm::SmallPtrSet<Identifier, 8> FailedModuleImportNames;
351351

352352
/// Retrieve the allocator for the given arena.
353353
llvm::BumpPtrAllocator &
@@ -894,7 +894,8 @@ class ASTContext final {
894894
/// module is loaded in full.
895895
bool canImportModuleImpl(ImportPath::Element ModulePath,
896896
llvm::VersionTuple version,
897-
bool underlyingVersion) const;
897+
bool underlyingVersion,
898+
bool updateFailingList) const;
898899
public:
899900
namelookup::ImportCache &getImportCache() const;
900901

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -943,6 +943,9 @@ REMARK(module_loaded,none,
943943
"loaded module at %0",
944944
(StringRef))
945945

946+
WARNING(cannot_find_project_version,none,
947+
"cannot find user version number for %0 module '%1'; version number ignored", (StringRef, StringRef))
948+
946949
// Operator decls
947950
ERROR(ambiguous_operator_decls,none,
948951
"ambiguous operator declarations found for operator", ())

include/swift/Serialization/SerializedModuleLoader.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,13 @@ class ImplicitSerializedModuleLoader : public SerializedModuleLoaderBase {
250250
/// Imports serialized Swift modules from a MemoryBuffer into an ASTContext.
251251
/// This interface is primarily used by LLDB.
252252
class MemoryBufferSerializedModuleLoader : public SerializedModuleLoaderBase {
253-
llvm::StringMap<std::unique_ptr<llvm::MemoryBuffer>> MemoryBuffers;
253+
254+
struct MemoryBufferInfo {
255+
std::unique_ptr<llvm::MemoryBuffer> buffer;
256+
llvm::VersionTuple userVersion;
257+
};
258+
259+
llvm::StringMap<MemoryBufferInfo> MemoryBuffers;
254260

255261
MemoryBufferSerializedModuleLoader(ASTContext &ctx,
256262
DependencyTracker *tracker,
@@ -289,8 +295,9 @@ class MemoryBufferSerializedModuleLoader : public SerializedModuleLoaderBase {
289295
///
290296
/// FIXME: make this an actual import *path* once submodules are designed.
291297
void registerMemoryBuffer(StringRef importPath,
292-
std::unique_ptr<llvm::MemoryBuffer> input) {
293-
MemoryBuffers[importPath] = std::move(input);
298+
std::unique_ptr<llvm::MemoryBuffer> input,
299+
llvm::VersionTuple version) {
300+
MemoryBuffers[importPath] = {std::move(input), version};
294301
}
295302

296303
void collectVisibleTopLevelModuleNames(

lib/AST/ASTContext.cpp

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1957,40 +1957,39 @@ bool ASTContext::shouldPerformTypoCorrection() {
19571957

19581958
bool ASTContext::canImportModuleImpl(ImportPath::Element ModuleName,
19591959
llvm::VersionTuple version,
1960-
bool underlyingVersion) const {
1961-
// If this module has already been successfully imported, it is importable.
1962-
if (getLoadedModule(ImportPath::Module::Builder(ModuleName).get()) != nullptr)
1963-
return true;
1964-
1960+
bool underlyingVersion,
1961+
bool updateFailingList) const {
19651962
// If we've failed loading this module before, don't look for it again.
19661963
if (FailedModuleImportNames.count(ModuleName.Item))
19671964
return false;
1968-
1965+
// If no specific version, the module is importable if it has already been imported.
1966+
if (version.empty()) {
1967+
// If this module has already been successfully imported, it is importable.
1968+
if (getLoadedModule(ImportPath::Module::Builder(ModuleName).get()) != nullptr)
1969+
return true;
1970+
}
19691971
// Otherwise, ask the module loaders.
19701972
for (auto &importer : getImpl().ModuleLoaders) {
19711973
if (importer->canImportModule(ModuleName, version, underlyingVersion)) {
19721974
return true;
19731975
}
19741976
}
1975-
1977+
if (updateFailingList && version.empty()) {
1978+
FailedModuleImportNames.insert(ModuleName.Item);
1979+
}
19761980
return false;
19771981
}
19781982

19791983
bool ASTContext::canImportModule(ImportPath::Element ModuleName,
19801984
llvm::VersionTuple version,
19811985
bool underlyingVersion) {
1982-
if (canImportModuleImpl(ModuleName, version, underlyingVersion)) {
1983-
return true;
1984-
} else {
1985-
FailedModuleImportNames.insert(ModuleName.Item);
1986-
return false;
1987-
}
1986+
return canImportModuleImpl(ModuleName, version, underlyingVersion, true);
19881987
}
19891988

19901989
bool ASTContext::canImportModule(ImportPath::Element ModuleName,
19911990
llvm::VersionTuple version,
19921991
bool underlyingVersion) const {
1993-
return canImportModuleImpl(ModuleName, version, underlyingVersion);
1992+
return canImportModuleImpl(ModuleName, version, underlyingVersion, false);
19941993
}
19951994

19961995
ModuleDecl *

lib/ASTSectionImporter/ASTSectionImporter.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ bool swift::parseASTSection(MemoryBufferSerializedModuleLoader &Loader,
4646
llvm::MemoryBuffer::getMemBuffer(moduleData, info.name, false));
4747

4848
// Register the memory buffer.
49-
Loader.registerMemoryBuffer(info.name, std::move(bitstream));
49+
Loader.registerMemoryBuffer(info.name, std::move(bitstream),
50+
info.userModuleVersion);
5051
foundModules.push_back(info.name.str());
5152
}
5253
} else {

lib/Serialization/SerializedModuleLoader.cpp

Lines changed: 76 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -962,22 +962,91 @@ bool swift::extractCompilerFlagsFromInterface(StringRef buffer,
962962

963963
bool SerializedModuleLoaderBase::canImportModule(
964964
ImportPath::Element mID, llvm::VersionTuple version, bool underlyingVersion) {
965+
// If underlying version is specified, this should be handled by Clang importer.
966+
if (!version.empty() && underlyingVersion)
967+
return false;
965968
// Look on disk.
966-
SmallVector<char, 0> *unusedModuleInterfacePath = nullptr;
969+
SmallVectorImpl<char> *unusedModuleInterfacePath = nullptr;
967970
std::unique_ptr<llvm::MemoryBuffer> *unusedModuleBuffer = nullptr;
968971
std::unique_ptr<llvm::MemoryBuffer> *unusedModuleDocBuffer = nullptr;
969972
std::unique_ptr<llvm::MemoryBuffer> *unusedModuleSourceInfoBuffer = nullptr;
970973
bool isFramework = false;
971974
bool isSystemModule = false;
972-
return findModule(mID, unusedModuleInterfacePath, unusedModuleBuffer,
973-
unusedModuleDocBuffer, unusedModuleSourceInfoBuffer,
974-
isFramework, isSystemModule);
975+
976+
llvm::SmallString<256> moduleInterfacePath;
977+
std::unique_ptr<llvm::MemoryBuffer> moduleInputBuffer;
978+
std::unique_ptr<llvm::MemoryBuffer> moduleDocBuffer;
979+
if (!version.empty()) {
980+
unusedModuleInterfacePath = &moduleInterfacePath;
981+
unusedModuleBuffer = &moduleInputBuffer;
982+
unusedModuleDocBuffer = &moduleDocBuffer;
983+
}
984+
985+
auto found = findModule(mID, unusedModuleInterfacePath, unusedModuleBuffer,
986+
unusedModuleDocBuffer, unusedModuleSourceInfoBuffer,
987+
isFramework, isSystemModule);
988+
// If we cannot find the module, don't continue.
989+
if (!found)
990+
return false;
991+
// If no version number is specified, don't continue.
992+
if (version.empty())
993+
return true;
994+
assert(found);
995+
assert(!version.empty());
996+
assert(!underlyingVersion);
997+
llvm::VersionTuple currentVersion;
998+
if (!moduleInterfacePath.empty()) {
999+
// Read the inteface file and extract its compiler arguments line
1000+
if (auto file = llvm::MemoryBuffer::getFile(moduleInterfacePath)) {
1001+
llvm::BumpPtrAllocator alloc;
1002+
llvm::StringSaver argSaver(alloc);
1003+
SmallVector<const char*, 8> args;
1004+
(void)extractCompilerFlagsFromInterface((*file)->getBuffer(), argSaver, args);
1005+
for (unsigned I = 0, N = args.size(); I + 1 < N; I++) {
1006+
// Check the version number specified via -user-module-version.
1007+
StringRef current(args[I]), next(args[I + 1]);
1008+
if (current == "-user-module-version") {
1009+
currentVersion.tryParse(next);
1010+
break;
1011+
}
1012+
}
1013+
}
1014+
}
1015+
// If failing to extract the user version from the interface file, try the binary
1016+
// format, if present.
1017+
if (currentVersion.empty() && unusedModuleBuffer) {
1018+
auto metaData =
1019+
serialization::validateSerializedAST((*unusedModuleBuffer)->getBuffer());
1020+
currentVersion = metaData.userModuleVersion;
1021+
}
1022+
1023+
if (currentVersion.empty()) {
1024+
Ctx.Diags.diagnose(mID.Loc, diag::cannot_find_project_version, "Swift",
1025+
mID.Item.str());
1026+
return true;
1027+
}
1028+
1029+
return currentVersion >= version;
9751030
}
9761031

9771032
bool MemoryBufferSerializedModuleLoader::canImportModule(
9781033
ImportPath::Element mID, llvm::VersionTuple version, bool underlyingVersion) {
979-
// See if we find it in the registered memory buffers.
980-
return MemoryBuffers.count(mID.Item.str());
1034+
// If underlying version is specified, this should be handled by Clang importer.
1035+
if (!version.empty() && underlyingVersion)
1036+
return false;
1037+
auto mIt = MemoryBuffers.find(mID.Item.str());
1038+
if (mIt == MemoryBuffers.end())
1039+
return false;
1040+
if (version.empty())
1041+
return true;
1042+
if (mIt->second.userVersion.empty()) {
1043+
Ctx.Diags.diagnose(mID.Loc, diag::cannot_find_project_version, "Swift",
1044+
mID.Item.str());
1045+
return true;
1046+
}
1047+
assert(!version.empty());
1048+
assert(!(mIt->second.userVersion.empty()));
1049+
return mIt->second.userVersion >= version;
9811050
}
9821051

9831052
ModuleDecl *
@@ -1055,7 +1124,7 @@ MemoryBufferSerializedModuleLoader::loadModule(SourceLoc importLoc,
10551124

10561125
bool isFramework = false;
10571126
std::unique_ptr<llvm::MemoryBuffer> moduleInputBuffer;
1058-
moduleInputBuffer = std::move(bufIter->second);
1127+
moduleInputBuffer = std::move(bufIter->second.buffer);
10591128
MemoryBuffers.erase(bufIter);
10601129
assert(moduleInputBuffer);
10611130

test/Parse/versioned_canimport.swift

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %empty-directory(%t/textual)
3+
// RUN: %empty-directory(%t/binary)
4+
// RUN: %empty-directory(%t/module-cache)
5+
6+
// RUN: echo "public func foo() {}" > %t/Foo.swift
7+
// RUN: %target-swift-frontend -emit-module %t/Foo.swift -module-name Foo -swift-version 5 -disable-implicit-concurrency-module-import -user-module-version 113.330 -emit-module-interface-path %t/textual/Foo.swiftinterface -enable-library-evolution -emit-module-path %t/binary/Foo.swiftmodule
8+
// RUN: %target-swift-frontend -emit-module %t/Foo.swift -module-name Bar -swift-version 5 -disable-implicit-concurrency-module-import -emit-module-interface-path %t/textual/Bar.swiftinterface -enable-library-evolution -emit-module-path %t/binary/Bar.swiftmodule
9+
10+
// RUN: %target-typecheck-verify-swift -disable-implicit-concurrency-module-import -I %t/textual
11+
// RUN: %target-typecheck-verify-swift -disable-implicit-concurrency-module-import -I %t/binary
12+
13+
import Foo
14+
import Bar
15+
16+
#if canImport(Bar, version: 113.331) // expected-warning {{cannot find user version number for Swift module 'Bar'; version number ignored}}
17+
#endif
18+
19+
#if canImport(Bar, version: 2) // expected-warning {{cannot find user version number for Swift module 'Bar'; version number ignored}}
20+
#endif
21+
22+
func canImportVersioned() {
23+
#if canImport(Foo, version: 113.331)
24+
let a = 1
25+
#endif
26+
27+
#if canImport(Foo, version: 113.3000)
28+
let b = 1
29+
#endif
30+
31+
#if canImport(Foo, version: 114)
32+
let c = 1
33+
#endif
34+
35+
#if canImport(Foo, version: 4)
36+
let d = 1 // expected-warning {{initialization of immutable value 'd' was never used; consider replacing with assignment to '_' or removing it}}
37+
#endif
38+
39+
#if canImport(Foo, version: 113.33)
40+
let e = 1 // expected-warning {{initialization of immutable value 'e' was never used; consider replacing with assignment to '_' or removing it}}
41+
#endif
42+
43+
#if canImport(Foo, underlyingVersion: 113.33)
44+
let ee = 1
45+
#endif
46+
47+
#if canImport(Foo, version: 113.329)
48+
let f = 1 // expected-warning {{initialization of immutable value 'f' was never used; consider replacing with assignment to '_' or removing it}}
49+
#endif
50+
51+
#if canImport(Foo, version: 113.330)
52+
let g = 1 // expected-warning {{initialization of immutable value 'g' was never used; consider replacing with assignment to '_' or removing it}}
53+
#endif
54+
55+
#if canImport(Foo)
56+
let h = 1 // expected-warning {{initialization of immutable value 'h' was never used; consider replacing with assignment to '_' or removing it}}
57+
#endif
58+
}

0 commit comments

Comments
 (0)