Skip to content

AST: Make the versioned variants of #if canImport() more reliable and consistent #60981

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 39 additions & 2 deletions include/swift/AST/ModuleLoader.h
Original file line number Diff line number Diff line change
Expand Up @@ -196,14 +196,51 @@ class ModuleLoader {
virtual void collectVisibleTopLevelModuleNames(
SmallVectorImpl<Identifier> &names) const = 0;

/// The kind of source for a module's version.
///
/// NOTE: The order of the members is significant; they are declared in
/// ascending priority order.
enum class ModuleVersionSourceKind {
ClangModuleTBD,
SwiftBinaryModule,
SwiftInterface,
};

/// Represents a module version and the source it was parsed from.
class ModuleVersionInfo {
llvm::VersionTuple Version;
llvm::Optional<ModuleVersionSourceKind> SourceKind;

public:
/// Returns true if the version has a valid source kind.
bool isValid() const { return SourceKind.hasValue(); }

/// Returns the version, which may be empty if a version was not present or
/// was unparsable.
llvm::VersionTuple getVersion() const { return Version; }

/// Returns the kind of source of the module version. Do not call if
/// \c isValid() returns false.
ModuleVersionSourceKind getSourceKind() const {
return SourceKind.getValue();
}

void setVersion(llvm::VersionTuple version, ModuleVersionSourceKind kind) {
Version = version;
SourceKind = kind;
}
};

/// Check whether the module with a given name can be imported without
/// importing it.
///
/// Note that even if this check succeeds, errors may still occur if the
/// module is loaded in full.
///
/// If a non-null \p versionInfo is provided, the module version will be
/// parsed and populated.
virtual bool canImportModule(ImportPath::Module named,
llvm::VersionTuple version,
bool underlyingVersion) = 0;
ModuleVersionInfo *versionInfo) = 0;

/// Import a module with the given module path.
///
Expand Down
6 changes: 4 additions & 2 deletions include/swift/ClangImporter/ClangImporter.h
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,11 @@ class ClangImporter final : public ClangModuleLoader {
///
/// Note that even if this check succeeds, errors may still occur if the
/// module is loaded in full.
///
/// If a non-null \p versionInfo is provided, the module version will be
/// parsed and populated.
virtual bool canImportModule(ImportPath::Module named,
llvm::VersionTuple version,
bool underlyingVersion) override;
ModuleVersionInfo *versionInfo) override;

/// Import a module with the given module path.
///
Expand Down
4 changes: 2 additions & 2 deletions include/swift/Frontend/ModuleInterfaceLoader.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,8 @@ class ExplicitSwiftModuleLoader: public SerializedModuleLoaderBase {
std::unique_ptr<llvm::MemoryBuffer> *ModuleSourceInfoBuffer,
bool skipBuildingInterface, bool IsFramework) override;

bool canImportModule(ImportPath::Module named, llvm::VersionTuple version,
bool underlyingVersion) override;
bool canImportModule(ImportPath::Module named,
ModuleVersionInfo *versionInfo) override;

bool isCached(StringRef DepPath) override { return false; };

Expand Down
6 changes: 4 additions & 2 deletions include/swift/Sema/SourceLoader.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,11 @@ class SourceLoader : public ModuleLoader {
///
/// Note that even if this check succeeds, errors may still occur if the
/// module is loaded in full.
///
/// If a non-null \p versionInfo is provided, the module version will be
/// parsed and populated.
virtual bool canImportModule(ImportPath::Module named,
llvm::VersionTuple version,
bool underlyingVersion) override;
ModuleVersionInfo *versionInfo) override;

/// Import a module with the given module path.
///
Expand Down
11 changes: 7 additions & 4 deletions include/swift/Serialization/SerializedModuleLoader.h
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,11 @@ class SerializedModuleLoaderBase : public ModuleLoader {
///
/// Note that even if this check succeeds, errors may still occur if the
/// module is loaded in full.
///
/// If a non-null \p versionInfo is provided, the module version will be
/// parsed and populated.
virtual bool canImportModule(ImportPath::Module named,
llvm::VersionTuple version,
bool underlyingVersion) override;
ModuleVersionInfo *versionInfo) override;

