[clang] [Clang] Serialize PartialDiagnostic for concepts (PR #211767)

Younan Zhang via cfe-commits cfe-commits at lists.llvm.org
Fri Jul 24 03:49:19 PDT 2026


https://github.com/zyn0217 created https://github.com/llvm/llvm-project/pull/211767

None

>From 5d3bf580b008ccd4c02e74047a0d72e7c41f205e Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Fri, 24 Jul 2026 12:08:07 +0800
Subject: [PATCH] [Clang] Serialize PartialDiagnostic for concepts

---
 clang/include/clang/AST/ASTConcept.h          |  44 ++++-
 clang/include/clang/AST/ASTContext.h          |   4 +-
 clang/include/clang/AST/ExprConcepts.h        |  29 ++--
 .../clang/Basic/DiagnosticSemaKinds.td        |  18 +--
 clang/include/clang/Sema/Sema.h               |   6 +-
 clang/lib/AST/ASTConcept.cpp                  |  74 ++++++++-
 clang/lib/AST/ASTImporter.cpp                 | 150 ++++++++++++++++--
 clang/lib/Sema/SemaConcept.cpp                | 128 ++++++++-------
 clang/lib/Sema/SemaExprCXX.cpp                |  12 +-
 clang/lib/Sema/SemaTemplateInstantiate.cpp    |  56 +++----
 clang/lib/Sema/TreeTransform.h                |   2 +-
 clang/lib/Serialization/ASTReaderStmt.cpp     | 108 +++++++++++--
 clang/lib/Serialization/ASTWriterStmt.cpp     |  99 +++++++++++-
 .../expr.prim.req/type-requirement.cpp        |   2 +-
 14 files changed, 552 insertions(+), 180 deletions(-)

diff --git a/clang/include/clang/AST/ASTConcept.h b/clang/include/clang/AST/ASTConcept.h
index 4319df35ab7e5..e4e2904b3c85b 100644
--- a/clang/include/clang/AST/ASTConcept.h
+++ b/clang/include/clang/AST/ASTConcept.h
@@ -18,14 +18,17 @@
 #include "clang/AST/NestedNameSpecifierBase.h"
 #include "clang/AST/TemplateBase.h"
 #include "clang/Basic/OptionalUnsigned.h"
+#include "clang/Basic/PartialDiagnostic.h"
 #include "clang/Basic/SourceLocation.h"
 #include "llvm/ADT/FoldingSet.h"
 #include "llvm/ADT/PointerUnion.h"
 #include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/TrailingObjects.h"
 #include <utility>
 
 namespace clang {
 
+class ASTContext;
 class ConceptDecl;
 class TemplateDecl;
 class ConceptReference;
@@ -33,11 +36,50 @@ class Expr;
 class NamedDecl;
 struct PrintingPolicy;
 
+/// A compact, trivially-destructible stand-in for \c PartialDiagnostic suitable
+/// for storage on the AST and for serialization.
+///
+/// Unlike \c PartialDiagnostic it doesn't own a \c DiagnosticStorage and is not
+/// tied to a \c DiagStorageAllocator: only part of the arguments that are
+/// currently used by SFINAE diagnostics are kept.
+///
+class ASTPartialDiagnostic final
+    : private llvm::TrailingObjects<ASTPartialDiagnostic, uint64_t, uint32_t,
+                                    CharSourceRange> {
+  friend TrailingObjects;
+
+  /// Low bits of each packed word hold the ArgumentKind; the rest is the
+  /// (string) text length.
+  static constexpr unsigned KindBits = 5;
+
+  unsigned DiagID;
+  unsigned NumArgs;
+  unsigned NumRanges;
+
+  size_t numTrailingObjects(OverloadToken<uint64_t>) const { return NumArgs; }
+  size_t numTrailingObjects(OverloadToken<uint32_t>) const { return NumArgs; }
+
+  ASTPartialDiagnostic(const ASTContext &C, const PartialDiagnostic &PD,
+                       unsigned NumArgs, unsigned NumRanges);
+
+public:
+  static ASTPartialDiagnostic *Create(const ASTContext &C,
+                                      const PartialDiagnostic &PD);
+
+  unsigned getDiagID() const { return DiagID; }
+
+  /// Rebuild an equivalent \c PartialDiagnostic, using \p Alloc for its
+  /// storage.
+  PartialDiagnostic
+  getPartialDiagnostic(PartialDiagnostic::DiagStorageAllocator &Alloc) const;
+};
+
 /// Unsatisfied constraint expressions if the template arguments could be
 /// substituted into them, or a diagnostic if substitution resulted in
 /// an invalid expression.
 ///
-using ConstraintSubstitutionDiagnostic = std::pair<SourceLocation, StringRef>;
+using ConstraintSubstitutionDiagnostic =
+    std::pair<SourceLocation, ASTPartialDiagnostic *>;
 using UnsatisfiedConstraintRecord =
     llvm::PointerUnion<const Expr *, const ConceptReference *,
                        const ConstraintSubstitutionDiagnostic *>;
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 7ed6509c3c16c..3279ea27f2d51 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -887,10 +887,10 @@ class ASTContext : public RefCountedBase<ASTContext> {
   }
   void Deallocate(void *Ptr) const {}
 
-  llvm::StringRef backupStr(llvm::StringRef S) const {
+  StringRef backupStr(StringRef S) const {
     char *Buf = new (*this) char[S.size()];
     llvm::copy(S, Buf);
-    return llvm::StringRef(Buf, S.size());
+    return StringRef(Buf, S.size());
   }
 
   /// Allocates a \c DeclListNode or returns one from the \c ListNodeFreeList
diff --git a/clang/include/clang/AST/ExprConcepts.h b/clang/include/clang/AST/ExprConcepts.h
index 4f162b6ffc8af..ddaf749281bce 100644
--- a/clang/include/clang/AST/ExprConcepts.h
+++ b/clang/include/clang/AST/ExprConcepts.h
@@ -183,12 +183,13 @@ class Requirement {
   bool Satisfied : 1;
 public:
   struct SubstitutionDiagnostic {
-    StringRef SubstitutedEntity;
-    // FIXME: Store diagnostics semantically and not as prerendered strings.
-    //  Fixing this probably requires serialization of PartialDiagnostic
-    //  objects.
+    llvm::PointerUnion<const ParmVarDecl *, const Expr *,
+                       const TypeSourceInfo *>
+        Entity;
     SourceLocation DiagLoc;
-    StringRef DiagMessage;
+    /// The captured substitution-failure diagnostic, or null if none was
+    /// available (an "unknown substitution error").
+    ASTPartialDiagnostic *Diag;
   };
 
   Requirement(RequirementKind Kind, bool IsDependent,
@@ -433,7 +434,6 @@ class NestedRequirement : public Requirement {
   Expr *Constraint = nullptr;
   const ASTConstraintSatisfaction *Satisfaction = nullptr;
   bool HasInvalidConstraint = false;
-  StringRef InvalidConstraintEntity;
 
 public:
   friend ASTStmtReader;
@@ -456,25 +456,20 @@ class NestedRequirement : public Requirement {
         Constraint(Constraint),
         Satisfaction(ASTConstraintSatisfaction::Create(C, Satisfaction)) {}
 
-  NestedRequirement(StringRef InvalidConstraintEntity,
+  NestedRequirement(Expr *InvalidConstraintEntity,
                     const ASTConstraintSatisfaction *Satisfaction)
       : Requirement(RK_Nested,
                     /*IsDependent=*/false,
-                    /*ContainsUnexpandedParameterPack*/ false,
+                    /*ContainsUnexpandedParameterPack=*/false,
                     Satisfaction->IsSatisfied),
-        Satisfaction(Satisfaction), HasInvalidConstraint(true),
-        InvalidConstraintEntity(InvalidConstraintEntity) {}
-
-  NestedRequirement(ASTContext &C, StringRef InvalidConstraintEntity,
-                    const ConstraintSatisfaction &Satisfaction)
-      : NestedRequirement(InvalidConstraintEntity,
-                          ASTConstraintSatisfaction::Create(C, Satisfaction)) {}
+        Constraint(InvalidConstraintEntity), Satisfaction(Satisfaction),
+        HasInvalidConstraint(true) {}
 
   bool hasInvalidConstraint() const { return HasInvalidConstraint; }
 
-  StringRef getInvalidConstraintEntity() {
+  Expr *getInvalidConstraintEntity() {
     assert(hasInvalidConstraint());
-    return InvalidConstraintEntity;
+    return Constraint;
   }
 
   Expr *getConstraintExpr() const {
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index f14288dd2967d..eea3b9fd0fc83 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3195,7 +3195,7 @@ def err_template_arg_list_constraints_not_satisfied : Error<
   "constraints not satisfied for %select{class template|function template|variable template|alias template|"
   "template template parameter|template}0 %1%2">;
 def note_substituted_constraint_expr_is_ill_formed : Note<
-  "because substituted constraint expression is ill-formed%0">;
+  "because substituted constraint expression is ill-formed: %0">;
 def note_constraint_references_error
     : Note<"constraint depends on a previously diagnosed expression">;
 def note_atomic_constraint_evaluated_to_false
@@ -3220,27 +3220,27 @@ def err_reference_to_function_with_unsatisfied_constraints : Error<
 def err_requires_expr_local_parameter_default_argument : Error<
   "default arguments not allowed for parameters of a requires expression">;
 def note_expr_requirement_expr_substitution_error : Note<
-  "%select{and|because}0 '%1' would be invalid: %2">;
+  "%select{and|because}0 %1 would be invalid: %2">;
 def note_expr_requirement_expr_unknown_substitution_error : Note<
-  "%select{and|because}0 '%1' would be invalid">;
+  "%select{and|because}0 %1 would be invalid">;
 def note_expr_requirement_noexcept_not_met
     : Note<"%select{and|because}0 %1 may throw an exception">;
 def note_expr_requirement_type_requirement_substitution_error : Note<
-  "%select{and|because}0 '%1' would be invalid: %2">;
+  "%select{and|because}0 %1 would be invalid: %2">;
 def note_expr_requirement_type_requirement_unknown_substitution_error : Note<
-  "%select{and|because}0 '%1' would be invalid">;
+  "%select{and|because}0 %1 would be invalid">;
 def note_expr_requirement_constraints_not_satisfied
     : Note<"%select{and|because}0 type constraint %1 was not satisfied:">;
 def note_expr_requirement_constraints_not_satisfied_simple : Note<
   "%select{and|because}0 %1 does not satisfy %2:">;
 def note_type_requirement_substitution_error : Note<
-  "%select{and|because}0 '%1' would be invalid: %2">;
+  "%select{and|because}0 %1 would be invalid: %2">;
 def note_type_requirement_unknown_substitution_error : Note<
-  "%select{and|because}0 '%1' would be invalid">;
+  "%select{and|because}0 %1 would be invalid">;
 def note_nested_requirement_substitution_error : Note<
-  "%select{and|because}0 '%1' would be invalid%2">;
+  "%select{and|because}0 %1 would be invalid: %2">;
 def note_nested_requirement_unknown_substitution_error : Note<
-  "%select{and|because}0 '%1' would be invalid">;
+  "%select{and|because}0 %1 would be invalid">;
 def note_ambiguous_atomic_constraints : Note<
   "similar constraint expressions not considered equivalent; constraint "
   "expressions cannot be considered equivalent unless they originate from the "
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 11550340ac8d2..48959061c9e65 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -8944,7 +8944,7 @@ class Sema final : public SemaBase {
       concepts::Requirement::SubstitutionDiagnostic *SubstDiag);
   concepts::NestedRequirement *BuildNestedRequirement(Expr *E);
   concepts::NestedRequirement *
-  BuildNestedRequirement(StringRef InvalidConstraintEntity,
+  BuildNestedRequirement(Expr *InvalidConstraintEntity,
                          const ASTConstraintSatisfaction &Satisfaction);
   ExprResult ActOnRequiresExpr(SourceLocation RequiresKWLoc,
                                RequiresExprBodyDecl *Body,
@@ -14104,12 +14104,10 @@ class Sema final : public SemaBase {
            !inConstraintSubstitution();
   }
 
-  using EntityPrinter = llvm::function_ref<void(llvm::raw_ostream &)>;
-
   /// \brief create a Requirement::SubstitutionDiagnostic with only a
   /// SubstitutedEntity and DiagLoc using ASTContext's allocator.
   concepts::Requirement::SubstitutionDiagnostic *
-  createSubstDiagAt(SourceLocation Location, EntityPrinter Printer);
+  createSubstDiagAt(SourceLocation Location, Expr *E);
 
   ///@}
 
diff --git a/clang/lib/AST/ASTConcept.cpp b/clang/lib/AST/ASTConcept.cpp
index fd12bc4e83827..dda7753a7c542 100644
--- a/clang/lib/AST/ASTConcept.cpp
+++ b/clang/lib/AST/ASTConcept.cpp
@@ -31,14 +31,74 @@ CreateUnsatisfiedConstraintRecord(const ASTContext &C,
   else if (const auto *Concept =
                llvm::dyn_cast<const ConceptReference *>(Detail))
     new (TrailingObject) UnsatisfiedConstraintRecord(Concept);
-  else {
-    auto &SubstitutionDiagnostic =
-        *cast<const clang::ConstraintSubstitutionDiagnostic *>(Detail);
-    StringRef Message = C.backupStr(SubstitutionDiagnostic.second);
-    auto *NewSubstDiag = new (C) clang::ConstraintSubstitutionDiagnostic(
-        SubstitutionDiagnostic.first, Message);
-    new (TrailingObject) UnsatisfiedConstraintRecord(NewSubstDiag);
+  else
+    new (TrailingObject)
+        UnsatisfiedConstraintRecord(new (C) ConstraintSubstitutionDiagnostic(
+            *cast<const ConstraintSubstitutionDiagnostic *>(Detail)));
+}
+
+ASTPartialDiagnostic *
+ASTPartialDiagnostic::Create(const ASTContext &C, const PartialDiagnostic &PD) {
+  unsigned NumArgs = 0, NumRanges = 0;
+  if (PD.hasStorage()) {
+    const DiagnosticStorage *S = PD.getStorage();
+    NumArgs = S->NumDiagArgs;
+    NumRanges = S->DiagRanges.size();
+  }
+  void *Mem = C.Allocate(totalSizeToAlloc<uint64_t, uint32_t, CharSourceRange>(
+                             NumArgs, NumArgs, NumRanges),
+                         alignof(ASTPartialDiagnostic));
+  return new (Mem) ASTPartialDiagnostic(C, PD, NumArgs, NumRanges);
+}
+
+ASTPartialDiagnostic::ASTPartialDiagnostic(const ASTContext &C,
+                                           const PartialDiagnostic &PD,
+                                           unsigned NumArgs, unsigned NumRanges)
+    : DiagID(PD.getDiagID()), NumArgs(NumArgs), NumRanges(NumRanges) {
+  if (!NumArgs && !NumRanges)
+    return;
+  const DiagnosticStorage *S = PD.getStorage();
+  uint64_t *Values = getTrailingObjects<uint64_t>();
+  uint32_t *KindLengths = getTrailingObjects<uint32_t>();
+  for (unsigned I = 0; I != NumArgs; ++I) {
+    auto Kind = (DiagnosticsEngine::ArgumentKind)S->DiagArgumentsKind[I];
+    if (Kind == DiagnosticsEngine::ak_std_string ||
+        Kind == DiagnosticsEngine::ak_c_string) {
+      StringRef Text =
+          C.backupStr(Kind == DiagnosticsEngine::ak_std_string
+                          ? StringRef(S->DiagArgumentsStr[I])
+                          : StringRef(reinterpret_cast<const char *>(
+                                S->DiagArgumentsVal[I])));
+      assert(Text.size() < (1u << (32 - KindBits)) && "argument text too long");
+      Values[I] = reinterpret_cast<uint64_t>(Text.data());
+      KindLengths[I] =
+          DiagnosticsEngine::ak_std_string | (Text.size() << KindBits);
+    } else {
+      Values[I] = S->DiagArgumentsVal[I];
+      KindLengths[I] = Kind;
+    }
+  }
+  llvm::copy(S->DiagRanges, getTrailingObjects<CharSourceRange>());
+}
+
+PartialDiagnostic ASTPartialDiagnostic::getPartialDiagnostic(
+    PartialDiagnostic::DiagStorageAllocator &Alloc) const {
+  PartialDiagnostic PD(DiagID, Alloc);
+  const uint64_t *Values = getTrailingObjects<uint64_t>();
+  const uint32_t *KindLengths = getTrailingObjects<uint32_t>();
+  for (unsigned I = 0; I != NumArgs; ++I) {
+    auto Kind = (DiagnosticsEngine::ArgumentKind)(KindLengths[I] &
+                                                  ((1u << KindBits) - 1));
+    if (Kind == DiagnosticsEngine::ak_std_string)
+      PD.AddString(StringRef(reinterpret_cast<const char *>(Values[I]),
+                             KindLengths[I] >> KindBits));
+    else
+      PD.AddTaggedVal(Values[I], Kind);
   }
+  for (const CharSourceRange &R :
+       getTrailingObjects<CharSourceRange>(NumRanges))
+    PD.AddSourceRange(R);
+  return PD;
 }
 
 ASTConstraintSatisfaction::ASTConstraintSatisfaction(
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index db7d223d56af3..173508e3c1f7e 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -1074,6 +1074,104 @@ StringRef ASTNodeImporter::ImportASTStringRef(StringRef FromStr) {
   return StringRef(ToStore, FromStr.size());
 }
 
+template <>
+Expected<ASTPartialDiagnostic *>
+ASTNodeImporter::import(ASTPartialDiagnostic *From) {
+  if (!From)
+    return nullptr;
+
+  PartialDiagnostic::DiagStorageAllocator Allocator;
+  PartialDiagnostic FromPD = From->getPartialDiagnostic(Allocator);
+  PartialDiagnostic ToPD(FromPD.getDiagID(), Allocator);
+  using ArgumentKind = DiagnosticsEngine::ArgumentKind;
+  if (const DiagnosticStorage *S =
+          FromPD.hasStorage() ? FromPD.getStorage() : nullptr) {
+    for (unsigned I = 0; I != S->NumDiagArgs; ++I) {
+      auto K = (ArgumentKind)S->DiagArgumentsKind[I];
+      uint64_t Val = S->DiagArgumentsVal[I];
+      switch (K) {
+      case ArgumentKind::ak_std_string:
+        ToPD.AddString(S->DiagArgumentsStr[I]);
+        break;
+      case ArgumentKind::ak_qualtype: {
+        Expected<QualType> ToQT =
+            import(QualType::getFromOpaquePtr(reinterpret_cast<void *>(Val)));
+        if (!ToQT)
+          return ToQT.takeError();
+        ToPD.AddTaggedVal(reinterpret_cast<uint64_t>(ToQT->getAsOpaquePtr()),
+                          K);
+        break;
+      }
+      case ArgumentKind::ak_declarationname: {
+        Expected<DeclarationName> ToName =
+            import(DeclarationName::getFromOpaqueInteger(Val));
+        if (!ToName)
+          return ToName.takeError();
+        ToPD.AddTaggedVal(ToName->getAsOpaqueInteger(), K);
+        break;
+      }
+      case ArgumentKind::ak_nameddecl: {
+        auto ToDecl = import(reinterpret_cast<NamedDecl *>(Val));
+        if (!ToDecl)
+          return ToDecl.takeError();
+        ToPD.AddTaggedVal(reinterpret_cast<uint64_t>(*ToDecl), K);
+        break;
+      }
+      case ArgumentKind::ak_declcontext: {
+        auto ToDecl = import(
+            Decl::castFromDeclContext(reinterpret_cast<DeclContext *>(Val)));
+        if (!ToDecl)
+          return ToDecl.takeError();
+        ToPD.AddTaggedVal(
+            reinterpret_cast<uint64_t>(Decl::castToDeclContext(*ToDecl)), K);
+        break;
+      }
+      case ArgumentKind::ak_nestednamespec: {
+        Expected<NestedNameSpecifier> ToNNS =
+            import(NestedNameSpecifier::getFromVoidPointer(
+                reinterpret_cast<void *>(Val)));
+        if (!ToNNS)
+          return ToNNS.takeError();
+        ToPD.AddTaggedVal(reinterpret_cast<uint64_t>(ToNNS->getAsVoidPointer()),
+                          K);
+        break;
+      }
+      case ArgumentKind::ak_attr: {
+        auto ToAttr = import(reinterpret_cast<Attr *>(Val));
+        if (!ToAttr)
+          return ToAttr.takeError();
+        ToPD.AddTaggedVal(reinterpret_cast<uint64_t>(*ToAttr), K);
+        break;
+      }
+      case ArgumentKind::ak_expr: {
+        auto ToExpr = import(reinterpret_cast<Expr *>(Val));
+        if (!ToExpr)
+          return ToExpr.takeError();
+        ToPD.AddTaggedVal(reinterpret_cast<uint64_t>(*ToExpr), K);
+        break;
+      }
+      case ArgumentKind::ak_identifierinfo:
+        ToPD.AddTaggedVal(reinterpret_cast<uint64_t>(Importer.Import(
+                              reinterpret_cast<IdentifierInfo *>(Val))),
+                          K);
+        break;
+      default:
+        ToPD.AddTaggedVal(Val, K);
+        break;
+      }
+    }
+    for (const CharSourceRange &R : S->DiagRanges) {
+      Expected<SourceRange> ToRange = import(R.getAsRange());
+      if (!ToRange)
+        return ToRange.takeError();
+      ToPD.AddSourceRange(R.isTokenRange()
+                              ? CharSourceRange::getTokenRange(*ToRange)
+                              : CharSourceRange::getCharRange(*ToRange));
+    }
+  }
+  return ASTPartialDiagnostic::Create(Importer.getToContext(), ToPD);
+}
+
 Error ASTNodeImporter::ImportConstraintSatisfaction(
     const ASTConstraintSatisfaction &FromSat, ConstraintSatisfaction &ToSat) {
   ToSat.IsSatisfied = FromSat.IsSatisfied;
@@ -1091,16 +1189,17 @@ Error ASTNodeImporter::ImportConstraintSatisfaction(
           return ToCROrErr.takeError();
         ToSat.Details.emplace_back(ToCROrErr.get());
       } else {
-        auto Pair =
+        auto *Pair =
             Record->dyn_cast<const ConstraintSubstitutionDiagnostic *>();
-
-        ExpectedSLoc ToPairFirst = import(Pair->first);
-        if (!ToPairFirst)
-          return ToPairFirst.takeError();
-        StringRef ToPairSecond = ImportASTStringRef(Pair->second);
-        ToSat.Details.emplace_back(new (Importer.getToContext())
-                                       ConstraintSubstitutionDiagnostic{
-                                           ToPairFirst.get(), ToPairSecond});
+        ExpectedSLoc ToLoc = import(Pair->first);
+        if (!ToLoc)
+          return ToLoc.takeError();
+        auto ToDiag = import(Pair->second);
+        if (!ToDiag)
+          return ToDiag.takeError();
+        ToSat.Details.emplace_back(
+            new (Importer.getToContext())
+                ConstraintSubstitutionDiagnostic(*ToLoc, *ToDiag));
       }
     }
   }
@@ -1111,14 +1210,33 @@ template <>
 Expected<concepts::Requirement::SubstitutionDiagnostic *>
 ASTNodeImporter::import(
     concepts::Requirement::SubstitutionDiagnostic *FromDiag) {
-  StringRef ToEntity = ImportASTStringRef(FromDiag->SubstitutedEntity);
+  llvm::PointerUnion<const ParmVarDecl *, const Expr *, const TypeSourceInfo *>
+      Entity;
+  if (auto *D = dyn_cast<const ParmVarDecl *>(FromDiag->Entity)) {
+    auto ToEntity = import(D);
+    if (!ToEntity)
+      return ToEntity.takeError();
+    Entity = *ToEntity;
+  } else if (auto *E = dyn_cast<const Expr *>(FromDiag->Entity)) {
+    auto ToEntity = import(E);
+    if (!ToEntity)
+      return ToEntity.takeError();
+    Entity = *ToEntity;
+  } else if (auto *TSI = dyn_cast<const TypeSourceInfo *>(FromDiag->Entity)) {
+    auto ToEntity = import(TSI);
+    if (!ToEntity)
+      return ToEntity.takeError();
+    Entity = *ToEntity;
+  }
   ExpectedSLoc ToLoc = import(FromDiag->DiagLoc);
   if (!ToLoc)
     return ToLoc.takeError();
-  StringRef ToDiagMessage = ImportASTStringRef(FromDiag->DiagMessage);
+  auto ToDiag = import(FromDiag->Diag);
+  if (!ToDiag)
+    return ToDiag.takeError();
   return new (Importer.getToContext())
-      concepts::Requirement::SubstitutionDiagnostic{ToEntity, ToLoc.get(),
-                                                    ToDiagMessage};
+      concepts::Requirement::SubstitutionDiagnostic{Entity, ToLoc.get(),
+                                                    *ToDiag};
 }
 
 Expected<concepts::Requirement *>
@@ -1205,12 +1323,14 @@ ASTNodeImporter::ImportNestedRequirement(concepts::NestedRequirement *From) {
   const ASTConstraintSatisfaction &FromSatisfaction =
       From->getConstraintSatisfaction();
   if (From->hasInvalidConstraint()) {
-    StringRef ToEntity = ImportASTStringRef(From->getInvalidConstraintEntity());
+    auto ToEntity = import(From->getInvalidConstraintEntity());
+    if (!ToEntity)
+      return ToEntity.takeError();
     ASTConstraintSatisfaction *ToSatisfaction =
         ASTConstraintSatisfaction::Rebuild(Importer.getToContext(),
                                            FromSatisfaction);
     return new (Importer.getToContext())
-        NestedRequirement(ToEntity, ToSatisfaction);
+        NestedRequirement(*ToEntity, ToSatisfaction);
   } else {
     ExpectedExpr ToExpr = import(From->getConstraintExpr());
     if (!ToExpr)
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 8831a26224e7d..f3aecd7144620 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -515,6 +515,7 @@ class HashParameterMapping : public RecursiveASTVisitor<HashParameterMapping> {
 
 class ConstraintSatisfactionChecker {
   Sema &S;
+  ASTContext &Context;
   const NamedDecl *Template;
   const ConceptReference *TopLevelConceptId;
   SourceLocation TemplateNameLoc;
@@ -580,8 +581,8 @@ class ConstraintSatisfactionChecker {
                                 UnsignedOrNone PackSubstitutionIndex,
                                 ConstraintSatisfaction &Satisfaction,
                                 bool BuildExpression)
-      : S(SemaRef), Template(Template), TopLevelConceptId(TopLevelConceptId),
-        TemplateNameLoc(TemplateNameLoc),
+      : S(SemaRef), Context(SemaRef.getASTContext()), Template(Template),
+        TopLevelConceptId(TopLevelConceptId), TemplateNameLoc(TemplateNameLoc),
         PackSubstitutionIndex(PackSubstitutionIndex),
         Satisfaction(Satisfaction), BuildExpression(BuildExpression) {}
 
@@ -589,14 +590,6 @@ class ConstraintSatisfactionChecker {
                       const MultiLevelTemplateArgumentList &MLTAL);
 };
 
-StringRef allocateStringFromConceptDiagnostic(const Sema &S,
-                                              const PartialDiagnostic Diag) {
-  SmallString<128> DiagString;
-  DiagString = ": ";
-  Diag.EmitToString(S.getDiagnostics(), DiagString);
-  return S.getASTContext().backupStr(DiagString);
-}
-
 } // namespace
 
 ExprResult ConstraintSatisfactionChecker::EvaluateAtomicConstraint(
@@ -639,16 +632,10 @@ ExprResult ConstraintSatisfactionChecker::EvaluateAtomicConstraint(
       PartialDiagnosticAt SubstDiag{SourceLocation(),
                                     PartialDiagnostic::NullDiagnostic()};
       Info.takeSFINAEDiagnostic(SubstDiag);
-      // FIXME: This is an unfortunate consequence of there
-      //  being no serialization code for PartialDiagnostics and the fact
-      //  that serializing them would likely take a lot more storage than
-      //  just storing them as strings. We would still like, in the
-      //  future, to serialize the proper PartialDiagnostic as serializing
-      //  it as a string defeats the purpose of the diagnostic mechanism.
       Satisfaction.Details.emplace_back(
-          new (S.Context) ConstraintSubstitutionDiagnostic{
+          new (Context) ConstraintSubstitutionDiagnostic(
               SubstDiag.first,
-              allocateStringFromConceptDiagnostic(S, SubstDiag.second)});
+              ASTPartialDiagnostic::Create(Context, SubstDiag.second)));
       Satisfaction.IsSatisfied = false;
       return ExprEmpty();
     }
@@ -805,11 +792,11 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
     Satisfaction.IsSatisfied = false;
     Satisfaction.ContainsErrors = true;
 
-    PartialDiagnostic Msg = S.PDiag(diag::note_constraint_references_error);
     Satisfaction.Details.emplace_back(
-        new (S.Context) ConstraintSubstitutionDiagnostic{
+        new (Context) ConstraintSubstitutionDiagnostic(
             SubstitutedAtomicExpr.get()->getBeginLoc(),
-            allocateStringFromConceptDiagnostic(S, Msg)});
+            ASTPartialDiagnostic::Create(
+                Context, S.PDiag(diag::note_constraint_references_error))));
     return SubstitutedAtomicExpr;
   }
 
@@ -1047,17 +1034,11 @@ ExprResult ConstraintSatisfactionChecker::EvaluateSlow(
     PartialDiagnosticAt SubstDiag{SourceLocation(),
                                   PartialDiagnostic::NullDiagnostic()};
     Info.takeSFINAEDiagnostic(SubstDiag);
-    // FIXME: This is an unfortunate consequence of there
-    //  being no serialization code for PartialDiagnostics and the fact
-    //  that serializing them would likely take a lot more storage than
-    //  just storing them as strings. We would still like, in the
-    //  future, to serialize the proper PartialDiagnostic as serializing
-    //  it as a string defeats the purpose of the diagnostic mechanism.
     Satisfaction.Details.insert(
         Satisfaction.Details.begin() + Size,
-        new (S.Context) ConstraintSubstitutionDiagnostic{
+        new (Context) ConstraintSubstitutionDiagnostic(
             SubstDiag.first,
-            allocateStringFromConceptDiagnostic(S, SubstDiag.second)});
+            ASTPartialDiagnostic::Create(Context, SubstDiag.second)));
     return ExprError();
   }
 
@@ -1814,6 +1795,37 @@ bool Sema::CheckFunctionTemplateConstraints(
                                      PointOfInstantiation, Satisfaction);
 }
 
+template <class DiagnoseBuilder, class Type>
+static void DiagnoseEntity(DiagnoseBuilder &DB, Type Entity) {
+  if (auto *D = dyn_cast<const ParmVarDecl *>(Entity)) {
+    DB << D;
+  } else if (auto *E = dyn_cast<const Expr *>(Entity)) {
+    DB << E;
+  } else if (auto *TSI = dyn_cast<const TypeSourceInfo *>(Entity)) {
+    DB << TSI->getType();
+  }
+}
+
+namespace {
+struct DiagnoseSubstitutionDiagnostic {
+  Sema &SemaRef;
+  const ASTPartialDiagnostic &PD;
+  DiagnoseSubstitutionDiagnostic(Sema &SemaRef, const ASTPartialDiagnostic &PD)
+      : SemaRef(SemaRef), PD(PD) {}
+
+  friend const StreamingDiagnostic &
+  operator<<(const StreamingDiagnostic &Diag,
+             const DiagnoseSubstitutionDiagnostic &This) {
+    SmallString<128> StringBuf;
+    This.PD
+        .getPartialDiagnostic(This.SemaRef.getASTContext().getDiagAllocator())
+        .EmitToString(This.SemaRef.getDiagnostics(), StringBuf);
+    Diag << StringBuf;
+    return Diag;
+  }
+};
+} // namespace
+
 static void diagnoseUnsatisfiedRequirement(Sema &S,
                                            concepts::ExprRequirement *Req,
                                            bool First) {
@@ -1825,15 +1837,14 @@ static void diagnoseUnsatisfiedRequirement(Sema &S,
     break;
   case concepts::ExprRequirement::SS_ExprSubstitutionFailure: {
     auto *SubstDiag = Req->getExprSubstitutionDiagnostic();
-    if (!SubstDiag->DiagMessage.empty())
-      S.Diag(SubstDiag->DiagLoc,
-             diag::note_expr_requirement_expr_substitution_error)
-          << (int)First << SubstDiag->SubstitutedEntity
-          << SubstDiag->DiagMessage;
-    else
-      S.Diag(SubstDiag->DiagLoc,
-             diag::note_expr_requirement_expr_unknown_substitution_error)
-          << (int)First << SubstDiag->SubstitutedEntity;
+    unsigned DiagId =
+        SubstDiag->Diag
+            ? diag::note_expr_requirement_expr_substitution_error
+            : diag::note_expr_requirement_expr_unknown_substitution_error;
+    auto DB = S.Diag(SubstDiag->DiagLoc, DiagId) << (int)First;
+    DiagnoseEntity(DB, SubstDiag->Entity);
+    if (SubstDiag->Diag)
+      DB << DiagnoseSubstitutionDiagnostic(S, *SubstDiag->Diag);
     break;
   }
   case concepts::ExprRequirement::SS_NoexceptNotMet:
@@ -1843,17 +1854,15 @@ static void diagnoseUnsatisfiedRequirement(Sema &S,
   case concepts::ExprRequirement::SS_TypeRequirementSubstitutionFailure: {
     auto *SubstDiag =
         Req->getReturnTypeRequirement().getSubstitutionDiagnostic();
-    if (!SubstDiag->DiagMessage.empty())
-      S.Diag(SubstDiag->DiagLoc,
-             diag::note_expr_requirement_type_requirement_substitution_error)
-          << (int)First << SubstDiag->SubstitutedEntity
-          << SubstDiag->DiagMessage;
-    else
-      S.Diag(
-          SubstDiag->DiagLoc,
-          diag::
-              note_expr_requirement_type_requirement_unknown_substitution_error)
-          << (int)First << SubstDiag->SubstitutedEntity;
+    unsigned DiagId =
+        SubstDiag->Diag
+            ? diag::note_expr_requirement_type_requirement_substitution_error
+            : diag::
+                  note_expr_requirement_type_requirement_unknown_substitution_error;
+    auto DB = S.Diag(SubstDiag->DiagLoc, DiagId) << (int)First;
+    DiagnoseEntity(DB, SubstDiag->Entity);
+    if (SubstDiag->Diag)
+      DB << DiagnoseSubstitutionDiagnostic(S, *SubstDiag->Diag);
     break;
   }
   case concepts::ExprRequirement::SS_ConstraintsNotSatisfied: {
@@ -1878,14 +1887,14 @@ static void diagnoseUnsatisfiedRequirement(Sema &S,
     return;
   case concepts::TypeRequirement::SS_SubstitutionFailure: {
     auto *SubstDiag = Req->getSubstitutionDiagnostic();
-    if (!SubstDiag->DiagMessage.empty())
-      S.Diag(SubstDiag->DiagLoc, diag::note_type_requirement_substitution_error)
-          << (int)First << SubstDiag->SubstitutedEntity
-          << SubstDiag->DiagMessage;
-    else
-      S.Diag(SubstDiag->DiagLoc,
-             diag::note_type_requirement_unknown_substitution_error)
-          << (int)First << SubstDiag->SubstitutedEntity;
+    unsigned DiagId =
+        SubstDiag->Diag
+            ? diag::note_type_requirement_substitution_error
+            : diag::note_type_requirement_unknown_substitution_error;
+    auto DB = S.Diag(SubstDiag->DiagLoc, DiagId) << (int)First;
+    DiagnoseEntity(DB, SubstDiag->Entity);
+    if (SubstDiag->Diag)
+      DB << DiagnoseSubstitutionDiagnostic(S, *SubstDiag->Diag);
     return;
   }
   default:
@@ -2041,10 +2050,11 @@ static void diagnoseUnsatisfiedConstraintExpr(
               .template dyn_cast<const ConstraintSubstitutionDiagnostic *>()) {
     if (Req)
       S.Diag(Diag->first, diag::note_nested_requirement_substitution_error)
-          << (int)First << Req->getInvalidConstraintEntity() << Diag->second;
+          << (int)First << Req->getInvalidConstraintEntity()
+          << DiagnoseSubstitutionDiagnostic(S, *Diag->second);
     else
       S.Diag(Diag->first, diag::note_substituted_constraint_expr_is_ill_formed)
-          << Diag->second;
+          << DiagnoseSubstitutionDiagnostic(S, *Diag->second);
     return;
   }
   if (const auto *Concept = dyn_cast<const ConceptReference *>(Record)) {
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 538604aa2e64b..52862b9c87a0a 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -8023,12 +8023,8 @@ Sema::BuildExprRequirement(
       // FIXME: Capture diagnostics from the SFINAE trap and store them in the
       // requirement.
       return new (Context) concepts::ExprRequirement(
-          createSubstDiagAt(IDC->getExprLoc(),
-                            [&](llvm::raw_ostream &OS) {
-                              IDC->printPretty(OS, /*Helper=*/nullptr,
-                                               getPrintingPolicy());
-                            }),
-          IsSimple, NoexceptLoc, ReturnTypeRequirement);
+          createSubstDiagAt(IDC->getExprLoc(), IDC), IsSimple, NoexceptLoc,
+          ReturnTypeRequirement);
     }
     if (!SubstitutedConstraintExpr->isSatisfied())
       Status = concepts::ExprRequirement::SS_ConstraintsNotSatisfied;
@@ -8078,8 +8074,8 @@ Sema::BuildNestedRequirement(Expr *Constraint) {
 }
 
 concepts::NestedRequirement *
-Sema::BuildNestedRequirement(StringRef InvalidConstraintEntity,
-                       const ASTConstraintSatisfaction &Satisfaction) {
+Sema::BuildNestedRequirement(Expr *InvalidConstraintEntity,
+                             const ASTConstraintSatisfaction &Satisfaction) {
   return new (Context) concepts::NestedRequirement(
       InvalidConstraintEntity,
       ASTConstraintSatisfaction::Rebuild(Context, Satisfaction));
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index bad0cdd1b7067..74acab34618bb 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -24,6 +24,7 @@
 #include "clang/AST/TypeLoc.h"
 #include "clang/AST/TypeVisitor.h"
 #include "clang/Basic/LangOptions.h"
+#include "clang/Basic/PartialDiagnostic.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/Sema/DeclSpec.h"
 #include "clang/Sema/EnterExpressionEvaluationContext.h"
@@ -2672,35 +2673,30 @@ QualType TemplateInstantiator::TransformSubstBuiltinTemplatePackType(
 
 static concepts::Requirement::SubstitutionDiagnostic *
 createSubstDiag(Sema &S, TemplateDeductionInfo &Info,
-                Sema::EntityPrinter Printer) {
-  SmallString<128> Message;
+                llvm::PointerUnion<const ParmVarDecl *, const Expr *,
+                                   const TypeSourceInfo *>
+                    Entity) {
   SourceLocation ErrorLoc;
+  const ASTContext &C = S.Context;
+  ASTPartialDiagnostic *Diag = nullptr;
   if (Info.hasSFINAEDiagnostic()) {
     PartialDiagnosticAt PDA(SourceLocation(),
                             PartialDiagnostic::NullDiagnostic{});
     Info.takeSFINAEDiagnostic(PDA);
-    PDA.second.EmitToString(S.getDiagnostics(), Message);
     ErrorLoc = PDA.first;
+    Diag = ASTPartialDiagnostic::Create(C, PDA.second);
   } else {
     ErrorLoc = Info.getLocation();
   }
-  SmallString<128> Entity;
-  llvm::raw_svector_ostream OS(Entity);
-  Printer(OS);
-  const ASTContext &C = S.Context;
-  return new (C) concepts::Requirement::SubstitutionDiagnostic{
-      C.backupStr(Entity), ErrorLoc, C.backupStr(Message)};
+  return new (C)
+      concepts::Requirement::SubstitutionDiagnostic{Entity, ErrorLoc, Diag};
 }
 
 concepts::Requirement::SubstitutionDiagnostic *
-Sema::createSubstDiagAt(SourceLocation Location, EntityPrinter Printer) {
-  SmallString<128> Entity;
-  llvm::raw_svector_ostream OS(Entity);
-  Printer(OS);
+Sema::createSubstDiagAt(SourceLocation Location, Expr *E) {
   const ASTContext &C = Context;
   return new (C) concepts::Requirement::SubstitutionDiagnostic{
-      /*SubstitutedEntity=*/C.backupStr(Entity),
-      /*DiagLoc=*/Location, /*DiagMessage=*/StringRef()};
+      /*Entity=*/E, /*DiagLoc=*/Location, /*Diag=*/nullptr};
 }
 
 ExprResult TemplateInstantiator::TransformRequiresTypeParams(
@@ -2724,8 +2720,8 @@ ExprResult TemplateInstantiator::TransformRequiresTypeParams(
     ParmVarDecl *FailedDecl = Params[ErrorIdx];
     // Add a 'failed' Requirement to contain the error that caused the failure
     // here.
-    TransReqs.push_back(RebuildTypeRequirement(createSubstDiag(
-        SemaRef, Info, [&](llvm::raw_ostream &OS) { OS << *FailedDecl; })));
+    TransReqs.push_back(
+        RebuildTypeRequirement(createSubstDiag(SemaRef, Info, FailedDecl)));
     return getDerived().RebuildRequiresExpr(KWLoc, Body, RE->getLParenLoc(),
                                             TransParams, RE->getRParenLoc(),
                                             TransReqs, RBraceLoc);
@@ -2754,10 +2750,8 @@ TemplateInstantiator::TransformTypeRequirement(concepts::TypeRequirement *Req) {
     return nullptr;
   TypeSourceInfo *TransType = TransformType(Req->getType());
   if (!TransType || Trap.hasErrorOccurred())
-    return RebuildTypeRequirement(createSubstDiag(SemaRef, Info,
-        [&] (llvm::raw_ostream& OS) {
-            Req->getType()->getType().print(OS, SemaRef.getPrintingPolicy());
-        }));
+    return RebuildTypeRequirement(
+        createSubstDiag(SemaRef, Info, Req->getType()));
   return RebuildTypeRequirement(TransType);
 }
 
@@ -2783,9 +2777,7 @@ TemplateInstantiator::TransformExprRequirement(concepts::ExprRequirement *Req) {
         TransExprRes.get()->hasPlaceholderType())
       TransExprRes = SemaRef.CheckPlaceholderExpr(TransExprRes.get());
     if (TransExprRes.isInvalid() || Trap.hasErrorOccurred())
-      TransExpr = createSubstDiag(SemaRef, Info, [&](llvm::raw_ostream &OS) {
-        E->printPretty(OS, nullptr, SemaRef.getPrintingPolicy());
-      });
+      TransExpr = createSubstDiag(SemaRef, Info, E);
     else
       TransExpr = TransExprRes.get();
   }
@@ -2807,11 +2799,9 @@ TemplateInstantiator::TransformExprRequirement(concepts::ExprRequirement *Req) {
       return nullptr;
     TemplateParameterList *TPL = TransformTemplateParameterList(OrigTPL);
     if (!TPL || Trap.hasErrorOccurred())
-      TransRetReq.emplace(createSubstDiag(SemaRef, Info,
-          [&] (llvm::raw_ostream& OS) {
-              RetReq.getTypeConstraint()->getImmediatelyDeclaredConstraint()
-                  ->printPretty(OS, nullptr, SemaRef.getPrintingPolicy());
-          }));
+      TransRetReq.emplace(createSubstDiag(
+          SemaRef, Info,
+          RetReq.getTypeConstraint()->getImmediatelyDeclaredConstraint()));
     else {
       TPLInst.Clear();
       TransRetReq.emplace(TPL);
@@ -2835,14 +2825,10 @@ TemplateInstantiator::TransformNestedRequirement(
   Expr *Constraint = Req->getConstraintExpr();
   ConstraintSatisfaction Satisfaction;
 
-  auto NestedReqWithDiag = [&C, this](Expr *E,
-                                      ConstraintSatisfaction Satisfaction) {
+  auto NestedReqWithDiag = [&C](Expr *E, ConstraintSatisfaction Satisfaction) {
     Satisfaction.IsSatisfied = false;
-    SmallString<128> Entity;
-    llvm::raw_svector_ostream OS(Entity);
-    E->printPretty(OS, nullptr, SemaRef.getPrintingPolicy());
     return new (C) concepts::NestedRequirement(
-        SemaRef.Context, C.backupStr(Entity), std::move(Satisfaction));
+        E, ASTConstraintSatisfaction::Create(C, Satisfaction));
   };
 
   if (Req->hasInvalidConstraint()) {
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index cc126f9000717..f974c9f905204 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -3848,7 +3848,7 @@ class TreeTransform {
   }
 
   concepts::NestedRequirement *
-  RebuildNestedRequirement(StringRef InvalidConstraintEntity,
+  RebuildNestedRequirement(Expr *InvalidConstraintEntity,
                            const ASTConstraintSatisfaction &Satisfaction) {
     return SemaRef.BuildNestedRequirement(InvalidConstraintEntity,
                                           Satisfaction);
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 87cec16a76323..c1547cf07c164 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -822,6 +822,80 @@ void ASTStmtReader::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E) {
   E->setRParenLoc(readSourceLocation());
 }
 
+static PartialDiagnostic
+readPartialDiagnostic(ASTRecordReader &Record,
+                      PartialDiagnostic::DiagStorageAllocator &Alloc) {
+  unsigned DiagID = Record.readUInt32();
+  PartialDiagnostic PD(DiagID, Alloc);
+  unsigned NumArgs = Record.readInt();
+  using ArgumentKind = DiagnosticsEngine::ArgumentKind;
+  for (unsigned I = 0; I != NumArgs; ++I) {
+    auto K = static_cast<ArgumentKind>(Record.readInt());
+    switch (K) {
+    case ArgumentKind::ak_std_string:
+    case ArgumentKind::ak_c_string:
+      PD.AddString(Record.readString());
+      break;
+    case ArgumentKind::ak_sint:
+    case ArgumentKind::ak_uint:
+    case ArgumentKind::ak_tokenkind:
+    case ArgumentKind::ak_addrspace:
+    case ArgumentKind::ak_qual:
+      PD.AddTaggedVal(Record.readUInt64(), K);
+      break;
+    case ArgumentKind::ak_qualtype:
+      PD.AddTaggedVal(
+          reinterpret_cast<uint64_t>(Record.readType().getAsOpaquePtr()), K);
+      break;
+    case ArgumentKind::ak_declarationname:
+      PD.AddTaggedVal(Record.readDeclarationName().getAsOpaqueInteger(), K);
+      break;
+    case ArgumentKind::ak_nameddecl:
+      PD.AddTaggedVal(
+          reinterpret_cast<uint64_t>(Record.readDeclAs<NamedDecl>()), K);
+      break;
+    case ArgumentKind::ak_nestednamespec:
+      PD.AddTaggedVal(reinterpret_cast<uint64_t>(
+                          Record.readNestedNameSpecifier().getAsVoidPointer()),
+                      K);
+      break;
+    case ArgumentKind::ak_declcontext:
+      PD.AddTaggedVal(reinterpret_cast<uint64_t>(
+                          Decl::castToDeclContext(Record.readDecl())),
+                      K);
+      break;
+    case ArgumentKind::ak_attr:
+      PD.AddTaggedVal(reinterpret_cast<uint64_t>(Record.readAttr()), K);
+      break;
+    case ArgumentKind::ak_expr:
+      PD.AddTaggedVal(reinterpret_cast<uint64_t>(Record.readExpr()), K);
+      break;
+    case ArgumentKind::ak_identifierinfo:
+      PD.AddTaggedVal(reinterpret_cast<uint64_t>(Record.readIdentifier()), K);
+      break;
+    case ArgumentKind::ak_qualtype_pair:
+    case ArgumentKind::ak_attr_info:
+      llvm_unreachable("unexpected diagnostic argument kind");
+    }
+  }
+  unsigned NumRanges = Record.readInt();
+  for (unsigned I = 0; I != NumRanges; ++I) {
+    SourceRange SR = Record.readSourceRange();
+    bool IsTokenRange = Record.readInt();
+    PD.AddSourceRange(CharSourceRange(SR, IsTokenRange));
+  }
+  // FIXME: Do we need to serialize FixItHints?
+  return PD;
+}
+
+static ASTPartialDiagnostic *readASTPartialDiagnostic(const ASTContext &C,
+                                                      ASTRecordReader &Record) {
+  if (!Record.readInt())
+    return nullptr;
+  PartialDiagnostic::DiagStorageAllocator Alloc;
+  return ASTPartialDiagnostic::Create(C, readPartialDiagnostic(Record, Alloc));
+}
+
 static ConstraintSatisfaction
 readConstraintSatisfaction(ASTRecordReader &Record) {
   ConstraintSatisfaction Satisfaction;
@@ -833,11 +907,10 @@ readConstraintSatisfaction(ASTRecordReader &Record) {
     for (unsigned i = 0; i != NumDetailRecords; ++i) {
       auto Kind = Record.readInt();
       if (Kind == 0) {
-        SourceLocation DiagLocation = Record.readSourceLocation();
-        StringRef DiagMessage = C.backupStr(Record.readString());
-
-        Satisfaction.Details.emplace_back(new (
-            C) ConstraintSubstitutionDiagnostic(DiagLocation, DiagMessage));
+        SourceLocation Loc = Record.readSourceLocation();
+        auto *Diag = readASTPartialDiagnostic(C, Record);
+        Satisfaction.Details.emplace_back(
+            new (C) ConstraintSubstitutionDiagnostic(Loc, Diag));
       } else if (Kind == 1) {
         Satisfaction.Details.emplace_back(Record.readExpr());
       } else {
@@ -863,13 +936,21 @@ void ASTStmtReader::VisitConceptSpecializationExpr(
 static concepts::Requirement::SubstitutionDiagnostic *
 readSubstitutionDiagnostic(ASTRecordReader &Record) {
   const ASTContext &C = Record.getContext();
-  StringRef SubstitutedEntity = C.backupStr(Record.readString());
+  unsigned Type = Record.readUInt32();
+  llvm::PointerUnion<const ParmVarDecl *, const Expr *, const TypeSourceInfo *>
+      Entity;
+  if (Type == 0) {
+    Entity = cast<ParmVarDecl>(Record.readNamedDeclRef());
+  } else if (Type == 1) {
+    Entity = Record.readExpr();
+  } else if (Type == 2) {
+    Entity = Record.readTypeSourceInfo();
+  }
   SourceLocation DiagLoc = Record.readSourceLocation();
-  StringRef DiagMessage = C.backupStr(Record.readString());
-
+  ASTPartialDiagnostic *Diagnostic = readASTPartialDiagnostic(C, Record);
   return new (Record.getContext())
-      concepts::Requirement::SubstitutionDiagnostic{SubstitutedEntity, DiagLoc,
-                                                    DiagMessage};
+      concepts::Requirement::SubstitutionDiagnostic{Entity, DiagLoc,
+                                                    Diagnostic};
 }
 
 void ASTStmtReader::VisitRequiresExpr(RequiresExpr *E) {
@@ -954,10 +1035,11 @@ void ASTStmtReader::VisitRequiresExpr(RequiresExpr *E) {
         ASTContext &C = Record.getContext();
         bool HasInvalidConstraint = Record.readInt();
         if (HasInvalidConstraint) {
-          StringRef InvalidConstraint = C.backupStr(Record.readString());
+          Expr *InvalidConstraint = Record.readExpr();
+          auto Satisfaction = readConstraintSatisfaction(Record);
           R = new (C) concepts::NestedRequirement(
-              Record.getContext(), InvalidConstraint,
-              readConstraintSatisfaction(Record));
+              InvalidConstraint,
+              ASTConstraintSatisfaction::Create(C, Satisfaction));
           break;
         }
         Expr *E = Record.readExpr();
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index 70477f4cf4001..67ff08be4b1de 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -487,6 +487,81 @@ void ASTStmtWriter::VisitDependentCoawaitExpr(DependentCoawaitExpr *E) {
   Code = serialization::EXPR_DEPENDENT_COAWAIT;
 }
 
+static void addPartialDiagnostic(ASTRecordWriter &Record,
+                                 const PartialDiagnostic &PD) {
+  Record.push_back(PD.getDiagID());
+  const DiagnosticStorage *S = PD.hasStorage() ? PD.getStorage() : nullptr;
+  unsigned NumArgs = S ? S->NumDiagArgs : 0;
+  Record.push_back(NumArgs);
+  using ArgumentKind = DiagnosticsEngine::ArgumentKind;
+  for (unsigned I = 0; I != NumArgs; ++I) {
+    auto K = (ArgumentKind)S->DiagArgumentsKind[I];
+    uint64_t Val = S->DiagArgumentsVal[I];
+    Record.push_back(K);
+    switch (K) {
+    case ArgumentKind::ak_std_string:
+      Record.AddString(S->DiagArgumentsStr[I]);
+      break;
+    case ArgumentKind::ak_c_string:
+      Record.AddString(reinterpret_cast<const char *>(Val));
+      break;
+    case ArgumentKind::ak_sint:
+    case ArgumentKind::ak_uint:
+    case ArgumentKind::ak_tokenkind:
+    case ArgumentKind::ak_addrspace:
+    case ArgumentKind::ak_qual:
+      Record.push_back(Val);
+      break;
+    case ArgumentKind::ak_qualtype:
+      Record.AddTypeRef(
+          QualType::getFromOpaquePtr(reinterpret_cast<void *>(Val)));
+      break;
+    case ArgumentKind::ak_declarationname:
+      Record.AddDeclarationName(DeclarationName::getFromOpaqueInteger(Val));
+      break;
+    case ArgumentKind::ak_nameddecl:
+      Record.AddDeclRef(reinterpret_cast<NamedDecl *>(Val));
+      break;
+    case ArgumentKind::ak_nestednamespec:
+      Record.AddNestedNameSpecifier(NestedNameSpecifier::getFromVoidPointer(
+          reinterpret_cast<void *>(Val)));
+      break;
+    case ArgumentKind::ak_declcontext:
+      Record.AddDeclRef(
+          Decl::castFromDeclContext(reinterpret_cast<DeclContext *>(Val)));
+      break;
+    case ArgumentKind::ak_attr:
+      Record.AddAttr(reinterpret_cast<Attr *>(Val));
+      break;
+    case ArgumentKind::ak_expr:
+      Record.AddStmt(reinterpret_cast<Expr *>(Val));
+      break;
+    case ArgumentKind::ak_identifierinfo:
+      Record.AddIdentifierRef(reinterpret_cast<IdentifierInfo *>(Val));
+      break;
+    case ArgumentKind::ak_qualtype_pair:
+    case ArgumentKind::ak_attr_info:
+      llvm_unreachable("unexpected diagnostic argument kind");
+    }
+  }
+  unsigned NumRanges = S ? S->DiagRanges.size() : 0;
+  Record.push_back(NumRanges);
+  for (unsigned I = 0; I != NumRanges; ++I) {
+    Record.AddSourceRange(S->DiagRanges[I].getAsRange());
+    Record.push_back(S->DiagRanges[I].isTokenRange());
+  }
+  // FIXME: Do we need to serialize FixItHints?
+}
+
+static void addASTPartialDiagnostic(ASTRecordWriter &Record,
+                                    const ASTPartialDiagnostic *PD) {
+  Record.push_back(PD != nullptr);
+  if (!PD)
+    return;
+  PartialDiagnostic::DiagStorageAllocator Alloc;
+  addPartialDiagnostic(Record, PD->getPartialDiagnostic(Alloc));
+}
+
 static void
 addConstraintSatisfaction(ASTRecordWriter &Record,
                           const ASTConstraintSatisfaction &Satisfaction) {
@@ -499,7 +574,7 @@ addConstraintSatisfaction(ASTRecordWriter &Record,
               DetailRecord)) {
         Record.push_back(/*Kind=*/0);
         Record.AddSourceLocation(Diag->first);
-        Record.AddString(Diag->second);
+        addASTPartialDiagnostic(Record, Diag->second);
         continue;
       }
       if (auto *E = dyn_cast<const Expr *>(DetailRecord)) {
@@ -514,13 +589,21 @@ addConstraintSatisfaction(ASTRecordWriter &Record,
   }
 }
 
-static void
-addSubstitutionDiagnostic(
+static void addSubstitutionDiagnostic(
     ASTRecordWriter &Record,
-    const concepts::Requirement::SubstitutionDiagnostic *D) {
-  Record.AddString(D->SubstitutedEntity);
-  Record.AddSourceLocation(D->DiagLoc);
-  Record.AddString(D->DiagMessage);
+    const concepts::Requirement::SubstitutionDiagnostic *Diag) {
+  if (auto *D = dyn_cast<const ParmVarDecl *>(Diag->Entity)) {
+    Record.push_back(0);
+    Record.AddDeclRef(D);
+  } else if (auto *E = dyn_cast<const Expr *>(Diag->Entity)) {
+    Record.push_back(1);
+    Record.AddStmt(const_cast<Expr *>(E));
+  } else if (auto *TSI = dyn_cast<const TypeSourceInfo *>(Diag->Entity)) {
+    Record.push_back(2);
+    Record.AddTypeSourceInfo(const_cast<TypeSourceInfo *>(TSI));
+  }
+  Record.AddSourceLocation(Diag->DiagLoc);
+  addASTPartialDiagnostic(Record, Diag->Diag);
 }
 
 void ASTStmtWriter::VisitConceptSpecializationExpr(
@@ -587,7 +670,7 @@ void ASTStmtWriter::VisitRequiresExpr(RequiresExpr *E) {
       Record.push_back(concepts::Requirement::RK_Nested);
       Record.push_back(NestedReq->hasInvalidConstraint());
       if (NestedReq->hasInvalidConstraint()) {
-        Record.AddString(NestedReq->getInvalidConstraintEntity());
+        Record.AddStmt(NestedReq->getInvalidConstraintEntity());
         addConstraintSatisfaction(Record, *NestedReq->Satisfaction);
       } else {
         Record.AddStmt(NestedReq->getConstraintExpr());
diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.req/type-requirement.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.req/type-requirement.cpp
index 28dff336d053c..4f9b4f9de2a4d 100644
--- a/clang/test/CXX/expr/expr.prim/expr.prim.req/type-requirement.cpp
+++ b/clang/test/CXX/expr/expr.prim/expr.prim.req/type-requirement.cpp
@@ -165,7 +165,7 @@ static_assert(requires { typename Z<int>; });
 namespace std_example {
   template<typename T, typename T::type = 0> struct S;
   // expected-note at -1 {{because 'typename S<T>' would be invalid: no type named 'type' in 'std_example::has_inner}}
-  template<typename T> using Ref = T&; // expected-note{{because 'typename Ref<T>' would be invalid: cannot form a reference to 'void'}}
+  template<typename T> using Ref = T&; // expected-note{{because 'typename Ref<T>' (aka 'T &') would be invalid: cannot form a reference to 'void'}}
   template<typename T> concept C1 =
     requires {
       typename T::inner;



More information about the cfe-commits mailing list