Skip to content

Fix some SourceKit assertion hits #28226

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

Merged
merged 4 commits into from
Nov 19, 2019
Merged

Conversation

nathawes
Copy link
Contributor

@nathawes nathawes commented Nov 13, 2019

SourceKit's purely syntactic requests don't install a type-checker and so were hitting the below assertion in ValueDecl::getInterfaceType() when they inadvertently invoked some logic that required a type-checker:

assert(ctx.getLegacyGlobalTypeChecker()
         && "The type checker must be installed to make semantic queries!");

This was happening in calls to:

  1. FuncDecl::isStatic(), which was calling getInterfaceType() unnecessarily in order to print it in a diagnostic message.
  2. ASTVerifier::verifyParsed() for initializers and deinitializers which both call isInvalid() when the init/deinit isn't contained in a type/class decl. I just removed these checks since they're already being checked in ASTVerifier::verifyChecked() as well.
  3. ValueDecl::getFormalAccess() to provide the access level in the document structure response. We could avoid this by approximating the access level syntactically, and actually used to do this before getFormalAccess was requestified, but the access level info doesn't seem like it's being used anywhere (at least not in Xcode or Swift Playgrounds) so I just removed it. This method was calling isInvalid() too (which I've replaced with what is hopefully an equivalent check), and also hit problems in inactive ifconfig clauses. It asserts that extensions are already bound and they're not in inactive ifconfig clauses (ASTScope doesn't support this at the moment). This method calls isInvalid() too (though doesn't need to) but also triggers a different assertion if extensions aren't bound, and we don't want to do that here for performance reasons (these requests run on every keypress in the editor). I've resurrected the old inferAccessLevel() logic we used to use prior to getFormalAccess().

Resolves rdar://problem/57202584

@nathawes nathawes changed the title Fix some SourceKit assertion hits [WIP] Fix some SourceKit assertion hits Nov 13, 2019
@nathawes
Copy link
Contributor Author

@swift-ci please test

@johnfairh
Copy link
Contributor

The key.accessibility has been present since the very early days and is relied on by widely-used open-source tools including at least SwiftLint and Jazzy -- it's a big problem to delete it.

Ideally keep the existing behaviour, but restoring the older syntactic algorithm would be OK --- though there was some fallout for us when this last changed.

@marcelofabri fyi

@CodaFi
Copy link
Contributor

CodaFi commented Nov 13, 2019

@johnfairh The problem is that the result of this key would not have been stable. There wasn’t a previous syntactic algorithm, there was only a quasi-semantic algorithm that used to get silently bounced out of the type checker with a null answer and whatever fallback that path gave you.

@johnfairh
Copy link
Contributor

Right; I think the 'previous syntactic' version that Nathan mentioned is the inferAccessLevel() stuff removed by c51f884, which was not always technically correct but close enough in practice.

@marcelofabri
Copy link
Contributor

16 SwiftLint rules use this, as well as Jazzy.

Even though this key would not be stable, I don't remember ever seeing any false positives caused by this. cc @jpsim

@nathawes
Copy link
Contributor Author

Ah ok, thanks @johnfairh! I'll see if @CodaFi's suggestion works because that will probably have less change in behavior and go back to the inferAccessLevel logic if I hit problems.

@CodaFi
Copy link
Contributor

CodaFi commented Nov 13, 2019

The type checker fixes in this round of changers LGTM

@nathawes
Copy link
Contributor Author

@swift-ci please test

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 8b9506ed288f15d014f610defb2c61be283343ba

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 8b9506ed288f15d014f610defb2c61be283343ba

@nathawes nathawes changed the title [WIP] Fix some SourceKit assertion hits Fix some SourceKit assertion hits Nov 14, 2019
@nathawes nathawes marked this pull request as ready for review November 14, 2019 02:31
Copy link
Contributor

@CodaFi CodaFi left a comment

Choose a reason for hiding this comment

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

Formalizing this: TypeChecker parts LGTM. Can't speak for the syntax model.

Nathan Hawes added 2 commits November 15, 2019 18:10
It was being used purely to get the name of the type context for a diagnostic
message. SourceKit's syntactic-only requests were hitting an assertion when
this diagnostic was triggered because they don't set up a type checker.
SourceKit doesn't install a typechecker for its syntactic requests so was
hitting assertions in these methods if any typechecking was triggered.
@nathawes
Copy link
Contributor Author

nathawes commented Nov 16, 2019

Ok, so after thinking about this a bit more I don't think we can keep using ValueDecl::getFormalAccess() after all. To use it we need to (and recently started to) bind extensions to avoid hitting the reasonable assertion in ExtensionDecl::getBoundNominal() that extensions have actually been bound when getFormalAccess() calls it. The commit that added that assertion (here) also modfied the Sourcekitd's syntactic requests to bind extensions to avoid tripping it. These requests are intended to be run on every keypress in the editor though, so we shouldn't be doing name lookup (which may require module loading) to keep them as fast as possible.

The newest commit stops binding extensions and restores the equivalent of the old inferAccessLevel() functions we used to use prior to the switch to getFormalAccess() but with a few fixes to match its behavior where they'd diverged. It does as much as it can syntactically, and simply doesn't report the access level in cases where it couldn't be computed without name-lookup.

@slavapestov are you ok with this? Or should I try to parameterize getFormalAccess() to have a 'syntactic-only' mode or something to share the code? (Just checking since you deduped this in the first place).

@nathawes nathawes requested a review from slavapestov November 16, 2019 02:18
@nathawes
Copy link
Contributor Author

@swift-ci please test

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - dd9e11818bed8ccc19dd41c67e2eba94f6676373

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - dd9e11818bed8ccc19dd41c67e2eba94f6676373

It looks like we recently started binding extensions to their nominals in order
to continue to compute access levels via ValueDecl::getFormalAccess() after an
assertion was added to enforce that bindExtensions had been called before
anything tried to call ExtensionDecl::getBoundNominal() - which
getFormalAccess() depends on. Sourcekitd's syntactic requests are made on every
keypress in the editor though, so we shouldn't do any name binding (which may
require module loading) to keep them as fast as possible.

This patch restores the old inferAccessLevel() functions we used prior to the
switch to ValueDecl::getFormalAccess() (plus a few fixes) that does as much as
it can syntactically, without any name binding, and simply doesn't report the
access level in cases where it couldn't be computed without name-binding.

This also fixes an assertion hit we were getting trying to bind extensions in
inactive ifconfig clauses, which ASTScope doesn't support.

Resolves rdar://problem/57202584
@nathawes
Copy link
Contributor Author

@swift-ci please test

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - e8616dcb8c656aa14b1a377def47ba6e8795020d

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - e8616dcb8c656aa14b1a377def47ba6e8795020d

if (D->getKind() == DeclKind::Destructor ||
D->getKind() == DeclKind::EnumElement) {
if (auto container = dyn_cast<NominalTypeDecl>(D->getDeclContext()))
return std::max(container->getFormalAccess(), AccessLevel::Internal);
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be calling inferAccessSyntactically() again instead of getFormalAccess()?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes! Thanks – I'll add a test case and fix.

It should have been recursing into inferAccessSyntactically() to avoid
any chance of triggering name lookup.
@nathawes
Copy link
Contributor Author

@swift-ci please test

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 11d20b8

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 11d20b8

@nathawes
Copy link
Contributor Author

Going to go ahead and merge this to let SourceKit stress tester CI job progress a little further, but if there are any further comments let me know and I'll address them in a follow-up PR.

@nathawes nathawes merged commit 7b33254 into swiftlang:master Nov 19, 2019
@nathawes nathawes deleted the assertion-fixes branch November 19, 2019 17:50
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.

I think this is fine in principle, but I'm wondering why do you need the access level at all for 'syntactic' requests?

@nathawes
Copy link
Contributor Author

Ideally we wouldn't. I'd like to remove it, and did in an earlier version of this PR but it's been in the SourceKit response since Swift was open sourced and from what others have said earlier in the comments several open source tools like SwiftLint and Jazzy currently rely on it. Longer term I think it'd be good if we could remove these syntactic requests completely in favor of using SwiftSyntax.

@jpsim
Copy link
Contributor

jpsim commented Nov 19, 2019

Those open source tools can certainly evolve and use preferred mechanisms to access that information. That migration path isn't completely clear to me, especially as SwiftSyntax grows its capabilities to match everything SourceKit's syntactic requests can provide.

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.

7 participants