-
Notifications
You must be signed in to change notification settings - Fork 14.1k
[Modules] Record whether VarDecl initializers contain side effects #143739
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
base: main
Are you sure you want to change the base?
Conversation
@llvm/pr-subscribers-clang @llvm/pr-subscribers-clang-modules Author: Henrik G. Olsson (hnrklssn) ChangesThis assert was reportedly added to "Defensively ensure that GetExternalDeclStmt protects itself from nested deserialization". In the tests for swiftlang/swift#81859 I was able to trigger this assert without nested deserialization. In
where
In this case
in DeclMustBeEmitted we have:
in VarDecl::getInit we have:
which ends up calling At first I considered whether rdar://153085264 Full diff: https://github.com/llvm/llvm-project/pull/143739.diff 1 Files Affected:
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 70b54b7296882..aaa64b06e1cee 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -8310,8 +8310,6 @@ Stmt *ASTReader::GetExternalDeclStmt(uint64_t Offset) {
Error(std::move(Err));
return nullptr;
}
- assert(NumCurrentElementsDeserializing == 0 &&
- "should not be called while already deserializing");
Deserializing D(this);
return ReadStmtFromStream(*Loc.F);
}
|
I think we shouldn't remove the assertion. Your test passes with the removal of the assertion since the initializers are not complex. So it ends quickly. But if it is a complex initialization which triggers more deserialization, I feel it will be problematic. I think the point is in I think, the proper solution may be:
|
Thanks for providing context! I'm not very familiar with this part of clang. I have a few clarifying questions: Actually, looking at the callers, one of them is Another option would be to set some bits in |
Semantically it is better to do this in We can relate ASTContext and ASTReader by ExternalASTSource generally. We can add an interface in ExternalASTSource and call it in ASTContext and implement it in ASTReader.
Generally we don't do this, since it will add additional cost for non-module users. Although it is cheaper and simpler for module users, generally we prefer to give non-module users higher precedence. |
f5b43df
to
d239fee
Compare
✅ With the latest revision this PR passed the C/C++ code formatter. |
d239fee
to
a8042b8
Compare
@ChuanqiXu9 I pushed a new fix based on your feedback. Please let me know what you think. I don't really know how to test this, so if you think it needs testing I'm open for suggestions. |
a8042b8
to
44e965e
Compare
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 think this new function should be called from ASTContext::DeclMustBeEmitted
, no?
@@ -1442,6 +1442,10 @@ class ASTReader | |||
const StringRef &operator*() && = delete; | |||
}; | |||
|
|||
/// VarDecls with initializers containing side effects must be emitted, | |||
/// but DeclMustBeEmitted is not allowed to deserialize the intializer. | |||
llvm::SmallPtrSet<Decl *, 16> InitSideEffectVars; |
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.
Not a big deal, but do we have any kind of data informing us how many of these we typically see?
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.
No idea!
The direction meets my expectation. I think you already have an existing test for swift. Maybe you can try to reduce a lit test from it. |
44e965e
to
8658e3e
Compare
Yeah, I've tried reducing a lit test, but my reduced case doesn't trigger the original assert because the initializer is already deserialized. I'm trying to figure out what the difference is. |
2c488b2
to
89179c0
Compare
Managed to create a reduced test case now that triggered the original assert, but doesn't with the current patch. |
Of course, yes! Fixed now. |
89179c0
to
3f29c7c
Compare
Calling `DeclMustBeEmitted` should not lead to more deserialization, as it may occur before previous deserialization has finished. When passed a `VarDecl` with an initializer however, `DeclMustBeEmitted` needs to know whether that initializer contains side effects. When the `VarDecl` is deserialized but the initializer is not, this triggers deserialization of the initializer. To avoid this we add a bit to the serialization format for `VarDecl`s, indicating whether its initializer contains side effects or not, so that the `ASTReader` can query this information directly without deserializing the initializer. rdar://153085264
3f29c7c
to
eacaac0
Compare
This assert was reportedly added to "Defensively ensure that GetExternalDeclStmt protects itself from nested deserialization". In the tests for swiftlang/swift#81859 I was able to trigger this assert without nested deserialization. In
FinishedDeserializing
we have this:where
NumCurrentElementsDeserializing
is clearly 1 when callingfinishPendingActions
. Through this we end up inloadDeclUpdateRecords
, which has:In this case
Record.JustLoaded
is false, so we end up callingisConsumerInterestedIn
.In isConsumerInterestedIn we have:
in DeclMustBeEmitted we have:
in VarDecl::getInit we have:
which ends up calling
ASTReader::GetExternalDeclStmt
.At first I considered whether
bool WasInteresting = Record.JustLoaded || isConsumerInterestedIn(D);
should guard against callingisConsumerInterestedIn
in even more cases, but I didn't know what that would be. Instead I tried removing the assert, and my test passed, so I assume callingisConsumerInterestedIn
was safe in this case.rdar://153085264