Skip to content

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

Conversation

CodaFi
Copy link
Contributor

@CodaFi CodaFi commented Sep 13, 2019

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

  • Defines a new UnderlyingTypeRequest for computing the interface type of the underlying type if we have it.
  • Changes the structural type request to resugar with a TypeAliasType. I suspect this is what is causing the changes in requirement signatures in the few tests that are touched here.
  • Separate computing the interface type of a TypeAliasDecl from computing its underlying type. Not doing this makes reasoning about the laziness in UnderlyingTypeRequest interacting with the interface type of the typealias extremely difficult.

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!

@CodaFi CodaFi requested review from slavapestov and xymus September 13, 2019 18:19
@CodaFi
Copy link
Contributor Author

CodaFi commented Sep 13, 2019

The canary in the coal mine:

@swift-ci please smoke test

@CodaFi
Copy link
Contributor Author

CodaFi commented Sep 13, 2019

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.

@CodaFi
Copy link
Contributor Author

CodaFi commented Sep 13, 2019

Oh joy, I need LLDB patches.

// 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();
Copy link
Contributor

Choose a reason for hiding this comment

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

This should use getDeclaredInterfaceType()

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch

return resolved;

if (typealias->hasInterfaceType())
return typealias->getDeclaredInterfaceType();
return typealias->getStructuralType();
Copy link
Contributor

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()) {
Copy link
Contributor

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?

Copy link
Contributor Author

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()) {
Copy link
Contributor

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

Copy link
Contributor Author

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()) {
Copy link
Contributor

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()) {
Copy link
Contributor

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()) {
Copy link
Contributor

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());
Copy link
Contributor

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);
Copy link
Contributor

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.

Copy link
Contributor Author

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.

@slavapestov
Copy link
Contributor

Once getGenericSignature() is a request, we can request-ify getDeclaredInterfaceType() on a type alias decl.

CodaFi added a commit to CodaFi/swift-lldb that referenced this pull request Sep 13, 2019
@CodaFi
Copy link
Contributor Author

CodaFi commented Sep 13, 2019

apple/swift-lldb#1986
@swift-ci Please smoke test

@CodaFi
Copy link
Contributor Author

CodaFi commented Sep 13, 2019

Looks like changing the requirement signature has changed the way the exportability checker behaves. Looking into this.

Apparently, validateType is needed for UnderlyingTypeRequest to emit the right diagnostics.

@CodaFi CodaFi force-pushed the aliasing-artifacts-and-noise-reduction-techniques branch from 3a52ab0 to 82b6101 Compare September 13, 2019 23:06
@CodaFi
Copy link
Contributor Author

CodaFi commented Sep 13, 2019

apple/swift-lldb#1986
@swift-ci Please smoke test

@CodaFi CodaFi force-pushed the aliasing-artifacts-and-noise-reduction-techniques branch from 82b6101 to f87c978 Compare September 14, 2019 01:03
@CodaFi
Copy link
Contributor Author

CodaFi commented Sep 14, 2019

apple/swift-lldb#1986
@swift-ci Please smoke test

1 similar comment
@CodaFi
Copy link
Contributor Author

CodaFi commented Sep 14, 2019

apple/swift-lldb#1986
@swift-ci Please smoke test

@slavapestov
Copy link
Contributor

@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.

@CodaFi
Copy link
Contributor Author

CodaFi commented Sep 14, 2019

@swift-ci please test

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 3f9738bbf13afb06aaf18d3137dfe511f45867dd

@CodaFi
Copy link
Contributor Author

CodaFi commented Sep 14, 2019

apple/swift-lldb#1986
@swift-ci Please test

@CodaFi
Copy link
Contributor Author

CodaFi commented Sep 14, 2019

apple/swift-lldb#1986
@swift-ci Please test source compatibility

@CodaFi
Copy link
Contributor Author

CodaFi commented Sep 14, 2019

Sent the wrong CI command here. Gonna try again when macOS fails.

@swiftlang swiftlang deleted a comment from swift-ci Sep 14, 2019
@swiftlang swiftlang deleted a comment from swift-ci Sep 14, 2019
CodaFi added a commit to CodaFi/swift-lldb that referenced this pull request Sep 14, 2019
@swiftlang swiftlang deleted a comment from swift-ci Sep 14, 2019
@swiftlang swiftlang deleted a comment from swift-ci Sep 14, 2019
@CodaFi
Copy link
Contributor Author

CodaFi commented Sep 14, 2019

apple/swift-lldb#1986
@swift-ci Please test

@CodaFi
Copy link
Contributor Author

CodaFi commented Sep 14, 2019

apple/swift-lldb#1986
@swift-ci Please test source compatibility

@CodaFi CodaFi force-pushed the aliasing-artifacts-and-noise-reduction-techniques branch from c2e4bbb to f87c978 Compare September 16, 2019 18:38
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.
@CodaFi CodaFi force-pushed the aliasing-artifacts-and-noise-reduction-techniques branch from f87c978 to c254f4d Compare September 17, 2019 15:49
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.
@CodaFi
Copy link
Contributor Author

CodaFi commented Sep 18, 2019

Canary

apple/swift-lldb#1986
@swift-ci Please smoke test

@CodaFi
Copy link
Contributor Author

CodaFi commented Sep 18, 2019

apple/swift-lldb#1986
@swift-ci Please test

@CodaFi
Copy link
Contributor Author

CodaFi commented Sep 18, 2019

apple/swift-lldb#1986
@swift-ci Please test source compatibility

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - f87c9781ac1edc1933ca877e06c19b5ede99fb70

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - f87c9781ac1edc1933ca877e06c19b5ede99fb70

@CodaFi
Copy link
Contributor Author

CodaFi commented Sep 18, 2019

Ugh, the same set of offenders as last time...

Not having the generic signature is the real culprit here.
@CodaFi
Copy link
Contributor Author

CodaFi commented Sep 18, 2019

apple/swift-lldb#1986
@swift-ci Please test

@CodaFi
Copy link
Contributor Author

CodaFi commented Sep 18, 2019

apple/swift-lldb#1986
@swift-ci Please test source compatibility

@swiftlang swiftlang deleted a comment from swift-ci Sep 18, 2019
@swiftlang swiftlang deleted a comment from swift-ci Sep 18, 2019
CodaFi added a commit to CodaFi/swift-lldb that referenced this pull request Sep 18, 2019
@CodaFi
Copy link
Contributor Author

CodaFi commented Sep 18, 2019

apple/swift-lldb#1986
@swift-ci Please test Linux platform

@CodaFi
Copy link
Contributor Author

CodaFi commented Sep 18, 2019

apple/swift-lldb#1986
@swift-ci Please test macOS platform

@CodaFi
Copy link
Contributor Author

CodaFi commented Sep 18, 2019

⛵️

@CodaFi CodaFi merged commit 38bde33 into swiftlang:master Sep 18, 2019
@CodaFi CodaFi deleted the aliasing-artifacts-and-noise-reduction-techniques branch September 18, 2019 22:00
Copy link
Contributor

@slavapestov slavapestov left a comment

Choose a reason for hiding this comment

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

Nice work @xymus and @CodaFi! Really happy to see this land.

@@ -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()) {
Copy link
Contributor

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 :(

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'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
Copy link
Contributor

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();
Copy link
Contributor

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())
Copy link
Contributor

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()

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 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)) {
Copy link
Contributor

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;
Copy link
Contributor

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()?

CodaFi added a commit to CodaFi/swift that referenced this pull request Sep 18, 2019
- Clean up some errant formatting mistakes.
- Collapse some code that was duplicating computing the interface type.
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.

3 participants