[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