-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Kill validateDeclForNameLookup Harder #27172
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
Kill validateDeclForNameLookup Harder #27172
Conversation
The canary in the coal mine: @swift-ci please smoke test |
I have high hopes for this patch. If it actually works then we can merge the lazy generic signatures patch minus a ton of the hacks. |
Oh joy, I need LLDB patches. |
lib/Sema/TypeCheckGeneric.cpp
Outdated
// to compute the structural type. This also prevents us from writing | ||
// ErrorTypes into otherwise valid ASTs with generated typealiases. | ||
if (typeAlias->hasInterfaceType()) { | ||
return typeAlias->getInterfaceType()->getMetatypeInstanceType(); |
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 should use getDeclaredInterfaceType()
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.
Good catch
lib/AST/GenericSignatureBuilder.cpp
Outdated
return resolved; | ||
|
||
if (typealias->hasInterfaceType()) | ||
return typealias->getDeclaredInterfaceType(); | ||
return typealias->getStructuralType(); |
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.
Doesn't getStructuralType() subsume the above 'if' now?
if (auto type = typealias->getUnderlyingTypeLoc().getType()) { | ||
if (type->isAnyObject()) | ||
anyObject = true; | ||
if (typealias->hasInterfaceType()) { |
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 hasInterfaceType() check should not be necessary?
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.
Requesting the underlying type potentially calls back into validation. This is also justified because the interface type separation changes mean that AnyObject
declarations will always have their interface type set by the time we see them.
: extendedNominal->getDeclaredType(); | ||
// Nested Hack to break cycles if this is called before validation has | ||
// finished. | ||
if (aliasDecl->hasInterfaceType()) { |
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.
Instead of checking for hasInterfaceType(), validate the alias to make this deterministic
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 here because we're validating the alias when this is false...
dyn_cast<TypeAliasDecl>(unboundGeneric->getAnyGeneric())) { | ||
if (ugAliasDecl == aliasDecl) { | ||
if (resolution.getStage() == TypeResolutionStage::Structural && | ||
!aliasDecl->hasInterfaceType()) { |
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.
Shouldn't need this check
dyn_cast<TypeAliasType>(extendedType.getPointer())) { | ||
if (aliasType->getDecl() == aliasDecl) { | ||
if (resolution.getStage() == TypeResolutionStage::Structural && | ||
!aliasDecl->hasInterfaceType()) { |
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.
Ditto
// Otherwise, simply return the underlying type. | ||
// Otherwise, return the appropriate type. | ||
if (resolution.getStage() == TypeResolutionStage::Structural && | ||
!aliasDecl->hasInterfaceType()) { |
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.
Ditto
selfType = resolution.mapTypeIntoContext( | ||
foundDC->getDeclaredInterfaceType()); | ||
selfType = | ||
resolution.mapTypeIntoContext(foundDC->getDeclaredInterfaceType()); |
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.
There's a lot of re-formatting here which makes the patch harder to review. Is it really necessary?
!isa<NominalTypeDecl>(typeDecl)) { | ||
if ((!options.is(TypeResolverContext::ExtensionBinding) || | ||
!isa<NominalTypeDecl>(typeDecl)) && | ||
!prevalidatingAlias(typeDecl, resolution)) { | ||
// Validate the declaration. | ||
if (lazyResolver) | ||
lazyResolver->resolveDeclSignature(typeDecl); |
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 could try removing this altogether, and validating the decl only once you know you need the interface type.
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.
There's some deep cycles at work that break the standard library if this is removed.
Once getGenericSignature() is a request, we can request-ify getDeclaredInterfaceType() on a type alias decl. |
apple/swift-lldb#1986 |
Apparently, |
3a52ab0
to
82b6101
Compare
apple/swift-lldb#1986 |
82b6101
to
f87c978
Compare
apple/swift-lldb#1986 |
1 similar comment
apple/swift-lldb#1986 |
@CodaFi I see there's a few diagnostic checks in validateType(). They should be pushed down to resolveType() so that we emit the same diagnostics everywhere; validateType() should just be a transitional utility method that writes the Type back into a TypeLoc. But you don't have to fix that now. Let's look at those cycles involving validateDecl() next -- we'll have to resolve them once getInterfaceType() becomes a request, because now cycles will produce diagnostics instead of silently bailing out of validation. |
@swift-ci please test |
Build failed |
apple/swift-lldb#1986 |
apple/swift-lldb#1986 |
Sent the wrong CI command here. Gonna try again when macOS fails. |
apple/swift-lldb#1986 |
apple/swift-lldb#1986 |
c2e4bbb
to
f87c978
Compare
Define a request for computing the interface type of the underlying type of a typealias. This can be used in place of the declared interface type of the alias itself when it is nested to preserve the fragile validation ordering issues that can cause.
Structural type computation ought to actually return a typealias type here using the same mechanism we compute the interface type with (minus the metatype).
Dump the underlying type by desugaring ourselves a little.
Computing the interface type of a typealias used to push validation forward and recompute the interface type on the fly. This was fragile and inconsistent with the way interface types are computed in the rest of the decls. Separate these two notions, and plumb through explicit interface type computations with the same "computeType" idiom. This will better allow us to identify the places where we have to force an interface type computation. Also remove access to the underlying type loc. It's now just a cache location the underlying type request will use. Push a type repr accessor to the places that need it, and push the underlying type accessor for everywhere else. Getting the structural type is still preferred for pre-validated computations. This required the resetting of a number of places where we were - in many cases tacitly - asking the question "does the interface type exist". This enables the removal of validateDeclForNameLookup
Flush it and the early validation hack now that we can delay computing the underlying interface type on demand and have taught type resolution to honor the structural type of a typealias. This changes the way requirement signatures are spelled as a side effect.
This avoids making the structural type dependent on whether the interface type has been computed and neatly avoids special-casing non-parsed declarations which set their underlying type up front.
f87c978
to
c254f4d
Compare
Use the interface type of the underlying type in the GSB in as many cases as possible except for one important one: requirement signature computation. There, use the structural type so we don't wind up recursively validating the requirements of an incomplete protocol.
Canary apple/swift-lldb#1986 |
apple/swift-lldb#1986 |
apple/swift-lldb#1986 |
Build failed |
Build failed |
Ugh, the same set of offenders as last time... |
Not having the generic signature is the real culprit here.
apple/swift-lldb#1986 |
apple/swift-lldb#1986 |
apple/swift-lldb#1986 |
apple/swift-lldb#1986 |
⛵️ |
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.
@@ -616,9 +616,9 @@ namespace { | |||
void visitTypeAliasDecl(TypeAliasDecl *TAD) { | |||
printCommon(TAD, "typealias"); | |||
PrintWithColorRAII(OS, TypeColor) << " type='"; | |||
if (TAD->getUnderlyingTypeLoc().getType()) { | |||
if (auto underlying = TAD->getUnderlyingType()) { |
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 kicks off a request :(
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 going to add an RAII type to catch all of these.
return resolved; | ||
|
||
return typealias->getStructuralType(); | ||
// When we're computing requirement signatures, the structural type |
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 looks like the indentation here might be a bit funky
// requirements. | ||
auto *proto = dyn_cast_or_null<ProtocolDecl>(typealias->getDeclContext()->getAsDecl()); | ||
if (proto && proto->isComputingRequirementSignature()) | ||
return typealias->getStructuralType(); |
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 too
validateTypealiasType(*this, typeAlias); | ||
|
||
// Finally, set the interface type. | ||
if (!typeAlias->hasInterfaceType()) |
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.
Is it ever the case that hasInterfaceType() returns true here? If you copy and pasted this from the old code it's just an artifact of validateDecl() being called after validateDeclForNameLookup()
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 was true when the code was written - something something cycles with witness matching.
@@ -556,7 +556,7 @@ static Type formExtensionInterfaceType( | |||
} | |||
|
|||
// If we have a typealias, try to form type sugar. | |||
if (typealias && TypeChecker::isPassThroughTypealias(typealias)) { | |||
if (typealias && TypeChecker::isPassThroughTypealias(typealias, typealias->getUnderlyingType(), nominal)) { |
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 line is too long
|
||
auto underlying = MF.getType(underlyingTypeID); | ||
alias->setUnderlyingType(underlying); | ||
SubstitutionMap subs; |
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.
Is this not alias->computeType()
?
- Clean up some errant formatting mistakes. - Collapse some code that was duplicating computing the interface type.
Using the techniques from @xymus' patch, try to kill validateDeclForNameLookup harder. There are some changes from the exact code in #24957 that, unfortunately, I cannot get to pass the tests to land independently. Namely, this patch
TypeAliasType
. I suspect this is what is causing the changes in requirement signatures in the few tests that are touched here.All of this should finally let us remove
validateDeclForNameLookup
. I have great fears that this will fail the compatibility suite. But there's only one way to find out!