Skip to content

Commit

Permalink
[CodeCompletion] Remove parser hacks regarding type body fingerprints
Browse files Browse the repository at this point in the history
Take type body fingerprints into account for inteface hash checking.
Since `SourceFile.getInterfacehash()` doesn't digest the type body
fingerprints in the file, enabling type body fingerprints regressed
fast-completion.

rdar://problem/69890297
  • Loading branch information
rintaro committed Oct 10, 2020
1 parent a665ba6 commit df94c4f
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 11 deletions.
53 changes: 51 additions & 2 deletions lib/IDE/CompletionInstance.cpp
Expand Up @@ -276,6 +276,51 @@ static bool areAnyDependentFilesInvalidated(
});
}

/// Get interface hash of \p SF including the type members in the file.
///
/// See if the inteface of the function and types visible from a function body
/// has changed since the last completion. If they haven't changed, completion
/// can reuse the existing AST of the source file. \c SF->getInterfaceHash() is
/// not enough because it doesn't take the interface of the type members into
/// account. For example:
///
/// struct S {
/// func foo() {}
/// }
/// func main(val: S) {
/// val.<HERE>
/// }
///
/// In this case, we need to ensure that the interface of \c S hasn't changed.
/// Note that we don't care about local types (i.e. type declarations inside
/// function bodies, closures, or top level statement bodies) because they are
/// not visible from other functions where the completion is happening.
void getInterfaceHashIncludingTypeMembers(SourceFile *SF,
llvm::SmallString<32> &str) {
/// FIXME: Gross. Hashing multiple "hash" values.
llvm::MD5 hash;
SF->getInterfaceHash(str);
hash.update(str);

std::function<void(IterableDeclContext *)> hashTypeBodyFingerprints =
[&](IterableDeclContext *IDC) {
if (auto fp = IDC->getBodyFingerprint())
hash.update(*fp);
for (auto *member : IDC->getParsedMembers())
if (auto *childIDC = dyn_cast<IterableDeclContext>(member))
hashTypeBodyFingerprints(childIDC);
};

for (auto *D : SF->getTopLevelDecls()) {
if (auto IDC = dyn_cast<IterableDeclContext>(D))
hashTypeBodyFingerprints(IDC);
}

llvm::MD5::MD5Result result;
hash.final(result);
str = result.digest();
}

} // namespace

bool CompletionInstance::performCachedOperationIfPossible(
Expand Down Expand Up @@ -355,8 +400,8 @@ bool CompletionInstance::performCachedOperationIfPossible(
// If the interface has changed, AST must be refreshed.
llvm::SmallString<32> oldInterfaceHash{};
llvm::SmallString<32> newInterfaceHash{};
oldSF->getInterfaceHash(oldInterfaceHash);
tmpSF->getInterfaceHash(newInterfaceHash);
getInterfaceHashIncludingTypeMembers(oldSF, oldInterfaceHash);
getInterfaceHashIncludingTypeMembers(tmpSF, newInterfaceHash);
if (oldInterfaceHash != newInterfaceHash)
return false;

Expand Down Expand Up @@ -406,6 +451,10 @@ bool CompletionInstance::performCachedOperationIfPossible(
Scope Top(SI, ScopeKind::TopLevel);
Scope Body(SI, ScopeKind::FunctionBody);

assert(oldInfo.Kind == CodeCompletionDelayedDeclKind::FunctionBody &&
"If the interface hash is the same as old one, the previous kind "
"must be FunctionBody too. Otherwise, hashing is too weak");
oldInfo.Kind = CodeCompletionDelayedDeclKind::FunctionBody;
oldInfo.ParentContext = DC;
oldInfo.StartOffset = newInfo.StartOffset;
oldInfo.EndOffset = newInfo.EndOffset;
Expand Down
10 changes: 1 addition & 9 deletions lib/Parse/ParseDecl.cpp
Expand Up @@ -4672,13 +4672,8 @@ Parser::parseDeclList(SourceLoc LBLoc, SourceLoc &RBLoc, Diag<> ErrorDiag,

// If we're hashing the type body separately, record the curly braces but
// nothing inside for the interface hash.
//
// FIXME: There's no real reason code completion cannot also use this code
// path. But it seems to cause lazy parsing in contexts that the current
// implementation does not expect.
Optional<llvm::SaveAndRestore<Optional<llvm::MD5>>> MemberHashingScope;
if (IDC->areTokensHashedForThisBodyInsteadOfInterfaceHash() &&
!L->isCodeCompletion()) {
if (IDC->areTokensHashedForThisBodyInsteadOfInterfaceHash()) {
recordTokenHash("{");
recordTokenHash("}");
MemberHashingScope.emplace(CurrentTokenHash, llvm::MD5());
Expand Down Expand Up @@ -4713,9 +4708,6 @@ Parser::parseDeclList(SourceLoc LBLoc, SourceLoc &RBLoc, Diag<> ErrorDiag,
if (RBLoc.isInvalid())
hadError = true;

if (L->isCodeCompletion())
return std::make_pair(decls, None);

llvm::MD5::MD5Result result;
auto declListHash = MemberHashingScope ? *CurrentTokenHash : llvm::MD5();
declListHash.final(result);
Expand Down

0 comments on commit df94c4f

Please sign in to comment.