-
Notifications
You must be signed in to change notification settings - Fork 10.6k
[CodeCompletion] Avoid type checking all top-level decls in the primary file #27981
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
[CodeCompletion] Avoid type checking all top-level decls in the primary file #27981
Conversation
4ffb312 to
883cbd4
Compare
lib/IDE/CodeCompletion.cpp
Outdated
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.
Previously B.foo() is overwriteAccess(AccessLevel::Public)ed from diagnoseWitnessFixAccessLevel(). Since the typechecking is now in DiagnosticSuppression, diagnoseWitnessFixAccessLevel() is not called so we need to manually check it.
lib/IDE/CodeCompletion.cpp
Outdated
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.
HasInitialValueAttr is injected in the typechecking.
lib/IDE/CodeCompletion.cpp
Outdated
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.
Moved after typeCheckContextUntil() because addKeywords() may call DeclContext::getSelfProtocolDecl() which can only be called after bindExtensions().
|
@swift-ci Please smoke test |
f96a0cd to
9f9298e
Compare
|
@swift-ci Please smoke test |
|
@swift-ci Please test Linux |
|
Build failed |
|
@swift-ci Please test Linux |
9f9298e to
0d6e0c8
Compare
|
@swift-ci Please smoke test |
lib/Sema/LookupVisibleDecls.cpp
Outdated
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.
How is this triggered during normal type-checking? This seems kind of brittle to live in lookup-visible-decls, although admittedly this whole file is basically like that...
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.
If the type is declared in the primary files, they are synthesized in TypeChecker::typeCheckDecl()(DeclChecker) or in typeCheckFunctionsAndExternalDecls().
But if it's in the non-primary files, AFAIU, they are lazily synthesized on demand in DeclContext::lookupQualified()
https://github.com/apple/swift/blob/bd971507bae5a12a2b40365e795799037ec59fcc/lib/AST/NameLookup.cpp#L1573-L1579
https://github.com/apple/swift/blob/bd971507bae5a12a2b40365e795799037ec59fcc/lib/Sema/TypeChecker.h#L1018-L1024
I can move this synthesizeMemberDeclsForLookup() function into TypeChecker::, but I don't think it's worth...
0d6e0c8 to
fc2a4d6
Compare
|
Resolved conflicts. |
lib/IDE/CodeCompletion.cpp
Outdated
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 sure I understand. Can you give an example? Normally stored properties can be overridden with computed properties as long as they're not final.
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 believe this meant something like:
class C {
class var foo = 1
}But that's compile error, so we don't care much how they are treated in override completion. So maybe this check is not needed.
But here, I'm just changing D->getAttrs().hasAttribute<HasInitialValueAttr>() to isa<VarDecl>(D) && cast<VarDecl>(D)->hasInitialValue()
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.
My point was that the presence of an initial value expression does not have any bearing on whether the declaration can be overridden or not. The comment above is incorrect.
I think the only relevant check here is whether the property is final (static is equivalent to class final).
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.
Right, I just removed this check.
lib/IDE/CodeCompletion.cpp
Outdated
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 access of the requirement is the same as the access of the protocol, so I believe this entire loop and the search for the witness of 'VD' can go 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.
You mean like this?
if (Access < AccessLevel::Public &&
!isa<ProtocolDecl>(VD->getDeclContext())) {
for (auto Conformance : CurrentNominal->getAllConformances()) {
auto Proto = Conformance->getProtocol();
Access = std::max(Access, Proto->getFormalAccess());
}
}This may cause unnecessary public when the type conforms to two or more protocols. Like
public protocol P1 { }
protocol P2 {
func bar()
}
public class B {
func bar() {}
}
public class C: B, P1, P2 {
<complete>
}In this case when overriding bar(), C.bar() is not needed to be public.
lib/IDE/ExprContextAnalysis.cpp
Outdated
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.
Maybe the getStdlibModule() and bindExtensions() calls should be done as part of performParseAndNameBindingOnly()?
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.
Made bindExtensions() a part of performParseAndNameBindingOnly().
It turned out that getStdlibModule() is meaningless. Since ASTContext::getStdlibModule(bool loadIfAbsent = false) is called with default loadIfAbsent = false, it doesn't do anything. So I just removed it from performTypechecking()
lib/Sema/LookupVisibleDecls.cpp
Outdated
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.
You can replace this loop with calls to Conformance->forEachTypeWitness() and forEachValueWitness(), passing in useResolver=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.
Done.
lib/Sema/LookupVisibleDecls.cpp
Outdated
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.
You can check if the IDC itself is a member of a source file outside the loop.
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.
Thanks, done!
lib/Sema/LookupVisibleDecls.cpp
Outdated
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.
It's possible that an interface type has been set, but implicit constructors were not added (and vice versa). You can unconditinally call addImplicitConstructors() here; it's idempotent if called multiple times.
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.
IIRC, that causes circular dependency error. I will try that again.
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.
Here's the cyclic dependency error when enabling typo correction. How can we fix this?
$ cat test.swift
struct Broken {
var b = True
}
$ swift -frontend -typecheck -typo-correction-limit 10 -debug-cycles test.swift
test.swift:1:8: error: circular reference
struct Broken {
^
test.swift:2:3: note: through reference here
var b = True
^
test.swift:2:7: note: through reference here
var b = True
^
test.swift:2:7: note: through reference here
var b = True
^
===CYCLE DETECTED===
`--SynthesizeMemberwiseInitRequest(test.(file)[email protected]:1:8)
`--ParseMembersRequest(test.(file)[email protected]:1:8) -> {(unknown decl), test.(file)[email protected]:2:7}
`--StorageImplInfoRequest(test.(file)[email protected]:2:7)
| `--AttachedPropertyWrappersRequest(test.(file)[email protected]:2:7)
`--StorageImplInfoRequest(test.(file)[email protected]:2:7)
| `--AttachedPropertyWrappersRequest(test.(file)[email protected]:2:7)
`--AccessLevelRequest(test.(file)[email protected]:2:7)
`--AccessLevelRequest(test.(file)[email protected]:2:7)
`--InterfaceTypeRequest(test.(file)[email protected]:2:7)
`--NamingPatternRequest(test.(file)[email protected]:2:7)
`--PatternBindingEntryRequest((unknown decl), 0)
`--AttachedPropertyWrappersRequest(test.(file)[email protected]:2:7)
...
`--ParseMembersRequest(test.(file)[email protected]:1:8) -> {(unknown decl), test.(file)[email protected]:2:7}
`--GenericParamListRequest(test.(file)[email protected]:1:8)
`--GenericSignatureRequest(test.(file)[email protected]:1:8)
| `--GenericParamListRequest(test.(file)[email protected]:1:8)
`--GenericParamListRequest(test.(file)[email protected]:1:8)
`--InterfaceTypeRequest(test.(file)[email protected]:1:8)
| `--GenericParamListRequest(test.(file)[email protected]:1:8)
| `--GenericParamListRequest(test.(file)[email protected]:1:8)
`--HasMemberwiseInitRequest(test.(file)[email protected]:1:8) -> 1
| `--InterfaceTypeRequest(test.(file)[email protected]:1:8)
| | `--GenericParamListRequest(test.(file)[email protected]:1:8)
| | `--GenericParamListRequest(test.(file)[email protected]:1:8)
| `--HasUserDefinedDesignatedInitRequest(test.(file)[email protected]:1:8) -> 0
| | `--ParseMembersRequest(test.(file)[email protected]:1:8) -> {(unknown decl), test.(file)[email protected]:2:7}
| `--ParseMembersRequest(test.(file)[email protected]:1:8) -> {(unknown decl), test.(file)[email protected]:2:7}
| `--StorageImplInfoRequest(test.(file)[email protected]:2:7)
| | `--AttachedPropertyWrappersRequest(test.(file)[email protected]:2:7)
| `--StorageImplInfoRequest(test.(file)[email protected]:2:7)
| | `--AttachedPropertyWrappersRequest(test.(file)[email protected]:2:7)
| `--AccessLevelRequest(test.(file)[email protected]:2:7)
`--SynthesizeMemberwiseInitRequest(test.(file)[email protected]:1:8) (cyclic dependency)
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 see. It might be better then to check to explicitly check if an SynthesizeMemberwiseInitRequest is active, instead of using hasInterfaceType(), ie,
VD->getASTContext().evaluator.hasActiveRequest(
SynthesizeMemberwiseInitRequest{...})
lib/Sema/LookupVisibleDecls.cpp
Outdated
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.
Can we do this all this inside lookupDeclsFromProtocolsBeingConformedTo() instead?
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 so because this has to be done before lookupTypeMembers() which is called before lookupDeclsFromProtocolsBeingConformedTo().
lib/Sema/LookupVisibleDecls.cpp
Outdated
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 might be cleaner to visit the extension's members in doGlobalExtensionLookup()
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.
Done
024b1f6 to
113ea63
Compare
|
@swift-ci Please smoke test |
DougGregor
left a comment
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 have much to add to Slava and Ben's feedback, but I love the form of this. It's great to see us taking advantage of more laziness in the type checker.
lib/IDE/CodeCompletion.cpp
Outdated
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 still think it's better to write Conformance->getProtocol()->getFormalAccess() here.
lib/IDE/CodeCompletion.cpp
Outdated
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.
My point was that the presence of an initial value expression does not have any bearing on whether the declaration can be overridden or not. The comment above is incorrect.
I think the only relevant check here is whether the property is final (static is equivalent to class final).
lib/Sema/LookupVisibleDecls.cpp
Outdated
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 see. It might be better then to check to explicitly check if an SynthesizeMemberwiseInitRequest is active, instead of using hasInterfaceType(), ie,
VD->getASTContext().evaluator.hasActiveRequest(
SynthesizeMemberwiseInitRequest{...})
- Default memberwise initializer for struct was not suggested - Dynamic member lookup didn't work - `init(rawValue:)` was not suggested for `RawRepresentable` types - '$name' was not suggested for `@propertyWrapper`ed value rdar://problem/56391233
… file - Use `performParseAndResolveImportsOnly()` to invoke the frontend - Do `bindExtensions()` in `ide::typeCheckContextUntil()` - Typecheck preceding `TopLevelCodeDecl`s only if the compleiton is in a `TopLevelCodeDecl` - Other related tweaks rdar://problem/56636747
113ea63 to
5c2b6a5
Compare
|
@swift-ci Please smoke test |
In override completion, we didn't use to emit 'class var' with the initial expression. However, having initial expression does not have any bearing on whether the declaration is overridable or not.
5c2b6a5 to
97b16ce
Compare
|
@swift-ci Please smoke test |
The first commit is purely bug fixes. They are revealed if the second commit is enabled. However they can be reproduced if the declaration is in the other files in the current module. (Added
test/IDE/complete_multifile.swiftregression test case).init(rawValue:)was not suggested forRawRepresentabletypes (rdar://problem/56391233)@propertyWrappered valueThe second commit actually stops eager typechecking decls in the primary file.
performParseAndResolveImportsOnly()to invoke the frontendbindExtensions()inide::typeCheckContextUntil()TopLevelCodeDecls only if the compleiton is ina
TopLevelCodeDeclrdar://problem/56636747