[Serialization] Handle uninitialized type constraints
authorFlorian Albrechtskirchinger <falbrechtskirchinger@gmail.com>
Mon, 21 Oct 2024 10:24:58 +0000 (12:24 +0200)
committerTobias Hieta <tobias@hieta.se>
Tue, 29 Oct 2024 10:09:39 +0000 (11:09 +0100)
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.

clang/lib/Serialization/ASTReaderDecl.cpp
clang/lib/Serialization/ASTWriterDecl.cpp
clang/test/PCH/cxx2a-constraints-crash.cpp

index c118f3818467d93e0320214ba97ee141b191b737..154acdfbe0327630e06a4dbc51dc8060e1d49d5c 100644 (file)
@@ -2665,7 +2665,8 @@ void ASTDeclReader::VisitTemplateTypeParmDecl(TemplateTypeParmDecl *D) {
 
   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();
index 8a4ca54349e38f1b1586d5d049551923f394107c..ff1334340874b29f7bba6669651bb26678d3935d 100644 (file)
@@ -1880,7 +1880,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) {
     auto *CR = TC->getConceptReference();
     Record.push_back(CR != nullptr);
@@ -1898,7 +1898,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() &&
@@ -2561,6 +2561,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));
 
index 637c55f0c879c938b2d0844048589e56627ce0fc..6126a0509fa4a9dec6b5041d1556785b7a2deb87 100644 (file)
@@ -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 @@ int main() {
 }
 
 #endif
+
+namespace GH99036 {
+
+template <typename T>
+concept C; // expected-error {{expected '='}}
+
+template <C U> void f(); // expected-error {{unknown type name 'C'}}
+
+} // namespace GH99036