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 <[email protected]> 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@-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; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
