-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[NFC] Rework types and terms for imports' access paths #33716
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
Conversation
Review notes: This is one of those patches that changes a ton of code, but most of the change is fairly mechanical. Here is how I suggest you review this PR:
In addition to the actual reviewers I'm adding, I'll also tag a couple people in specific comments. I'd love it if they read more than just their specific sections, but I don't necessarily need them to. |
be19bf7
to
6ef321a
Compare
lib/IDE/CodeCompletion.cpp
Outdated
// FIXME: Why are we allowing multiple module names? We don't support | ||
// submodule qualification. | ||
ImportPath::Module::Builder builder; | ||
for (auto Component : ITR->getComponentRange()) | ||
AccessPath.push_back({ Component->getNameRef().getBaseIdentifier(), | ||
Component->getLoc() }); | ||
if (auto Module = Context.getLoadedModule(AccessPath)) | ||
builder.push_back(Component->getNameRef().getBaseIdentifier(), | ||
Component->getLoc()); | ||
if (auto Module = Context.getLoadedModule(builder.get())) |
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 found the original code here confusing, so it's possible that I've misunderstood it somehow.
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.
@brentdax I think you're right. It should probably just early exit with return false
when there's more than one component 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.
I've restructured this to skip this logic unless there is exactly one component in the IdentTypeRepr.
auto LHSPath = LHS->getFullAccessPath(); | ||
auto RHSPath = RHS->getFullAccessPath(); | ||
// TODO: Probably buggy--thinks "import Foo" == "import Foo.Bar". | ||
// ImportPathBase should provide universal comparison functions to avoid this. |
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 haven't added an SR number yet in case reviewers tell me that I'm wrong about this. (I don't want to make functional changes in this PR if possible, which is why I'm not just fixing the bug right away.)
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.
Seems like a bug to me too.
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.
https://bugs.swift.org/browse/SR-13490
I'll add the bug number to the comment so we don't lose track of it.
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've added the bug number to the comment.
return false; | ||
if (Path.hasSubmodule()) | ||
if (!passModulePathElements(Path.takeParent(), ClangMod->Parent)) | ||
return false; |
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.
ImportPath::Module
enforces an invariant that it has at least one module name in it, which required restructuring here. I think I've preserved the old logic, but it's hard to tell—I'm not sure when ClangMod
can be nullptr or when Path
could previously be empty.
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.
This looks correct to me too. I think the empty Path
handling was just related to the recursive call here, since it didn't have an equivalent of the hasSubmodule
check before and would drop_back
the last path component and get the non-existent parent of the corresponding ClangMod
.
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've tweaked this a bit to flatten the control flow and enforce an invariant that a null ClangMod
should never be passed in.
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.
Thank you!
lib/IRGen/IRGenDebugInfo.cpp
Outdated
// ModuleDecl::ImportedModule inconsistently with typical use (placing the | ||
// module path in the `accessPath` field). It's not immediately clear if there | ||
// are matching inconsistencies elsewhere in IRGenDebugInfo. | ||
ModuleDecl::ImportedModule Imported = { ImportPath::Access(), D->getModule() }; |
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 think anything here reads the accessPath
field (...so why are we passing ImportedModule
s around instead of just ModuleDecl*
s?), but I could use an extra eye on this.
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.
@adrian-prantl Can you comment on this?
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.
This is used only in IRGenDebugInfoImpl::finalize() to avoid creating a debug info breadcrumb for a module import twice. If you are saying this use is inconsistent, I'd image that it likely doesn't work correctly at the moment. We probably didn't notice, since a duplicate import in the debug info won't cause any problems other than wasting space.
Can you remind me what the access path is in a typical module import example? This was never really clear to me since Swift uses a flat namespace for modules. Or is that changing now?
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.
In a scoped import (like import class Foo.Bar
), the access path is the declaration the import is scoped to (Bar
). Most imports are not scoped (like import Foo
); their access paths are empty.
If you’re trying to record what was imported so that you can fully(ish) recreate it later, you probably want to write D->getAccessPath()
instead of ImportPath::Access()
here. If you just want to record the modules and don’t care about the access paths, what’s here is fine.
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.
Okay, that makes sense! At the moment, we only record imports on a module granularity in the debug info rdar://problem/68149326, so I think having an empty access path is actually what we'd want 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.
Since you might eventually decide to start recording access paths, I think I'll just grab the right value from the ImportDecl and pass it along. Currently you'll just ignore it, but if that changes in the future you won't need to update this code.
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've force-pushed a change to construct it with the correct (ignored) access path.
# FIXME: technically misnamed; should be "ImportPathComponent" | ||
Node('AccessPathComponent', kind='Syntax', | ||
children=[ | ||
Child('Name', kind='IdentifierToken'), | ||
Child('TrailingDot', kind='PeriodToken', is_optional=True), | ||
]), | ||
|
||
# FIXME: technically misnamed; should be "ImportPath" | ||
Node('AccessPath', kind='SyntaxCollection', element='AccessPathComponent'), |
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.
Fixing this will change user-visible names in SwiftSyntax; I'm not sure that's quite worth it.
@nathawes I'd appreciate it if you could review the changes to lib/IDE and tools/SourceKit, and particularly the bits where I've left comments. |
I didn't see any breakage in downstream projects, but I don't quite believe that, so I'll start with a smoke test to confirm it. @swift-ci please smoke test |
I knew it was too good to be true. |
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.
Neat!
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 SourceKit/IDE bits look good to me. Thanks!
81b1fdf
to
eda8c15
Compare
@swift-ci test |
Build failed |
Build failed |
@swift-ci please smoke test and merge |
@swift-ci please smoke test macOS platform |
@swift-ci please smoke test macOS platform |
# Conflicts: # lib/IDE/CodeCompletion.cpp
To avoid ambiguity, ImportResolution and a few other things used the term “decl path” instead of “access path”. Switch back to the correct terminology now that the compiler is becoming more consistent about it.
This method was reimplemented three times in various source tools.
This is a reasonably common operation.
This was all deprecated in earlier commits.
eda8c15
to
6f28536
Compare
@swift-ci please smoke test and merge |
1 similar comment
@swift-ci please smoke test and merge |
Currently, the use of the term "access path" in relation to imports is inconsistent. It sometimes refers to the entire chain of dotted identifiers, sometimes just to the part scoping an import to a declaration, and occasionally refers to the part that doesn't scope an import to a declaration. This is made worse by the use of
typedef
andusing
in many places, making semantics and invariants unclear.This PR reforms the terminology surrounding the dotted chain of identifiers used in
import
statements, and introduces new, strong, better-documented types to avoid mistakes or confusion about them. Specifically:ImportPath
type) represents an undifferentiated chain of identifiers in animport
statement. By providing additional information about theImportKind
, it can be split into its constituent parts.ImportPath::Module
type) represents the part of an import path that identifies the module or submodule being imported.ImportPath::Access
type) represents the part of the import path that identifies the declaration targeted by a scoped import.Each of these types has appropriate invariants about its length enforced, and a corresponding
::Builder
type can be used to create a temporary. I have also added a constructor to split apart strings intoBuilder
types, and refactored away some code in SourceKit that used to duplicate theASTContext
method for handling dotted module names.