Skip to content

[ScanDependency][canImport] Improve canImport handling in explicit build #73853

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 1 commit into from
Jun 3, 2024

Conversation

cachemeifyoucan
Copy link
Contributor

Teach dependency scanner to report all the module canImport check result to swift-frontend, so swift-frontend doesn't need to parse swiftmodule or parse TBD file to determine the versions. This ensures dependency scanner and swift-frontend will have the same resolution for all canImport checks.

This also fixes two related issues:

  • Previously, in order to get consistant results between scanner and frontend, scanner will request building the module in canImport check even it is not imported later. This slightly alters the definition of the canImport to only succeed when the module can be found AND be built. This also can affect the auto-link in such cases.
  • For caching build, the location of the clang module is abstracted away so swift-frontend cannot locate the TBD file to resolve underlyingVersion.

rdar://128067152

@cachemeifyoucan
Copy link
Contributor Author

@swift-ci please smoke test

@ahoppen ahoppen removed their request for review May 23, 2024 20:05
@@ -411,7 +412,11 @@ class ASTContext final {

/// Cache of module names that passed the 'canImport' test. This cannot be
/// mutable since it needs to be queried for dependency discovery.
llvm::StringSet<> SucceededModuleImportNames;
struct ImportedModuleVersionInfo {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Doc comment should be on the map; why is it std::map instead of StringMap (or MapVector around StringMap if you need a stable order)? Does it really need to be sorted by key?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is either this needs to be sorted or the scanner that request the module version needs to sort it afterwards before converting to the build args. Since options do not get canonicalized now, the order of the version matters.

struct CanImportInfo {
std::string ModuleName;
llvm::VersionTuple Version;
llvm::VersionTuple UnderlyingVersion;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand the modeling here: it looks like you actually get a separate CanImportInfo for version vs. underlying version, so why do we store both instead of a flag?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, those are two separate versions, one for swift module and one for clang module, and they can co-exists for one module decl name because swift module can be an overlay for clang module.

Alternative is to module clang and swift separately and use different options in swift-frontend.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am guessing this is for some Swift client that may have both:

#if canImport(Foo, _version: 10.0) { ... }
#if canImport(Foo, _underlyingVersion: 10.0) { ... }

The names of these version tuples here at least, are confusing (they are confusing in the canImport directive itself, but that's a separate point). Maybe UnderlyingClangModuleVersion. Also, IIUC these tuples represent the lowest version for this module for which the versioned canImport evaluates to true, right? That's worth capturing in the comments here as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it is the version of the module discovered by scanner if version ever requested.

That said, there might be a corner case I need to fix. Looking...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe UnderlyingClangModuleVersion

Current variable actually matches the parameter in canImport :)


// If the canImport information is coming from the command-line, then no
// need to continue the search, return false.
if (!SearchPathOpts.CanImportModuleInfo.empty())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this check be outside the if (Found != SucceededModuleImportNames.end())?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Swift compiler sometimes query canImport module during setup phase (like can I import concurrency?) which doesn't gets mapped out by scanner. For the module name requested that is not in the map, it can fallback to module loading path. Will improve comments.

@cachemeifyoucan
Copy link
Contributor Author

@swift-ci please smoke test

@cachemeifyoucan
Copy link
Contributor Author

@swift-ci please smoke test

@cachemeifyoucan
Copy link
Contributor Author

ping

auto &entry = SucceededModuleImportNames[moduleName.str()];
if (!versionInfo.empty()) {
if (underlyingVersion)
entry.UnderlyingVersion = versionInfo;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if canImport() is evaluated multiple times with different version numbers? Shouldn't we only store the new version if it is higher than the previously stored version?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The version stored is the version of the module, not the version in canImport statement. The version is either requested or not requested, and cannot change during the build. And we can use that one version to check for every version requested through canImport.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. I find this naming ("Succeeded") confusing then; these are just the versions for these modules. It's just a nit, though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will rename it.

Teach dependency scanner to report all the module canImport check result
to swift-frontend, so swift-frontend doesn't need to parse swiftmodule
or parse TBD file to determine the versions. This ensures dependency
scanner and swift-frontend will have the same resolution for all
canImport checks.

This also fixes two related issues:
* Previously, in order to get consistant results between scanner and
  frontend, scanner will request building the module in canImport check
  even it is not imported later. This slightly alters the definition of
  the canImport to only succeed when the module can be found AND be
  built. This also can affect the auto-link in such cases.
* For caching build, the location of the clang module is abstracted away
  so swift-frontend cannot locate the TBD file to resolve
  underlyingVersion.

rdar://128067152
@cachemeifyoucan
Copy link
Contributor Author

@swift-ci please smoke test

@cachemeifyoucan cachemeifyoucan merged commit 0aa0687 into swiftlang:main Jun 3, 2024
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants