Split off functionality used in index-while-building feature as suggested in the review
https://reviews.llvm.org/D64384
Details
Diff Detail
Event Timeline
clang/lib/Frontend/FrontendAction.cpp | ||
---|---|---|
1130 | I think it is very difficult to combine these capability flags like hasIRSupport and hasCodeCompletionSupport. It seems to me that all wrapped actions need to precisely agree on values of these flags, otherwise at least one of the actions will be run in a mode that it does not expect. For example, take hasCodeCompletionSupport. Let's say we have two actions, one turns it on, another turns it off. If the combined wrapper decides to turn code completion support on, then the non-CC action will be unhappy because parsing will be cut off in the middle. If the combined wrapper decides to turn CC off, the CC action will be unhappy because parsing will not be interrupted. That was a hypothetical example, but a real example that does come up in the index-while-building patch is getTranslationUnitKind. GenerateIndexAction wants it to be TU_Complete, but IndexingFrontendAction can set it to TU_Prefix. When they disagree, there's no way to reconcile in a generic way, unless we try to understand what these specific actions do. Therefore, it seems like combining FrontendActions is not a great idea in general... I'm sorry for suggesting it, I didn't realize it at that point. Where we combine FrontendActions right now, things mostly happen to work by chance, and when wrappers forward these capability bits to the wrapped action, they often assume certain values for these bits anyway. I wanted to suggest something different -- using ASTConsumer as the base currency, and trying to combine abstractions. Let's look at GenerateIndexAction and IndexingFrontendAction in more detail. GenerateIndexAction is an ASTConsumer plus GenerateIndexAction::EndSourceFileAction callback. EndSourceFileAction, I think, is roughly equivalent to ASTConsumer::HandleTranslationUnit. Therefore, GenerateIndexAction does not need to be a FrontendAction, it is just an ASTConsumer. IndexingFrontendAction is a lot more complex -- not only it needs a complex factory that creates the ASTConsumer, but it also overrides getTranslationUnitKind, which changes the behavior of Sema and Preprocessor in significant ways. So it does not seem like IndexFrontendAction can become an ASTConsumer. However, IndexFrontendAction does not seem like a standalone ASTConsumer anyway. Its primary reason to exist is the logic in IndexingConsumer::shouldSkipFunctionBody. That logic seems interesting for general-purpose indexing, not just for libclang, so it would make sense to fold it into IndexASTConsumer. To summarize:
What do you think? |
clang/lib/Frontend/FrontendAction.cpp | ||
---|---|---|
1130 | I agree that this abstraction would be more on the pragmatic end of spectrum rather than on the end where suitable and fitting abstractions live. Both implementations and clients would have to be really careful to not violate some implicit assumptions. I'll give it another go. I'm definitely in favor of simplifying the design if possible. Thanks for the suggestions! I'll try to refactor the code to get some feel for what can be done here. |
I'll just leave this review here until we finish the discussion but consider it to be effectively abandoned.
clang/lib/Frontend/FrontendAction.cpp | ||
---|---|---|
1130 | Thanks for the suggestion, I didn't realize the correspondence between ASTConsumer::HandleTranslationUnit and FrontendAction::EndSourceFileAction. I changed Emit|Generate-IndexAction to ASTConsumer in the original patch. Now, I'm not sure I understand your intentions about moving shouldSkipFunctionBody from IndexingConsumer in libclang to (IIUC) GenerateIndexASTConsumer in libIndex as GenerateIndexASTConsumer. (Using terms of https://reviews.llvm.org/D64384). Or do you mean just exposing the TUSkipBodyControl machinery in libIndex? |
clang/lib/Frontend/FrontendAction.cpp | ||
---|---|---|
1130 | Thank you! | |
1130 | Sorry for being unclear -- yes, I meant exposing the APIs to skip function bodies in libIndex. Whether that involves exposing TUSkipBodyControl -- I don't know, maybe a simpler abstraction would be more general -- like a callback that, based on a source location, decides whether code at that declaration should be indexed. clangd would be also interested, this patch just went in: https://reviews.llvm.org/D66226 |
BTW It just occurred to me that multiplexing ASTConsumer::shouldSkipFunctionBody must have hit the same issue as FrontendAction multiplexing we considered above.
bool MultiplexConsumer::shouldSkipFunctionBody(Decl *D) { bool Skip = true; for (auto &Consumer : Consumers) Skip = Skip && Consumer->shouldSkipFunctionBody(D); return Skip; }
It's true that this is somewhat sketchy in the case when one of the consumers absolutely needs to skip the function body.
But the current implementation seems like the right trade-off in most (all?) of the practical use-cases: we should always prefer to skip function bodies because that makes clang faster; however, if any of the consumers do need the function body (e.g. the collect references, perform analyses on the function body, etc.), we cannot apply this optimization because that would introduce correctness bugs.
(sorry about chiming in the middle of the review, I've thought about this myself before and wanted to share my conclusions)
Agreed about multiplexing issues -- this is a case where wrapping or merging abstraction layers is the right solution.
Will take a look at getTranslationUnitKind optimization now.
clang/lib/Frontend/FrontendAction.cpp | ||
---|---|---|
1130 | I put up this patch: |
I think it is very difficult to combine these capability flags like hasIRSupport and hasCodeCompletionSupport. It seems to me that all wrapped actions need to precisely agree on values of these flags, otherwise at least one of the actions will be run in a mode that it does not expect.
For example, take hasCodeCompletionSupport. Let's say we have two actions, one turns it on, another turns it off. If the combined wrapper decides to turn code completion support on, then the non-CC action will be unhappy because parsing will be cut off in the middle. If the combined wrapper decides to turn CC off, the CC action will be unhappy because parsing will not be interrupted.
That was a hypothetical example, but a real example that does come up in the index-while-building patch is getTranslationUnitKind. GenerateIndexAction wants it to be TU_Complete, but IndexingFrontendAction can set it to TU_Prefix. When they disagree, there's no way to reconcile in a generic way, unless we try to understand what these specific actions do.
Therefore, it seems like combining FrontendActions is not a great idea in general... I'm sorry for suggesting it, I didn't realize it at that point.
Where we combine FrontendActions right now, things mostly happen to work by chance, and when wrappers forward these capability bits to the wrapped action, they often assume certain values for these bits anyway.
I wanted to suggest something different -- using ASTConsumer as the base currency, and trying to combine abstractions. Let's look at GenerateIndexAction and IndexingFrontendAction in more detail.
GenerateIndexAction is an ASTConsumer plus GenerateIndexAction::EndSourceFileAction callback. EndSourceFileAction, I think, is roughly equivalent to ASTConsumer::HandleTranslationUnit. Therefore, GenerateIndexAction does not need to be a FrontendAction, it is just an ASTConsumer.
IndexingFrontendAction is a lot more complex -- not only it needs a complex factory that creates the ASTConsumer, but it also overrides getTranslationUnitKind, which changes the behavior of Sema and Preprocessor in significant ways. So it does not seem like IndexFrontendAction can become an ASTConsumer.
However, IndexFrontendAction does not seem like a standalone ASTConsumer anyway. Its primary reason to exist is the logic in IndexingConsumer::shouldSkipFunctionBody. That logic seems interesting for general-purpose indexing, not just for libclang, so it would make sense to fold it into IndexASTConsumer.
To summarize:
What do you think?