-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
[Serialization] Handle uninitialized type constraints #110496
[Serialization] Handle uninitialized type constraints #110496
Conversation
Thank you for submitting a Pull Request (PR) to the LLVM Project! This PR will be automatically labeled and the relevant teams will be notified. If you wish to, you can add reviewers by using the "Reviewers" section on this page. If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers. If you have further questions, they may be answered by the LLVM GitHub User Guide. You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums. |
@llvm/pr-subscribers-clang-tools-extra @llvm/pr-subscribers-clangd Author: Florian Albrechtskirchinger (falbrechtskirchinger) ChangesNote: This patch is based on the assumption, that an assertion causing a crash in clangd, is incorrect. Alternatively, the true error may lie elsewhere, possibly in ´Sema::BuildTypeConstraint <hr /> The ASTWriter currently assumes template type constraints to be initialized ((bool)getTypeConstraint() == hasTypeConstraint()). The attached test case presents a scenario where that is not the case. This patch removes the assumption and adds another boolean to the serialization, to explicitly encode whether the type constraint has been initialized. Fixes #99036. Full diff: https://github.com/llvm/llvm-project/pull/110496.diff 3 Files Affected:
diff --git a/clang-tools-extra/clangd/test/GH99036.test b/clang-tools-extra/clangd/test/GH99036.test
new file mode 100644
index 00000000000000..64ebe4c75a999d
--- /dev/null
+++ b/clang-tools-extra/clangd/test/GH99036.test
@@ -0,0 +1,29 @@
+# RUN: rm -rf %t
+# RUN: mkdir -p %t
+# RUN: split-file %s %t
+#
+# RUN: not clangd --check=%t/main.cpp 2>&1 | FileCheck -strict-whitespace %s
+#
+# CHECK: Loaded compilation database
+# CHECK-NEXT: Compile command inferred from main.cpp is:
+# CHECK: Indexing headers...
+# CHECK-NEXT: [unknown_typename] Line 1: in included file: unknown type name '_Up'
+# CHECK: All checks completed, 1 errors
+
+#--- header.h
+template<_Up>
+concept __decayed_same_as;
+template<__decayed_same_as>
+partial_ordering operator0
+
+#--- main.cpp
+#include "header.h"
+
+#--- compile_commands.json
+[
+ {
+ "directory": "/",
+ "command": "c++ -std=c++20",
+ "file": "main.cpp"
+ }
+]
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 7cead2728ca938..90783963934bb0 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -2674,7 +2674,7 @@ void ASTDeclReader::VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D) {
D->setDeclaredWithTypename(Record.readInt());
- if (D->hasTypeConstraint()) {
+ if (Record.readBool() && D->hasTypeConstraint()) {
ConceptReference *CR = nullptr;
if (Record.readBool())
CR = Record.readConceptReference();
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index b71684569609ac..1c9898c042cd4b 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -1899,7 +1899,7 @@ void ASTDeclWriter::VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D) {
Record.push_back(D->wasDeclaredWithTypename());
const TypeConstraint *TC = D->getTypeConstraint();
- assert((bool)TC == D->hasTypeConstraint());
+ Record.push_back(TC != nullptr); // reflects TypeConstraintInitialized
if (TC) {
auto *CR = TC->getConceptReference();
Record.push_back(CR != nullptr);
@@ -1917,7 +1917,7 @@ void ASTDeclWriter::VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D) {
if (OwnsDefaultArg)
Record.AddTemplateArgumentLoc(D->getDefaultArgument());
- if (!TC && !OwnsDefaultArg &&
+ if (!D->hasTypeConstraint() && !OwnsDefaultArg &&
D->getDeclContext() == D->getLexicalDeclContext() &&
!D->isInvalidDecl() && !D->hasAttrs() &&
!D->isTopLevelDeclInObjCContainer() && !D->isImplicit() &&
@@ -2580,6 +2580,7 @@ void ASTWriter::WriteDeclAbbrevs() {
// TemplateTypeParmDecl
Abv->Add(
BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // wasDeclaredWithTypename
+ Abv->Add(BitCodeAbbrevOp(0)); // TypeConstraintInitialized
Abv->Add(BitCodeAbbrevOp(0)); // OwnsDefaultArg
DeclTemplateTypeParmAbbrev = Stream.EmitAbbrev(std::move(Abv));
|
296cc1b
to
20042cc
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.
Thanks again for analyzing & working on this issue.
Some suggestions regarding the test:
-
As this is more of a clang issue (the offending code belongs to the serialization part of clang), so it is more appropriate to write a clang test instead of a clangd test.
-
We don't usually write lit tests but rather unit tests (specifically, tests under the
clangd/unittests
directory) for clangd.
The other part LG in general, but I'd like to hear input from folks who are more familiar with the serialization.
20042cc
to
8913503
Compare
You’re very welcome! Let's hope I'm on the right track.
The most recent push contains a clangd unit test. I'd argue that replicating the test logic present in clangd ( I've verified that the test triggers the assertion without the changes introduced in this PR. [ RUN ] ClangdAST.HandleUninitializedTypeConstraints
ClangdTests: /data6/git/llvm-project/clang/lib/Serialization/ASTWriterDecl.cpp:1902: void clang::ASTDeclWriter::VisitTemplateTypeParmDecl(clang::TemplateTypeParmDecl*): Assertion `(bool)TC == D->hasTypeConstraint()' failed. If you insist on a clang unit test, I'll need help. :) |
We need to resort to Specifically, add the following file to Name it with a reasonable name e.g. // RUN: %clang_cc1 -std=c++2a -emit-pch -fallow-pch-with-compiler-errors %s -o %t
// RUN: %clang_cc1 -std=c++2a -include-pch %t -verify %s
namespace GH99036 {
template <typename T>
concept C;
template <C U> void f();
} // namespace GH99036
Running |
Ah, thanks! I would not have figured that out on my own. :)
I looked through that directory and found only one test with
|
8913503
to
c090601
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.
Thanks! LGTM now
I'll help you merge it a few days later in case anyone else has further comments.
The ASTWriter currently assumes template type constraints to be initialized ((bool)getTypeConstraint() == hasTypeConstraint()). The attached test case presents a scenario where that is not the case. This patch removes the assumption and adds another boolean to the serialization, to explicitly encode whether the type constraint has been initialized. Fixes llvm#99036 Fixes llvm#109354
c090601
to
220d76a
Compare
Will this be backported to 19.x afterwards? |
@vient I think we can, yes |
Is that change still necessary at all after #111179 ? |
#111179 aims for a different case (and that is a follow-up for @cor3ntin's recursive concept patch, which is not yet merged into 19, I think), while this one is related to a serialization bug that has persisted for some time. So yes, this one is still necessary - it should occur with an assertion-enabled build when compiling a header containing an invalid concept decl. |
Not in my testing. @zyn0217 If you'd like to check: namespace GH99036 {
template <typename T>
concept C; // expected-error {{expected '='}}
template <C U> void f();
} // namespace GH99036 |
@@ -1899,7 +1899,7 @@ void ASTDeclWriter::VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D) { | |||
Record.push_back(D->wasDeclaredWithTypename()); | |||
|
|||
const TypeConstraint *TC = D->getTypeConstraint(); | |||
assert((bool)TC == D->hasTypeConstraint()); | |||
Record.push_back(/*TypeConstraintInitialized=*/TC != nullptr); | |||
if (TC) { |
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.
What about checking for D->hasTypeConstraint()
instead of pushing am additional bool?
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.
Because D->hasTypeConstraint()
does not imply that D->getTypeConstraint()
is non-null, as the code assumed (hence, the assertion).
@falbrechtskirchinger Ooops, yes, it's true that the bug is gone w/ #111179. But I'd say if we're going to cherry-pick #111179, we have to backport its precedent #103867 as well, so I think we can probably go ahead with this one and backport it to 19, and then we can revert this one on the trunk (tests should definitely be preserved) - this is indeed a bit awkward, but it depends whether it's worth backporting #103867 @cor3ntin (Again, I apologize for not looking into it so carefully) |
I'd like this crash to be fixed in 19.x.
No worries. To summarize: Land this PR and backport it. Revert on trunk. Submit new PR for the regression test? (We want to keep that, right?) |
That's just my idea, and I'd like Corentin to say something about it :) |
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'll leave this to @cor3ntin
#111179 is merged now |
Bump, it would be nice to have this or that fix backported to 19.x |
I think the best past forward would be to close this PR, and to open a new one targeting the 10.x branch, which a clear explanation that it's not needed in trunk. would that work for you @falbrechtskirchinger ? |
Will do. |
The ASTWriter currently assumes template type constraints to be initialized ((bool)getTypeConstraint() == hasTypeConstraint()). The attached test case presents a scenario where that is not the case.
This patch removes the assumption and adds another boolean to the serialization, to explicitly encode whether the type constraint has been initialized.
Fixes #99036.
Fixes #109354.