Skip to content

Commit c9ba79c

Browse files
committed
[Dependency Scanning] Always record best version of discovered 'canImport'-ed modules
Suppose module 'Foo' exists in the search paths and specifies user module version '1.0'. If the first encountered 'canImport' query is unversioned: ... Followed by a versioned one: ... The success of the first check will record an unversioned successful canImport, which will cause the second check to evaluate to 'true', which is incorrect. This change causes even unversioned 'canImport' checks to track and record the discovered user module version.
1 parent d2ea34c commit c9ba79c

File tree

9 files changed

+164
-103
lines changed

9 files changed

+164
-103
lines changed

include/swift/AST/ASTContext.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1185,11 +1185,13 @@ class ASTContext final {
11851185
bool canImportModuleImpl(ImportPath::Module ModulePath, SourceLoc loc,
11861186
llvm::VersionTuple version, bool underlyingVersion,
11871187
bool isSourceCanImport,
1188-
llvm::VersionTuple &foundVersion) const;
1188+
llvm::VersionTuple &foundVersion,
1189+
llvm::VersionTuple &foundUnderlyingClangVersion) const;
11891190

11901191
/// Add successful canImport modules.
1191-
void addSucceededCanImportModule(StringRef moduleName, bool underlyingVersion,
1192-
const llvm::VersionTuple &versionInfo);
1192+
void addSucceededCanImportModule(StringRef moduleName,
1193+
const llvm::VersionTuple &versionInfo,
1194+
const llvm::VersionTuple &underlyingVersionInfo);
11931195

11941196
public:
11951197
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 & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -847,18 +847,16 @@ ASTContext::ASTContext(
847847
registerNameLookupRequestFunctions(evaluator);
848848

849849
// Register canImport module info.
850-
for (auto &info: SearchPathOpts.CanImportModuleInfo) {
851-
addSucceededCanImportModule(info.ModuleName, false, info.Version);
852-
addSucceededCanImportModule(info.ModuleName, true, info.UnderlyingVersion);
853-
}
850+
for (auto &info: SearchPathOpts.CanImportModuleInfo)
851+
addSucceededCanImportModule(info.ModuleName, info.Version, info.UnderlyingVersion);
854852

855853
// Provide a default OnDiskOutputBackend if user didn't supply one.
856854
if (!OutputBackend)
857855
OutputBackend = llvm::makeIntrusiveRefCnt<llvm::vfs::OnDiskOutputBackend>();
856+
858857
// Insert all block list config paths.
859-
for (auto path: langOpts.BlocklistConfigFilePaths) {
858+
for (auto path: langOpts.BlocklistConfigFilePaths)
860859
blockListConfig.addConfigureFilePath(path);
861-
}
862860
}
863861

864862
void ASTContext::Implementation::dump(llvm::raw_ostream &os) const {
@@ -2648,34 +2646,25 @@ static bool isClangModuleVersion(const ModuleLoader::ModuleVersionInfo &info) {
26482646
}
26492647
}
26502648

2651-
static StringRef
2652-
getModuleVersionKindString(const ModuleLoader::ModuleVersionInfo &info) {
2653-
switch (info.getSourceKind()) {
2654-
case ModuleLoader::ModuleVersionSourceKind::ClangModuleTBD:
2655-
return "Clang";
2656-
case ModuleLoader::ModuleVersionSourceKind::SwiftBinaryModule:
2657-
case ModuleLoader::ModuleVersionSourceKind::SwiftInterface:
2658-
return "Swift";
2659-
}
2660-
}
2661-
26622649
void ASTContext::addSucceededCanImportModule(
2663-
StringRef moduleName, bool underlyingVersion,
2664-
const llvm::VersionTuple &versionInfo) {
2650+
StringRef moduleName,
2651+
const llvm::VersionTuple &versionInfo,
2652+
const llvm::VersionTuple &underlyingVersionInfo) {
2653+
// We have previously recorded a successful canImport
2654+
// information for this module.
2655+
if (CanImportModuleVersions.count(moduleName.str()))
2656+
return;
2657+
26652658
auto &entry = CanImportModuleVersions[moduleName.str()];
2666-
if (!versionInfo.empty()) {
2667-
if (underlyingVersion)
2668-
entry.UnderlyingVersion = versionInfo;
2669-
else
2670-
entry.Version = versionInfo;
2671-
}
2659+
entry.Version = versionInfo;
2660+
entry.UnderlyingVersion = underlyingVersionInfo;
26722661
}
26732662

