Skip to content

Commit 2e31e46

Browse files
authored
Merge pull request swiftlang#80414 from artemcm/RestorePriorCanImportBehaviour
[Explicit Module Builds] Switch versioned `canImport` to return `true` when encountering unversioned candidate
2 parents b176ee4 + c9ba79c commit 2e31e46

10 files changed

+238
-111
lines changed

include/swift/AST/ASTContext.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1192,12 +1192,14 @@ class ASTContext final {
11921192
/// module is loaded in full.
11931193
bool canImportModuleImpl(ImportPath::Module ModulePath, SourceLoc loc,
11941194
llvm::VersionTuple version, bool underlyingVersion,
1195-
bool updateFailingList,
1196-
llvm::VersionTuple &foundVersion) const;
1195+
bool isSourceCanImport,
1196+
llvm::VersionTuple &foundVersion,
1197+
llvm::VersionTuple &foundUnderlyingClangVersion) const;
11971198

11981199
/// Add successful canImport modules.
1199-
void addSucceededCanImportModule(StringRef moduleName, bool underlyingVersion,
1200-
const llvm::VersionTuple &versionInfo);
1200+
void addSucceededCanImportModule(StringRef moduleName,
1201+
const llvm::VersionTuple &versionInfo,
1202+
const llvm::VersionTuple &underlyingVersionInfo);
12011203

12021204
public:
12031205
namelookup::ImportCache &getImportCache() const;

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1272,7 +1272,8 @@ REMARK(explicit_interface_build_skipped,none,
12721272
(StringRef))
12731273

12741274
WARNING(cannot_find_project_version,none,
1275-
"cannot find user version number for %0 module '%1'; version number ignored", (StringRef, StringRef))
1275+
"cannot find user version number for%select{| Clang}1 module '%0';"
1276+
" version number ignored", (StringRef, bool))
12761277