/// Import a module with the given module path.
///
Expand Down Expand Up @@ -287,8 +289,9 @@ class MemoryBufferSerializedModuleLoader : public SerializedModuleLoaderBase {
public:
virtual ~MemoryBufferSerializedModuleLoader();

bool canImportModule(ImportPath::Module named, llvm::VersionTuple version,
bool underlyingVersion) override;
bool canImportModule(ImportPath::Module named,
ModuleVersionInfo *versionInfo) override;

ModuleDecl *
loadModule(SourceLoc importLoc,
ImportPath::Module path) override;
Expand Down
75 changes: 67 additions & 8 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2184,6 +2184,27 @@ bool ASTContext::shouldPerformTypoCorrection() {
return NumTypoCorrections <= LangOpts.TypoCorrectionLimit;
}

static bool isClangModuleVersion(const ModuleLoader::ModuleVersionInfo &info) {
switch (info.getSourceKind()) {
case ModuleLoader::ModuleVersionSourceKind::ClangModuleTBD:
return true;
case ModuleLoader::ModuleVersionSourceKind::SwiftBinaryModule:
case ModuleLoader::ModuleVersionSourceKind::SwiftInterface:
return false;
}
}

static StringRef
getModuleVersionKindString(const ModuleLoader::ModuleVersionInfo &info) {
switch (info.getSourceKind()) {
case ModuleLoader::ModuleVersionSourceKind::ClangModuleTBD:
return "Clang";
case ModuleLoader::ModuleVersionSourceKind::SwiftBinaryModule:
case ModuleLoader::ModuleVersionSourceKind::SwiftInterface:
return "Swift";
}
}

bool ASTContext::canImportModuleImpl(ImportPath::Module ModuleName,
llvm::VersionTuple version,
bool underlyingVersion,
Expand All @@ -2195,22 +2216,60 @@ bool ASTContext::canImportModuleImpl(ImportPath::Module ModuleName,
// If we've failed loading this module before, don't look for it again.
if (FailedModuleImportNames.count(ModuleNameStr))
return false;
// If no specific version, the module is importable if it has already been imported.

if (version.empty()) {
// If this module has already been successfully imported, it is importable.
if (getLoadedModule(ModuleName) != nullptr)
return true;

// Otherwise, ask whether any module loader can load the module.
for (auto &importer : getImpl().ModuleLoaders) {
if (importer->canImportModule(ModuleName, nullptr))
return true;
}

if (updateFailingList)
FailedModuleImportNames.insert(ModuleNameStr);

return false;
}
// Otherwise, ask the module loaders.

// We need to check whether the version of the module is high enough.
// Retrieve a module version from each module loader that can find the module
// and use the best source available for the query.
ModuleLoader::ModuleVersionInfo bestVersionInfo;
for (auto &importer : getImpl().ModuleLoaders) {
if (importer->canImportModule(ModuleName, version, underlyingVersion)) {
return true;
}
ModuleLoader::ModuleVersionInfo versionInfo;

if (!importer->canImportModule(ModuleName, &versionInfo))
continue; // The loader can't find the module.

if (!versionInfo.isValid())
continue; // The loader didn't attempt to parse a version.

if (underlyingVersion && !isClangModuleVersion(versionInfo))
continue; // We're only matching Clang module versions.

if (bestVersionInfo.isValid() &&
versionInfo.getSourceKind() <= bestVersionInfo.getSourceKind())
continue; // This module version's source is lower priority.

bestVersionInfo = versionInfo;
}
if (updateFailingList && version.empty()) {
FailedModuleImportNames.insert(ModuleNameStr);

if (!bestVersionInfo.isValid())
return false;

if (bestVersionInfo.getVersion().empty()) {
// The module version could not be parsed from the preferred source for
// this query. Diagnose and treat the query as if it succeeded.
auto mID = ModuleName[0];
Diags.diagnose(mID.Loc, diag::cannot_find_project_version,
getModuleVersionKindString(bestVersionInfo), mID.Item.str());
return true;
}
return false;

return version <= bestVersionInfo.getVersion();
}

bool ASTContext::canImportModule(ImportPath::Module ModuleName,
Expand Down
21 changes: 7 additions & 14 deletions lib/ClangImporter/ClangImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1798,8 +1798,7 @@ static std::string getScalaNodeText(llvm::yaml::Node *N) {
}

bool ClangImporter::canImportModule(ImportPath::Module modulePath,
llvm::VersionTuple version,
bool underlyingVersion) {
ModuleVersionInfo *versionInfo) {
// Look up the top-level module to see if it exists.
auto &clangHeaderSearch = Impl.getClangPreprocessor().getHeaderSearchInfo();
auto topModule = modulePath.front();
Expand Down Expand Up @@ -1842,10 +1841,10 @@ bool ClangImporter::canImportModule(ImportPath::Module modulePath,
}
}

if (version.empty())
if (!versionInfo)
return true;

assert(available);
assert(!version.empty());
llvm::VersionTuple currentVersion;
StringRef path = getClangASTContext().getSourceManager()
.getFilename(clangModule->DefinitionLoc);
Expand Down Expand Up @@ -1888,16 +1887,10 @@ bool ClangImporter::canImportModule(ImportPath::Module modulePath,
}
break;
}
// Diagnose unable to checking the current version.
if (currentVersion.empty()) {
Impl.diagnose(topModule.Loc, diag::cannot_find_project_version, "Clang",
topModule.Item.str());
return true;
}
assert(!currentVersion.empty());
// Give a green light if the version on disk is greater or equal to the version
// specified in the canImport condition.
return currentVersion >= version;

versionInfo->setVersion(currentVersion,
ModuleVersionSourceKind::ClangModuleTBD);
return true;
}

ModuleDecl *ClangImporter::Implementation::loadModuleClang(
Expand Down
5 changes: 2 additions & 3 deletions lib/Frontend/ModuleInterfaceLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1947,9 +1947,8 @@ std::error_code ExplicitSwiftModuleLoader::findModuleFilesInDirectory(
return std::make_error_code(std::errc::not_supported);
}

bool ExplicitSwiftModuleLoader::canImportModule(ImportPath::Module path,
llvm::VersionTuple version,
bool underlyingVersion) {
bool ExplicitSwiftModuleLoader::canImportModule(
ImportPath::Module path, ModuleVersionInfo *versionInfo) {
// FIXME: Swift submodules?
if (path.hasSubmodule())
return false;
Expand Down
4 changes: 2 additions & 2 deletions lib/Sema/SourceLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,7 @@ void SourceLoader::collectVisibleTopLevelModuleNames(
}

bool SourceLoader::canImportModule(ImportPath::Module path,
llvm::VersionTuple version,
bool underlyingVersion) {
ModuleVersionInfo *versionInfo) {
// FIXME: Swift submodules?
if (path.hasSubmodule())
return false;
Expand All @@ -90,6 +89,7 @@ bool SourceLoader::canImportModule(ImportPath::Module path,

return false;
}

return true;
}

Expand Down
2 changes: 1 addition & 1 deletion lib/Serialization/ModuleDependencyScanner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ Optional<ModuleDependencies> SerializedModuleLoaderBase::getModuleDependencies(
assert(isa<PlaceholderSwiftModuleScanner>(scanners[0].get()) &&
"Expected PlaceholderSwiftModuleScanner as the first dependency scanner loader.");
for (auto &scanner : scanners) {
if (scanner->canImportModule(modulePath, llvm::VersionTuple(), false)) {
if (scanner->canImportModule(modulePath, nullptr)) {
// Record the dependencies.
cache.recordDependencies(moduleName, *(scanner->dependencies));
return std::move(scanner->dependencies);
Expand Down
Loading