-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Add -testable-import-module frontend flag (+ refactoring) #33935
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
Add -testable-import-module frontend flag (+ refactoring) #33935
Conversation
13e5e82
to
483920d
Compare
958a1f0
to
0e8007c
Compare
0e8007c
to
3a60f09
Compare
include/swift/AST/Import.h
Outdated
using ImportOptionsDMI = DenseMapInfo<swift::ImportOptions>; | ||
using StringRefDMI = DenseMapInfo<StringRef>; | ||
// FIXME: SPI groups not used by DenseMapInfo??? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was very surprised by this. @xymus, is this intentional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not familiar with the DenseMapInfo logic and the doc is sparse so I can't say this was intentional. However SPI groups on an import should affect type checking at the source file level only. Does it apply here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only use of DenseMapInfo
with AttributedImport
keys that I can find is during cross-importing (in the form of a SmallSetVector
). I think it could mean that, if you wrote something like:
@_spi(TreasureMaps) import MapKit
@_spi(IlluminatiBases) import MapKit
import SwiftUI
We would only consider the @_spi(TreasureMaps) import MapKit
, ignoring the second import.
Come to think of it, though, I don't think we've actually defined how cross-import overlays should interact with @_spi
. I'll look into that on Monday—might ping you for input at that time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll include the SPI groups in isEqual()
, but not in the hash value.
@swift-ci please test |
} | ||
}; | ||
|
||
// MARK: - Abstractions of imports | ||
|
||
/// Convenience struct to keep track of an import path and whether or not it | ||
/// is scoped. | ||
class UnloadedImportedModule { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you pinged me about naming :P, UnloadedImportedModule
to me conveys a sense of "the module was imported and loaded then unloaded", i.e. unloading as something that undoes loading. However, IIUC that's not how this is being used. Rather the semantics are more like NotYetImportedModule
(strawman alternative). Is that right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suspect part of the issue (for my lack of understanding) is that there are several moving parts here -- there's UnboundImport
as well as UnloadedImportedModule
(which kinda' sound similar-ish) -- and I only have passing familiarity with this code, so I don't fully grasp the distinction between loading modules and binding imports.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Think of it this way. We have these three public types which are used throughout the compiler:
-
ImportedModule
is basically just aModuleDecl *
and an access path. It's the minimum information you need to, for instance, figure out if a particular declaration is visible through that import, and it's also more or less what's still known about an import after deserialization. -
UnloadedImportedModule
is basically the name of a module and an access path. You use it in place ofImportedModule
when you haven't actually loaded aModuleDecl *
yet. -
AttributedImport<ImportedModule or UnloadedImportedModule>
adds to that type abstract information about the attributes of an import (like whether it's exported, implementation-only, which SPIs it imports, etc.).
And we have one other type that's private to ImportResolution.cpp:
UnboundImport
contains state and logic used to process a single import. (Could be anImportDecl
, a cross-import overlay we need to import, or now an implicit import.) In terms of state, it includes anAttributedImport<UnloadedImportedModule>
plus a couple extra things that are used when emitting diagnostics. But its primary purpose is to perform certain steps of import resolution on a particular import, not to store state. (Arguably it should actually beUnresolvedImport
; when I wrote this class, "import resolution" was called "name binding".)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't mind the name UnloadedImportedModule
, though perhaps another suggestion could be ImportedModuleToLoad
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does ImportedModule
always represent the state where a ModuleDecl *
has been loaded? Or does it sometimes have a ModuleDecl *
that hasn't been loaded?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ModuleDecls are, by definition, already loaded.
Build failed |
Build failed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice cleanups!
2dd993e
to
6aedab2
Compare
To help consolidate our various types describing imports, this commit moves the following types and methods to Import.h: * ImplicitImports * ImplicitStdlibKind * ImplicitImportInfo * ModuleDecl::ImportedModule * ModuleDecl::OrderImportedModules (as ImportedModule::Order) * ModuleDecl::removeDuplicateImports() (as ImportedModule::removeDuplicates()) * SourceFile::ImportFlags * SourceFile::ImportOptions * SourceFile::ImportedModuleDesc This commit is large and intentionally kept mechanical—nothing interesting to see here.
Rename ImportedModuleDesc to AttributedImport and make it a template that’s parameterized on the representation of the module. This will allow us to reduce duplicated representations of “abstract” ImportDecls.
Also renames a member in ImportResolution.cpp to align with this naming.
Removes what amount to redundant definitions from UnboundImport.
This doesn’t really change the design yet.
Instead, we will use AttributedImport<ImportedModule>, a common currency type which supports a superset of ImplicitImport’s current behavior.
Replace with an array of AttributedImport<UnloadedImportedModule> to add flexibility.
Allows it to be used in PointerIntPair and PointerUnion.
Unloaded implicit imports (e.g. the `-import-module` flag) will now be processed by import resolution, and will be fully validated and cross-imported. Preloaded imports, like the standard library import, are still not run through full import resolution, but this is a definite improvement over the status quo. This also folds `-import-underlying-module` and `@_exported import <ParentModule>` into a single code path, slightly changing the diagnostic for a failed overlay-style underlying module import.
This is just like -import-module, but it does an @testable import. Simple, right? Fixes rdar://66544654.
This ought to result in more consistent behavior.
6aedab2
to
944ad88
Compare
@swift-ci please smoke test |
Of course, I’ll need to update this. |
With swiftlang/llvm-project#1937 @swift-ci please smoke test |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great!
…atience [lldb] Update to match swiftlang/swift#33935
Previously, when importing a Swift module it the REPL or a Playground, LLDB would only cache the module name and reload the module at every expression evaluation. Doing so means that, when LLDB transfers imports information from the original source file to the new one, it is doing it with low fidelity, because LLDB would not keep track of the import attributes used in the previous expression evaluations. Thanks to swiftlang/swift#33935, we can now cache the whole `swift::AttributedImport<swift::ImportedModule>>` instead of only storing the module name. This struct contains both the `ImportedModule` with the `ModuleDecl` but also the `ImportOptions` set containing all the possible import attributes (Exported / Testable / PrivateImport ...) of that import statement. Now, before compiling and evaluating a Swift expression, LLDB fetches all the attributed imports cached previously and add them to the `ImplicitImportInfo` object that is used to create the `ModuleDecl` of the evaluated expression. It allows for the `ImportOptions` to be propagated in the compiled expression. This should fix some irregularities between the debugger and the compiler regarding import statements. rdar://65319954 Signed-off-by: Med Ismail Bennani <[email protected]>
Previously, when importing a Swift module it the REPL or a Playground, LLDB would only cache the module name and reload the module at every expression evaluation. Doing so means that, when LLDB transfers imports information from the original source file to the new one, it is doing it with low fidelity, because LLDB would not keep track of the import attributes used in the previous expression evaluations. Thanks to swiftlang/swift#33935, we can now cache the whole `swift::AttributedImport<swift::ImportedModule>>` instead of only storing the module name. This struct contains both the `ImportedModule` with the `ModuleDecl` but also the `ImportOptions` set containing all the possible import attributes (Exported / Testable / PrivateImport ...) of that import statement. Now, before compiling and evaluating a Swift expression, LLDB fetches all the attributed imports cached previously and add them to the `ImplicitImportInfo` object that is used to create the `ModuleDecl` of the evaluated expression. It allows for the `ImportOptions` to be propagated in the compiled expression. This should fix some irregularities between the debugger and the compiler regarding import statements. rdar://65319954 Signed-off-by: Med Ismail Bennani <[email protected]>
This PR has two functional changes:
-import-module
flag are now processed by import resolution. This means they are fully validated and cross-imported. The-import-underlying-module
flag is now also processed via this route, and it gets the same benefits (for what they're worth).-testable-import-module
frontend flag to match-import-module
. As you might guess, this adds an implicit@testable import
to every file, instead of an implicit ordinaryimport
.Those two functional changes are in the last two commits; they represent maybe 5% of the changes here. The other 95% of the PR enables these changes by refactoring the data types used to represent imports:
Fixes rdar://66544654.
Review suggestions: Probably best reviewed by commit. A lot of commits (especially the first one) are very mechanical, with lots of boring renaming at use sites. You can probably skim those bits. The commit messages may give some guidance on this.
The reason for a given change is not always clear until several commits later. In particular:
ModuleDecl::ImportedModule
andSourceFile::ImportedModuleDesc
because types currently in these two headers will become interdependent by the time we're finished.AttributedImport
a template because we will eventually use both the existingImportedModule
and a new type with a module name and access path instead. The template lets us have three types instead of four or five.ImplicitImportList
type which just wrapsArrayRef<ImplicitImport>
because (a) shifting toAttributedImport
will complicate its definition enough to require a type, and (b) introducing that type at the outset avoids multiple rounds of renaming several use sites.AttributedImport
in implicit imports to (a) allow the frontend to specify additional behaviors, and (b) make it easier to shift responsibility for loading implicit imports intoImportResolver
.ImportResolver
resolve implicit imports because (a) this makes it validate the implicit@testable
attribute added by-testable-import-module
, and (b) it simplifies the code and is the right way to do things anyway.@hamishknight, you worked a lot on import resolution recently and might have some opinions about what I'm doing. @CodaFi, you're most likely familiar with Hamish's work, but unlike him you're currently working on the compiler full-time. @varungandhi-apple, you clearly have opinions about names in this area.