[Serialization] Handle uninitialized type constraints
The ASTWriter currently assumes template type constraints to be
initialized ((bool)getTypeConstraint() == hasTypeConstraint()). Issues
#99036 and #109354 identified a scenario where this assertion is
violated.
This patch removes the assumption and adds another boolean to the
serialization, to explicitly encode whether the type constraint has been
initialized.
The same issue was incidentally fixed on the main branch by #111179.
This solution avoids backporting #111179 and its dependencies.
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index c118f38..154acdf 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -2665,7 +2665,8 @@
D->setDeclaredWithTypename(Record.readInt());
- if (D->hasTypeConstraint()) {
+ const bool TypeConstraintInitialized = Record.readBool();
+ if (TypeConstraintInitialized && 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 8a4ca54..ff13343 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -1880,7 +1880,7 @@
Record.push_back(D->wasDeclaredWithTypename());
const TypeConstraint *TC = D->getTypeConstraint();
- assert((bool)TC == D->hasTypeConstraint());
+ Record.push_back(/*TypeConstraintInitialized=*/TC != nullptr);
if (TC) {
auto *CR = TC->getConceptReference();
Record.push_back(CR != nullptr);
@@ -1898,7 +1898,7 @@
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() &&
@@ -2561,6 +2561,7 @@
// TemplateTypeParmDecl
Abv->Add(
BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // wasDeclaredWithTypename
+ Abv->Add(BitCodeAbbrevOp(0)); // TypeConstraintInitialized
Abv->Add(BitCodeAbbrevOp(0)); // OwnsDefaultArg
DeclTemplateTypeParmAbbrev = Stream.EmitAbbrev(std::move(Abv));
diff --git a/clang/test/PCH/cxx2a-constraints-crash.cpp b/clang/test/PCH/cxx2a-constraints-crash.cpp
index 637c55f..6126a05 100644
--- a/clang/test/PCH/cxx2a-constraints-crash.cpp
+++ b/clang/test/PCH/cxx2a-constraints-crash.cpp
@@ -1,7 +1,5 @@
-// RUN: %clang_cc1 -std=c++2a -emit-pch %s -o %t
-// RUN: %clang_cc1 -std=c++2a -include-pch %t -verify %s
-
-// expected-no-diagnostics
+// RUN: %clang_cc1 -std=c++2a -fallow-pch-with-compiler-errors -emit-pch -o %t %s -verify
+// RUN: %clang_cc1 -std=c++2a -fallow-pch-with-compiler-errors -include-pch %t %s -verify
#ifndef HEADER
#define HEADER
@@ -27,3 +25,12 @@
}
#endif
+
+namespace GH99036 {
+
+template <typename T>
+concept C; // expected-error {{expected '='}}
+
+template <C U> void f(); // expected-error {{unknown type name 'C'}}
+
+} // namespace GH99036