12771278
// Operator decls
12781279
ERROR(ambiguous_operator_decls,none,

lib/AST/ASTContext.cpp

Lines changed: 126 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -849,18 +849,16 @@ ASTContext::ASTContext(
849849
registerNameLookupRequestFunctions(evaluator);
850850

851851
// Register canImport module info.
852-
for (auto &info: SearchPathOpts.CanImportModuleInfo) {
853-
addSucceededCanImportModule(info.ModuleName, false, info.Version);
854-
addSucceededCanImportModule(info.ModuleName, true, info.UnderlyingVersion);
855-
}
852+
for (auto &info: SearchPathOpts.CanImportModuleInfo)
853+
addSucceededCanImportModule(info.ModuleName, info.Version, info.UnderlyingVersion);
856854

857855
// Provide a default OnDiskOutputBackend if user didn't supply one.
858856
if (!OutputBackend)
859857
OutputBackend = llvm::makeIntrusiveRefCnt<llvm::vfs::OnDiskOutputBackend>();
858+
860859
// Insert all block list config paths.
861-
for (auto path: langOpts.BlocklistConfigFilePaths) {
860+
for (auto path: langOpts.BlocklistConfigFilePaths)
862861
blockListConfig.addConfigureFilePath(path);
863-
}
864862
}
865863

866864
void ASTContext::Implementation::dump(llvm::raw_ostream &os) const {
@@ -2655,34 +2653,25 @@ static bool isClangModuleVersion(const ModuleLoader::ModuleVersionInfo &info) {
26552653
}
26562654
}
26572655

2658-
static StringRef
2659-
getModuleVersionKindString(const ModuleLoader::ModuleVersionInfo &info) {
2660-
switch (info.getSourceKind()) {
2661-
case ModuleLoader::ModuleVersionSourceKind::ClangModuleTBD:
2662-
return "Clang";
2663-
case ModuleLoader::ModuleVersionSourceKind::SwiftBinaryModule:
2664-
case ModuleLoader::ModuleVersionSourceKind::SwiftInterface:
2665-
return "Swift";
2666-
}
2667-
}
2668-
26692656
void ASTContext::addSucceededCanImportModule(
2670-
StringRef moduleName, bool underlyingVersion,
2671-
const llvm::VersionTuple &versionInfo) {
2657+
StringRef moduleName,
2658+
const llvm::VersionTuple &versionInfo,
2659+
const llvm::VersionTuple &underlyingVersionInfo) {
2660+
// We have previously recorded a successful canImport
2661+
// information for this module.
2662+
if (CanImportModuleVersions.count(moduleName.str()))
2663+
return;
2664+
26722665
auto &entry = CanImportModuleVersions[moduleName.str()];
2673-
if (!versionInfo.empty()) {
2674-
if (underlyingVersion)
2675-
entry.UnderlyingVersion = versionInfo;
2676-
else
2677-
entry.Version = versionInfo;
2678-
}
2666+
entry.Version = versionInfo;
2667+
entry.UnderlyingVersion = underlyingVersionInfo;
26792668
}
26802669

2681-
bool ASTContext::canImportModuleImpl(ImportPath::Module ModuleName,
2682-
SourceLoc loc, llvm::VersionTuple version,
2683-
bool underlyingVersion,
2684-
bool updateFailingList,
2685-
llvm::VersionTuple &foundVersion) const {
2670+
bool ASTContext::canImportModuleImpl(
2671+
ImportPath::Module ModuleName, SourceLoc loc, llvm::VersionTuple version,
2672+
bool underlyingVersion, bool isSourceCanImport,
2673+
llvm::VersionTuple &foundVersion,
2674+
llvm::VersionTuple &foundUnderlyingClangVersion) const {
26862675
SmallString<64> FullModuleName;
26872676
ModuleName.getString(FullModuleName);
26882677
auto ModuleNameStr = FullModuleName.str().str();
@@ -2691,6 +2680,20 @@ bool ASTContext::canImportModuleImpl(ImportPath::Module ModuleName,
26912680
if (FailedModuleImportNames.count(ModuleNameStr))
26922681
return false;
26932682

2683+
auto missingVersion = [this, &loc, &ModuleName,
2684+
&underlyingVersion]() -> bool {
2685+
// The module version could not be parsed from the preferred source for
2686+
// this query. Diagnose and return `true` to indicate that the unversioned module
2687+
// will satisfy the query.
2688+
auto mID = ModuleName[0];
2689+
auto diagLoc = mID.Loc;
2690+
if (mID.Loc.isInvalid())
2691+
diagLoc = loc;
2692+
Diags.diagnose(diagLoc, diag::cannot_find_project_version, mID.Item.str(),
2693+
underlyingVersion);
2694+
return true;
2695+
};
2696+
26942697
// If this module has already been checked or there is information for the
26952698
// module from commandline, use that information instead of loading the
26962699
// module.
@@ -2699,35 +2702,95 @@ bool ASTContext::canImportModuleImpl(ImportPath::Module ModuleName,
26992702
if (version.empty())
27002703
return true;
27012704

2702-
if (underlyingVersion) {
2703-
if (!Found->second.UnderlyingVersion.empty())
2704-
return version <= Found->second.UnderlyingVersion;
2705-
} else {
2706-
if (!Found->second.Version.empty())
2707-
return version <= Found->second.Version;
2705+
const auto &foundComparisonVersion = underlyingVersion
2706+
? Found->second.UnderlyingVersion
2707+
: Found->second.Version;
2708+
if (!foundComparisonVersion.empty())
2709+
return version <= foundComparisonVersion;
2710+
else
2711+
return missingVersion();
2712+
}
2713+
2714+
// When looking up a module, each module importer will report back
2715+
// if it finds a module with a specified version. This routine verifies
2716+
// whether said version is valid and if it superceeds the best
2717+
// previously-discovered version of this module found.
2718+
auto validateVersion =
2719+
[](const ModuleLoader::ModuleVersionInfo &bestVersionInfo,
2720+
const ModuleLoader::ModuleVersionInfo &versionInfo,
2721+
bool underlyingVersion) {
2722+
if (!versionInfo.isValid())
2723+
return false; // The loader didn't attempt to parse a version.
2724+
2725+
if (underlyingVersion && !isClangModuleVersion(versionInfo))
2726+
return false; // We're only matching Clang module versions.
2727+
2728+
if (bestVersionInfo.isValid() &&
2729+
versionInfo.getSourceKind() <= bestVersionInfo.getSourceKind())
2730+
return false; // This module version's source is lower priority.
2731+
2732+
return true;
2733+
};
2734+
2735+
// For each module loader, attempt to discover queried module,
2736+
// along the way record the discovered module's version as well as
2737+
// the discovered module's underlying Clang module's version.
2738+
auto lookupVersionedModule =
2739+
[&](ModuleLoader::ModuleVersionInfo &bestVersionInfo,
2740+
ModuleLoader::ModuleVersionInfo &bestUnderlyingVersionInfo) -> bool {
2741+
for (auto &importer : getImpl().ModuleLoaders) {
2742+
ModuleLoader::ModuleVersionInfo versionInfo;
2743+
if (!importer->canImportModule(ModuleName, loc, &versionInfo))
2744+
continue; // The loader can't find the module.
2745+
2746+
if (validateVersion(bestVersionInfo, versionInfo,
2747+
/* underlyingVersion */ false))
2748+
bestVersionInfo = versionInfo;
2749+
if (validateVersion(bestUnderlyingVersionInfo, versionInfo,
2750+
/* underlyingVersion */ true))
2751+
bestUnderlyingVersionInfo = versionInfo;
27082752
}
27092753

2710-
// If the canImport information is coming from the command-line, then no
2711-
// need to continue the search, return false. For checking modules that are
2712-
// not passed from command-line, allow fallback to the module loading since
2713-
// this is not in a canImport request context that has already been resolved
2714-
// by scanner.
2715-
if (!SearchPathOpts.CanImportModuleInfo.empty())
2754+
if (!underlyingVersion && !bestVersionInfo.isValid())
27162755
return false;
2717-
}
2756+
2757+
if (underlyingVersion && !bestUnderlyingVersionInfo.isValid())
2758+
return false;
2759+
2760+
foundVersion = bestVersionInfo.getVersion();
2761+
foundUnderlyingClangVersion = bestUnderlyingVersionInfo.getVersion();
2762+
return true;
2763+
};
2764+
2765+
// For queries which do not care about any kind of module information
2766+
// such as e.g. `testImportModule`, simply return `true` as soon
2767+
// as *any* loader can find the queried module.
2768+
auto lookupModule = [&]() -> bool {
2769+
for (auto &importer : getImpl().ModuleLoaders) {
2770+
ModuleLoader::ModuleVersionInfo versionInfo;
2771+
if (!importer->canImportModule(ModuleName, loc, &versionInfo))
2772+
continue; // The loader can't find the module.
2773+
return true;
2774+
}
2775+
return false;
2776+
};
27182777

27192778
if (version.empty()) {
27202779
// If this module has already been successfully imported, it is importable.
27212780
if (getLoadedModule(ModuleName) != nullptr)
27222781
return true;
2782+
2783+
if (!isSourceCanImport)
2784+
return lookupModule();
2785+
2786+
// Otherwise, ask whether any module loader can load the module,
2787+
// and record the module version that the succeeding loader
2788+
// observed.
2789+
ModuleLoader::ModuleVersionInfo versionInfo, underlyingVersionInfo;
2790+
if (lookupVersionedModule(versionInfo, underlyingVersionInfo))
2791+
return true;
27232792

2724-
// Otherwise, ask whether any module loader can load the module.
2725-
for (auto &importer : getImpl().ModuleLoaders) {
2726-
if (importer->canImportModule(ModuleName, loc, nullptr))
2727-
return true;
2728-
}
2729-
2730-
if (updateFailingList)
2793+
if (isSourceCanImport)
27312794
FailedModuleImportNames.insert(ModuleNameStr);
27322795

27332796
return false;
@@ -2736,43 +2799,15 @@ bool ASTContext::canImportModuleImpl(ImportPath::Module ModuleName,
27362799
// We need to check whether the version of the module is high enough.
27372800
// Retrieve a module version from each module loader that can find the module
27382801
// and use the best source available for the query.
2739-
ModuleLoader::ModuleVersionInfo bestVersionInfo;
2740-
for (auto &importer : getImpl().ModuleLoaders) {
2741-
ModuleLoader::ModuleVersionInfo versionInfo;
2742-
2743-
if (!importer->canImportModule(ModuleName, loc, &versionInfo))
2744-
continue; // The loader can't find the module.
2745-
2746-
if (!versionInfo.isValid())
2747-
continue; // The loader didn't attempt to parse a version.
2748-
2749-
if (underlyingVersion && !isClangModuleVersion(versionInfo))
2750-
continue; // We're only matching Clang module versions.
2751-
2752-
if (bestVersionInfo.isValid() &&
2753-
versionInfo.getSourceKind() <= bestVersionInfo.getSourceKind())
2754-
continue; // This module version's source is lower priority.
2755-
2756-
bestVersionInfo = versionInfo;
2757-
}
2758-
2759-
if (!bestVersionInfo.isValid())
2802+
ModuleLoader::ModuleVersionInfo versionInfo, underlyingVersionInfo;
2803+
if (!lookupVersionedModule(versionInfo, underlyingVersionInfo))
27602804
return false;
27612805

2762-
if (bestVersionInfo.getVersion().empty()) {
2763-
// The module version could not be parsed from the preferred source for
2764-
// this query. Diagnose and treat the query as if it succeeded.
2765-
auto mID = ModuleName[0];
2766-
auto diagLoc = mID.Loc;
2767-
if (mID.Loc.isInvalid())
2768-
diagLoc = loc;
2769-
Diags.diagnose(diagLoc, diag::cannot_find_project_version,
2770-
getModuleVersionKindString(bestVersionInfo), mID.Item.str());
2771-
return true;
2772-
}
2806+
const auto &queryVersion = underlyingVersion ? underlyingVersionInfo : versionInfo;
2807+
if (queryVersion.getVersion().empty())
2808+
return missingVersion();
27732809

2774-
foundVersion = bestVersionInfo.getVersion();
2775-
return version <= bestVersionInfo.getVersion();
2810+
return version <= queryVersion.getVersion();
27762811
}
27772812

27782813
void ASTContext::forEachCanImportVersionCheck(
@@ -2787,22 +2822,26 @@ bool ASTContext::canImportModule(ImportPath::Module moduleName, SourceLoc loc,
27872822
llvm::VersionTuple version,
27882823
bool underlyingVersion) {
27892824
llvm::VersionTuple versionInfo;
2825+
llvm::VersionTuple underlyingVersionInfo;
27902826
if (!canImportModuleImpl(moduleName, loc, version, underlyingVersion, true,
2791-
versionInfo))
2827+
versionInfo, underlyingVersionInfo))
27922828
return false;
27932829

27942830
SmallString<64> fullModuleName;
27952831
moduleName.getString(fullModuleName);
2796-
addSucceededCanImportModule(fullModuleName, underlyingVersion, versionInfo);
2832+
2833+
addSucceededCanImportModule(fullModuleName, versionInfo, underlyingVersionInfo);
27972834
return true;
27982835
}
27992836

28002837
bool ASTContext::testImportModule(ImportPath::Module ModuleName,
28012838
llvm::VersionTuple version,
28022839
bool underlyingVersion) const {
28032840
llvm::VersionTuple versionInfo;
2841+
llvm::VersionTuple underlyingVersionInfo;
28042842
return canImportModuleImpl(ModuleName, SourceLoc(), version,
2805-
underlyingVersion, false, versionInfo);
2843+
underlyingVersion, false, versionInfo,
2844+
underlyingVersionInfo);
28062845
}
28072846

28082847
ModuleDecl *

test/CAS/can-import.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@
2121
// CMD-NEXT: "C"
2222
// CMD-NEXT: "1.0"
2323
// CMD-NEXT: "0"
24-
// CMD-NEXT: "-module-can-import"
24+
// CMD-NEXT: "-module-can-import-version"
2525
// CMD-NEXT: "D"
26+
// CMD-NEXT: "1.0"
27+
// CMD-NEXT: "0"
2628
// CMD-NEXT: "-module-can-import-version"
2729
// CMD-NEXT: "Simple"
2830
// CMD-NEXT: "0"
@@ -111,6 +113,11 @@ public func c() { }
111113
// swift-module-flags: -module-name D -O -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import -parse-stdlib -user-module-version 1.0
112114
public func d() { }
113115

116+
//--- include/Simple.swiftinterface
117+
// swift-interface-format-version: 1.0
118+
// swift-module-flags: -module-name Simple -O -disable-implicit-string-processing-module-import -disable-implicit-concurrency-module-import -parse-stdlib
119+
public func simple() { }
120+
114121
//--- frameworks/Simple.framework/Modules/module.modulemap
115122
framework module Simple {
116123
umbrella header "Simple.h"

test/ClangImporter/can_import_underlying_version_tbd_missing_version.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ func canImportUnderlyingVersion() {
1616
}
1717

1818
func canImportVersion() {
19-
#if canImport(Simple, _version: 3.3) // expected-warning {{cannot find user version number for Clang module 'Simple'; version number ignored}}
20-
// TODO(ParserValidation): expected-warning@-1 *{{cannot find user version number for Clang module 'Simple'; version number ignored}}
19+
#if canImport(Simple, _version: 3.3) // expected-warning {{cannot find user version number for module 'Simple'; version number ignored}}
20+
// TODO(ParserValidation): expected-warning@-1 *{{cannot find user version number for module 'Simple'; version number ignored}}
2121
let a = 1 // expected-warning {{initialization of immutable value 'a' was never used; consider replacing with assignment to '_' or removing it}}
2222
#endif
2323
}

test/Parse/ConditionalCompilation/can_import_options.swift

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ func canImport() {
99
let basicCheck = 1 // expected-warning {{initialization of immutable value 'basicCheck' was never used; consider replacing with assignment to '_' or removing it}}
1010
#endif
1111

12-
#if canImport(Foo, _version: 1)
13-
// No actual Foo to be imported since it is not versioned.
14-
let versionCheck = 1
12+
#if canImport(Foo, _version: 1) // expected-warning {{cannot find user version number for module 'Foo'; version number ignored}}
13+
// TODO(ParserValidation): expected-warning@-1 *{{cannot find user version number for module 'Foo'; version number ignored}}
14+
// An unversioned 'Foo' causes versioned queries to evaluate to 'true'
15+
let versionCheck = 1 // expected-warning {{initialization of immutable value 'versionCheck' was never used; consider replacing with assignment to '_' or removing it}}
1516
#endif
1617
}
1718

@@ -72,9 +73,10 @@ func canImportVersioned() {
7273
let extraComponent = 1 // expected-warning {{initialization of immutable value 'extraComponent' was never used; consider replacing with assignment to '_' or removing it}}
7374
#endif
7475

75-
#if canImport(Bar, _underlyingVersion: 113.33)
76-
// Bar is a Swift module with no underlying clang module.
77-
let underlyingMinorSmaller = 1
76+
#if canImport(Bar, _underlyingVersion: 113.33) // expected-warning{{cannot find user version number for Clang module 'Bar'; version number ignored}}
77+
// TODO(ParserValidation): expected-warning@-1 *{{cannot find user version number for Clang module 'Bar'; version number ignored}}
78+
// Bar is an unversioned Swift module with no underlying clang module.
79+
let underlyingMinorSmaller = 1 // expected-warning {{initialization of immutable value 'underlyingMinorSmaller' was never used; consider replacing with assignment to '_' or removing it}}
7880
#endif
7981

8082
#if canImport(Bar)

0 commit comments

Comments
 (0)