2674-
bool ASTContext::canImportModuleImpl(ImportPath::Module ModuleName,
2675-
SourceLoc loc, llvm::VersionTuple version,
2676-
bool underlyingVersion,
2677-
bool isSourceCanImport,
2678-
llvm::VersionTuple &foundVersion) const {
2663+
bool ASTContext::canImportModuleImpl(
2664+
ImportPath::Module ModuleName, SourceLoc loc, llvm::VersionTuple version,
2665+
bool underlyingVersion, bool isSourceCanImport,
2666+
llvm::VersionTuple &foundVersion,
2667+
llvm::VersionTuple &foundUnderlyingClangVersion) const {
26792668
SmallString<64> FullModuleName;
26802669
ModuleName.getString(FullModuleName);
26812670
auto ModuleNameStr = FullModuleName.str().str();
@@ -2684,6 +2673,20 @@ bool ASTContext::canImportModuleImpl(ImportPath::Module ModuleName,
26842673
if (FailedModuleImportNames.count(ModuleNameStr))
26852674
return false;
26862675

2676+
auto missingVersion = [this, &loc, &ModuleName,
2677+
&underlyingVersion]() -> bool {
2678+
// The module version could not be parsed from the preferred source for
2679+
// this query. Diagnose and return `true` to indicate that the unversioned module
2680+
// will satisfy the query.
2681+
auto mID = ModuleName[0];
2682+
auto diagLoc = mID.Loc;
2683+
if (mID.Loc.isInvalid())
2684+
diagLoc = loc;
2685+
Diags.diagnose(diagLoc, diag::cannot_find_project_version, mID.Item.str(),
2686+
underlyingVersion);
2687+
return true;
2688+
};
2689+
26872690
// If this module has already been checked or there is information for the
26882691
// module from commandline, use that information instead of loading the
26892692
// module.
@@ -2692,26 +2695,93 @@ bool ASTContext::canImportModuleImpl(ImportPath::Module ModuleName,
26922695
if (version.empty())
26932696
return true;
26942697

2695-
if (underlyingVersion)
2696-
return Found->second.UnderlyingVersion.empty()
2697-
? true
2698-
: version <= Found->second.UnderlyingVersion;
2698+
const auto &foundComparisonVersion = underlyingVersion
2699+
? Found->second.UnderlyingVersion
2700+
: Found->second.Version;
2701+
if (!foundComparisonVersion.empty())
2702+
return version <= foundComparisonVersion;
26992703
else
2700-
return Found->second.Version.empty()
2701-
? true
2702-
: version <= Found->second.Version;
2704+
return missingVersion();
27032705
}
27042706

2707+
// When looking up a module, each module importer will report back
2708+
// if it finds a module with a specified version. This routine verifies
2709+
// whether said version is valid and if it superceeds the best
2710+
// previously-discovered version of this module found.
2711+
auto validateVersion =
2712+
[](const ModuleLoader::ModuleVersionInfo &bestVersionInfo,
2713+
const ModuleLoader::ModuleVersionInfo &versionInfo,
2714+
bool underlyingVersion) {
2715+
if (!versionInfo.isValid())
2716+
return false; // The loader didn't attempt to parse a version.
2717+
2718+
if (underlyingVersion && !isClangModuleVersion(versionInfo))
2719+
return false; // We're only matching Clang module versions.
2720+
2721+
if (bestVersionInfo.isValid() &&
2722+
versionInfo.getSourceKind() <= bestVersionInfo.getSourceKind())
2723+
return false; // This module version's source is lower priority.
2724+
2725+
return true;
2726+
};
2727+
2728+
// For each module loader, attempt to discover queried module,
2729+
// along the way record the discovered module's version as well as
2730+
// the discovered module's underlying Clang module's version.
2731+
auto lookupVersionedModule =
2732+
[&](ModuleLoader::ModuleVersionInfo &bestVersionInfo,
2733+
ModuleLoader::ModuleVersionInfo &bestUnderlyingVersionInfo) -> bool {
2734+
for (auto &importer : getImpl().ModuleLoaders) {
2735+
ModuleLoader::ModuleVersionInfo versionInfo;
2736+
if (!importer->canImportModule(ModuleName, loc, &versionInfo))
2737+
continue; // The loader can't find the module.
2738+
2739+
if (validateVersion(bestVersionInfo, versionInfo,
2740+
/* underlyingVersion */ false))
2741+
bestVersionInfo = versionInfo;
2742+
if (validateVersion(bestUnderlyingVersionInfo, versionInfo,
2743+
/* underlyingVersion */ true))
2744+
bestUnderlyingVersionInfo = versionInfo;
2745+
}
2746+
2747+
if (!underlyingVersion && !bestVersionInfo.isValid())
2748+
return false;
2749+
2750+
if (underlyingVersion && !bestUnderlyingVersionInfo.isValid())
2751+
return false;
2752+
2753+
foundVersion = bestVersionInfo.getVersion();
2754+
foundUnderlyingClangVersion = bestUnderlyingVersionInfo.getVersion();
2755+
return true;
2756+
};
2757+
2758+
// For queries which do not care about any kind of module information
2759+
// such as e.g. `testImportModule`, simply return `true` as soon
2760+
// as *any* loader can find the queried module.
2761+
auto lookupModule = [&]() -> bool {
2762+
for (auto &importer : getImpl().ModuleLoaders) {
2763+
ModuleLoader::ModuleVersionInfo versionInfo;
2764+
if (!importer->canImportModule(ModuleName, loc, &versionInfo))
2765+
continue; // The loader can't find the module.
2766+
return true;
2767+
}
2768+
return false;
2769+
};
2770+
27052771
if (version.empty()) {
27062772
// If this module has already been successfully imported, it is importable.
27072773
if (getLoadedModule(ModuleName) != nullptr)
27082774
return true;
2709-
2710-
// Otherwise, ask whether any module loader can load the module.
2711-
for (auto &importer : getImpl().ModuleLoaders) {
2712-
if (importer->canImportModule(ModuleName, loc, nullptr))
2713-
return true;
2714-
}
2775+
2776+
if (!isSourceCanImport)
2777+
return lookupModule();
2778+
2779+
// Otherwise, ask whether any module loader can load the module,
2780+
// and record the module version that the succeeding loader
2781+
// observed.
2782+
ModuleLoader::ModuleVersionInfo versionInfo, underlyingVersionInfo;
2783+
if (lookupVersionedModule(versionInfo, underlyingVersionInfo))
2784+
return true;
27152785

27162786
if (isSourceCanImport)
27172787
FailedModuleImportNames.insert(ModuleNameStr);
@@ -2722,43 +2792,15 @@ bool ASTContext::canImportModuleImpl(ImportPath::Module ModuleName,
27222792
// We need to check whether the version of the module is high enough.
27232793
// Retrieve a module version from each module loader that can find the module
27242794
// and use the best source available for the query.
2725-
ModuleLoader::ModuleVersionInfo bestVersionInfo;
2726-
for (auto &importer : getImpl().ModuleLoaders) {
2727-
ModuleLoader::ModuleVersionInfo versionInfo;
2728-
2729-
if (!importer->canImportModule(ModuleName, loc, &versionInfo))
2730-
continue; // The loader can't find the module.
2731-
2732-
if (!versionInfo.isValid())
2733-
continue; // The loader didn't attempt to parse a version.
2734-
2735-
if (underlyingVersion && !isClangModuleVersion(versionInfo))
2736-
continue; // We're only matching Clang module versions.
2737-
2738-
if (bestVersionInfo.isValid() &&
2739-
versionInfo.getSourceKind() <= bestVersionInfo.getSourceKind())
2740-
continue; // This module version's source is lower priority.
2741-
2742-
bestVersionInfo = versionInfo;
2743-
}
2744-
2745-
if (!bestVersionInfo.isValid())
2795+
ModuleLoader::ModuleVersionInfo versionInfo, underlyingVersionInfo;
2796+
if (!lookupVersionedModule(versionInfo, underlyingVersionInfo))
27462797
return false;
27472798

2748-
if (bestVersionInfo.getVersion().empty()) {
2749-
// The module version could not be parsed from the preferred source for
2750-
// this query. Diagnose and treat the query as if it succeeded.
2751-
auto mID = ModuleName[0];
2752-
auto diagLoc = mID.Loc;
2753-
if (mID.Loc.isInvalid())
2754-
diagLoc = loc;
2755-
Diags.diagnose(diagLoc, diag::cannot_find_project_version,
2756-
getModuleVersionKindString(bestVersionInfo), mID.Item.str());
2757-
return true;
2758-
}
2799+
const auto &queryVersion = underlyingVersion ? underlyingVersionInfo : versionInfo;
2800+
if (queryVersion.getVersion().empty())
2801+
return missingVersion();
27592802

2760-
foundVersion = bestVersionInfo.getVersion();
2761-
return version <= bestVersionInfo.getVersion();
2803+
return version <= queryVersion.getVersion();
27622804
}
27632805

27642806
void ASTContext::forEachCanImportVersionCheck(
@@ -2773,22 +2815,26 @@ bool ASTContext::canImportModule(ImportPath::Module moduleName, SourceLoc loc,
27732815
llvm::VersionTuple version,
27742816
bool underlyingVersion) {
27752817
llvm::VersionTuple versionInfo;
2818+
llvm::VersionTuple underlyingVersionInfo;
27762819
if (!canImportModuleImpl(moduleName, loc, version, underlyingVersion, true,
2777-
versionInfo))
2820+
versionInfo, underlyingVersionInfo))
27782821
return false;
27792822

27802823
SmallString<64> fullModuleName;
27812824
moduleName.getString(fullModuleName);
2782-
addSucceededCanImportModule(fullModuleName, underlyingVersion, versionInfo);
2825+
2826+
addSucceededCanImportModule(fullModuleName, versionInfo, underlyingVersionInfo);
27832827
return true;
27842828
}
27852829

27862830
bool ASTContext::testImportModule(ImportPath::Module ModuleName,
27872831
llvm::VersionTuple version,
27882832
bool underlyingVersion) const {
27892833
llvm::VersionTuple versionInfo;
2834+
llvm::VersionTuple underlyingVersionInfo;
27902835
return canImportModuleImpl(ModuleName, SourceLoc(), version,
2791-
underlyingVersion, false, versionInfo);
2836+
underlyingVersion, false, versionInfo,
2837+
underlyingVersionInfo);
27922838
}
27932839

27942840
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: 6 additions & 4 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,7 +73,8 @@ 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+
#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}}
7678
// Bar is an unversioned Swift module with no underlying clang module.
7779
let underlyingMinorSmaller = 1 // expected-warning {{initialization of immutable value 'underlyingMinorSmaller' was never used; consider replacing with assignment to '_' or removing it}}
7880
#endif

test/Parse/ConditionalCompilation/can_import_version.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,9 @@ func canImportVersioned() {
6767
let extraComponent = 1 // expected-warning {{initialization of immutable value 'extraComponent' was never used; consider replacing with assignment to '_' or removing it}}
6868
#endif
6969

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

@@ -135,8 +136,9 @@ func canImportVersionedString() {
135136
let extraComponent = 1 // expected-warning {{initialization of immutable value 'extraComponent' was never used; consider replacing with assignment to '_' or removing it}}
136137
#endif
137138

138-
#if canImport(Foo, _underlyingVersion: "113.33")
139-
// Foo is an unversioned Swift module with no underlying clang module.
139+
#if canImport(Foo, _underlyingVersion: "113.33") // expected-warning {{cannot find user version number for Clang module 'Foo'; version number ignored}}
140+
// TODO(ParserValidation): expected-warning@-1 *{{cannot find user version number for Clang module 'Foo'; version number ignored}}
141+
// Foo is a Swift module with no underlying clang module.
140142
let underlyingMinorSmaller = 1 // expected-warning {{initialization of immutable value 'underlyingMinorSmaller' was never used; consider replacing with assignment to '_' or removing it}}
141143
#endif
142144
}

0 commit comments

Comments
 (0)