[Explicit Module Builds] Switch versioned 'canImport' to return 'true' when encountering unversioned candidate

Specifically, when the scanner found a candidate which does not carry a user-specified version, it will pass '-module-can-import Foo' to compilation. During compilation, if the check is versioned but the candidate is unversioned, evaluate the check to 'true' to restore the behavior we had with implicitly-built modules.

Resolves rdar://148134993
This commit is contained in:
Artem Chikin
2025-03-31 10:34:29 -07:00
parent 86bfe3a0a4
commit d2ea34c0bd
6 changed files with 91 additions and 25 deletions

View File

@@ -1184,7 +1184,7 @@ public:
/// module is loaded in full.
bool canImportModuleImpl(ImportPath::Module ModulePath, SourceLoc loc,
llvm::VersionTuple version, bool underlyingVersion,
bool updateFailingList,
bool isSourceCanImport,
llvm::VersionTuple &foundVersion) const;
/// Add successful canImport modules.

View File

@@ -2674,7 +2674,7 @@ void ASTContext::addSucceededCanImportModule(
bool ASTContext::canImportModuleImpl(ImportPath::Module ModuleName,
SourceLoc loc, llvm::VersionTuple version,
bool underlyingVersion,
bool updateFailingList,
bool isSourceCanImport,
llvm::VersionTuple &foundVersion) const {
SmallString<64> FullModuleName;
ModuleName.getString(FullModuleName);
@@ -2692,21 +2692,14 @@ bool ASTContext::canImportModuleImpl(ImportPath::Module ModuleName,
if (version.empty())
return true;
if (underlyingVersion) {
if (!Found->second.UnderlyingVersion.empty())
return version <= Found->second.UnderlyingVersion;
} else {
if (!Found->second.Version.empty())
return version <= Found->second.Version;
}
// If the canImport information is coming from the command-line, then no
// need to continue the search, return false. For checking modules that are
// not passed from command-line, allow fallback to the module loading since
// this is not in a canImport request context that has already been resolved
// by scanner.
if (!SearchPathOpts.CanImportModuleInfo.empty())
return false;
if (underlyingVersion)
return Found->second.UnderlyingVersion.empty()
? true
: version <= Found->second.UnderlyingVersion;
else
return Found->second.Version.empty()
? true
: version <= Found->second.Version;
}
if (version.empty()) {
@@ -2720,7 +2713,7 @@ bool ASTContext::canImportModuleImpl(ImportPath::Module ModuleName,
return true;
}
if (updateFailingList)
if (isSourceCanImport)
FailedModuleImportNames.insert(ModuleNameStr);
return false;

View File

@@ -73,8 +73,8 @@ func canImportVersioned() {
#endif
#if canImport(Bar, _underlyingVersion: 113.33)
// Bar is a Swift module with no underlying clang module.
let underlyingMinorSmaller = 1
// Bar is an unversioned Swift module with no underlying clang module.
let underlyingMinorSmaller = 1 // expected-warning {{initialization of immutable value 'underlyingMinorSmaller' was never used; consider replacing with assignment to '_' or removing it}}
#endif
#if canImport(Bar)

View File

@@ -68,8 +68,8 @@ func canImportVersioned() {
#endif
#if canImport(Foo, _underlyingVersion: 113.33)
// Foo is a Swift module with no underlying clang module.
let underlyingMinorSmaller = 1
// Foo is an unversioned Swift module with no underlying clang module.
let underlyingMinorSmaller = 1 // expected-warning {{initialization of immutable value 'underlyingMinorSmaller' was never used; consider replacing with assignment to '_' or removing it}}
#endif
#if canImport(Foo)
@@ -136,7 +136,7 @@ func canImportVersionedString() {
#endif
#if canImport(Foo, _underlyingVersion: "113.33")
// Foo is a Swift module with no underlying clang module.
let underlyingMinorSmaller = 1
// Foo is an unversioned Swift module with no underlying clang module.
let underlyingMinorSmaller = 1 // expected-warning {{initialization of immutable value 'underlyingMinorSmaller' was never used; consider replacing with assignment to '_' or removing it}}
#endif
}

View File

@@ -0,0 +1,58 @@
// REQUIRES: executable_test
// RUN: %empty-directory(%t)
// RUN: %empty-directory(%t/inputs)
// RUN: %empty-directory(%t/module-cache)
// RUN: split-file %s %t
// - Fixup the input module file map
// RUN: sed -e "s|INPUTSDIR|%/t/inputs|g" %t/map.json.template > %t/map.json.template1
// RUN: sed -e "s|STDLIBMOD|%/stdlib_module|g" %t/map.json.template1 > %t/map.json.template2
// RUN: sed -e "s|ONONEMOD|%/ononesupport_module|g" %t/map.json.template2 > %t/map.json.template3
// RUN: sed -e "s|MYLIBMOD|%/t/inputs/MyLib.swiftmodule|g" %t/map.json.template3 > %t/map.json.template4
// RUN: sed -e "s|SWIFTLIBDIR|%swift-lib-dir|g" %t/map.json.template4 > %t/map.json
// - Set up explicit dependencies for MyExe
// RUN: %target-swift-emit-pcm -module-name SwiftShims %swift-lib-dir/swift/shims/module.modulemap -o %t/inputs/SwiftShims.pcm
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/inputs/MyLib.swiftmodule %t/MyLib.swift -module-name MyLib -disable-implicit-concurrency-module-import -disable-implicit-string-processing-module-import -disable-implicit-swift-modules -explicit-swift-module-map-file %t/map.json
// - Build MyExe
// RUN: %target-swift-frontend -c %t/MyExe.swift -I %t/inputs -disable-implicit-concurrency-module-import -disable-implicit-string-processing-module-import -disable-implicit-swift-modules -explicit-swift-module-map-file %t/map.json -module-can-import MyLib -verify
//--- map.json.template
[
{
"moduleName": "Swift",
"modulePath": "STDLIBMOD",
"isFramework": false
},
{
"moduleName": "SwiftOnoneSupport",
"modulePath": "ONONEMOD",
"isFramework": false
},
{
"moduleName": "SwiftShims",
"isFramework": false,
"clangModuleMapPath": "SWIFTLIBDIR/swift/shims/module.modulemap",
"clangModulePath": "INPUTSDIR/SwiftShims.pcm"
},
{
"moduleName": "MyLib",
"modulePath": "MYLIBMOD",
"isFramework": false
}
]
//--- MyLib.swift
public func Foo() {
print("foo")
}
//--- MyExe.swift
#if canImport(MyLib, _version: 42)
#warning("versioned canImport() of unversioned module is working")
// expected-warning@-1{{versioned canImport() of unversioned module is working}}
#else
#warning("versioned canImport() of unversioned module is broken")
#endif

View File

@@ -1,6 +1,10 @@
// RUN: %empty-directory(%t)
// RUN: %empty-directory(%t/unversionedInputs)
// RUN: split-file %s %t
// RUN: %target-swift-frontend -scan-dependencies -module-cache-path %t/clang-module-cache %t/main.swift -module-name Test -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -I %t/include -swift-version 4
// RUN: %target-swift-frontend -emit-module -emit-module-path %t/unversionedUnputs/Foo.swiftmodule %t/Foo.swift -module-name Foo -disable-implicit-concurrency-module-import -disable-implicit-string-processing-module-import
// RUN: %target-swift-frontend -scan-dependencies -module-cache-path %t/clang-module-cache %t/main.swift -module-name Test -o %t/deps.json -I %S/Inputs/CHeaders -I %S/Inputs/Swift -I %t/include -I %t/unversionedUnputs
// RUN: %{python} %S/../CAS/Inputs/SwiftDepsExtractor.py %t/deps.json Test directDependencies | %FileCheck %s
@@ -17,13 +21,24 @@
// CMD-NEXT: "ClangTest.Sub"
// CMD-NEXT: "-module-can-import"
// CMD-NEXT: "F"
// CMD-NEXT: "-module-can-import"
// CMD-NEXT: "Foo"
// CMD-NEXT: "-module-can-import-version"
// CMD-NEXT: "Version"
// CMD-NEXT: "100.1"
// CMD-NEXT: "0"
//--- Foo.swift
public func Foo() {
print("foo")
}
//--- main.swift
#if canImport(Foo, _version: 44)
import Foo
#endif
#if canImport(Missing)
import G
#endif