https://github.com/zyn0217 updated https://github.com/llvm/llvm-project/pull/230369
>From 4c24d35b9592a815d31ed590deadaa36d0414396 Mon Sep 17 00:00:00 2001 From: Younan Zhang <[email protected]> Date: Fri, 9 Oct 2026 12:32:41 +0800 Subject: [PATCH 1/2] [Clang] Improve ConstraintSatisfaction --- clang/include/clang/AST/ASTConcept.h | 49 +++++++++---------- clang/include/clang/AST/ExprConcepts.h | 4 +- clang/include/clang/Sema/Sema.h | 7 +-- clang/lib/AST/ASTConcept.cpp | 12 ++--- clang/lib/AST/ASTImporter.cpp | 4 +- clang/lib/AST/ComputeDependence.cpp | 2 +- clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp | 2 +- clang/lib/Sema/Sema.cpp | 8 +-- clang/lib/Sema/SemaConcept.cpp | 12 ++--- clang/lib/Sema/SemaDeclCXX.cpp | 2 +- clang/lib/Sema/SemaExprCXX.cpp | 10 +--- clang/lib/Sema/TreeTransform.h | 4 +- clang/lib/Serialization/ASTWriterStmt.cpp | 8 +-- 13 files changed, 52 insertions(+), 72 deletions(-) diff --git a/clang/include/clang/AST/ASTConcept.h b/clang/include/clang/AST/ASTConcept.h index 62fe2c0f653d57..71a16931d6b3d0 100644 --- a/clang/include/clang/AST/ASTConcept.h +++ b/clang/include/clang/AST/ASTConcept.h @@ -44,31 +44,15 @@ using UnsatisfiedConstraintRecord = /// The result of a constraint satisfaction check, containing the necessary /// information to diagnose an unsatisfied constraint. -class ConstraintSatisfaction : public llvm::FoldingSetNode { - // The template-like entity that 'owns' the constraint checked here (can be a - // constrained entity or a concept). - const NamedDecl *ConstraintOwner = nullptr; - llvm::SmallVector<TemplateArgument, 4> TemplateArgs; - +class ConstraintSatisfaction { public: - - ConstraintSatisfaction() = default; - - ConstraintSatisfaction(const NamedDecl *ConstraintOwner, - ArrayRef<TemplateArgument> TemplateArgs) - : ConstraintOwner(ConstraintOwner), TemplateArgs(TemplateArgs) {} - bool IsSatisfied = false; bool ContainsErrors = false; /// \brief The substituted constraint expr, if the template arguments could be /// substituted into them, or a diagnostic if substitution resulted in an /// invalid expression. - llvm::SmallVector<UnsatisfiedConstraintRecord, 4> Details; - - void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &C) { - Profile(ID, C, ConstraintOwner, TemplateArgs); - } + llvm::SmallVector<UnsatisfiedConstraintRecord, 1> Details; static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &C, const NamedDecl *ConstraintOwner, @@ -86,13 +70,24 @@ class ConstraintSatisfaction : public llvm::FoldingSetNode { /// necessary information to diagnose an unsatisfied constraint. /// /// This is safe to store in an AST node, as opposed to ConstraintSatisfaction. -struct ASTConstraintSatisfaction final : - llvm::TrailingObjects<ASTConstraintSatisfaction, - UnsatisfiedConstraintRecord> { - std::size_t NumRecords; +class ASTConstraintSatisfaction final + : llvm::TrailingObjects<ASTConstraintSatisfaction, + UnsatisfiedConstraintRecord> { + friend TrailingObjects; + friend class ASTStmtWriter; + friend class ASTStmtReader; + + unsigned NumRecords : 30; bool IsSatisfied : 1; bool ContainsErrors : 1; + ASTConstraintSatisfaction(const ASTContext &C, + const ConstraintSatisfaction &Satisfaction); + + ASTConstraintSatisfaction(const ASTContext &C, + const ASTConstraintSatisfaction &Satisfaction); + +public: const UnsatisfiedConstraintRecord *begin() const { return getTrailingObjects(); } @@ -105,13 +100,15 @@ struct ASTConstraintSatisfaction final : return {begin(), end()}; } - ASTConstraintSatisfaction(const ASTContext &C, - const ConstraintSatisfaction &Satisfaction); - ASTConstraintSatisfaction(const ASTContext &C, - const ASTConstraintSatisfaction &Satisfaction); + bool isSatisfied() const { return IsSatisfied; } + + bool containsErrors() const { return ContainsErrors; } + + unsigned record_size() const { return NumRecords; } static ASTConstraintSatisfaction * Create(const ASTContext &C, const ConstraintSatisfaction &Satisfaction); + static ASTConstraintSatisfaction * Rebuild(const ASTContext &C, const ASTConstraintSatisfaction &Satisfaction); }; diff --git a/clang/include/clang/AST/ExprConcepts.h b/clang/include/clang/AST/ExprConcepts.h index 18ce5a94ebef3c..7d977069e80964 100644 --- a/clang/include/clang/AST/ExprConcepts.h +++ b/clang/include/clang/AST/ExprConcepts.h @@ -128,7 +128,7 @@ class ConceptSpecializationExpr final : public Expr { bool isSatisfied() const { assert(!isValueDependent() && "isSatisfied called on a dependent ConceptSpecializationExpr"); - return Satisfaction->IsSatisfied; + return Satisfaction->isSatisfied(); } /// \brief Get elaborated satisfaction info about the template arguments' @@ -463,7 +463,7 @@ class NestedRequirement : public Requirement { : Requirement(RK_Nested, /*IsDependent=*/false, /*ContainsUnexpandedParameterPack*/ false, - Satisfaction->IsSatisfied), + Satisfaction->isSatisfied()), Satisfaction(Satisfaction), HasInvalidConstraint(true), InvalidConstraintEntity(InvalidConstraintEntity) {} diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 0864337a9374c6..e33d15c11cd757 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -115,7 +115,7 @@ struct InlineAsmIdentifierInfo; namespace clang { class ADLResult; class APValue; -struct ASTConstraintSatisfaction; +class ASTConstraintSatisfaction; class ASTConsumer; class ASTContext; class ASTDeclReader; @@ -8883,9 +8883,6 @@ class Sema final : public SemaBase { concepts::TypeRequirement *BuildTypeRequirement( concepts::Requirement::SubstitutionDiagnostic *SubstDiag); concepts::NestedRequirement *BuildNestedRequirement(Expr *E); - concepts::NestedRequirement * - BuildNestedRequirement(StringRef InvalidConstraintEntity, - const ASTConstraintSatisfaction &Satisfaction); ExprResult ActOnRequiresExpr(SourceLocation RequiresKWLoc, RequiresExprBodyDecl *Body, SourceLocation LParenLoc, @@ -15165,7 +15162,7 @@ class Sema final : public SemaBase { /// Cache whether the associated constraint of a declaration /// is satisfied. - llvm::ContextualFoldingSet<ConstraintSatisfaction, const ASTContext &> + llvm::DenseMap<llvm::FoldingSetNodeID, ConstraintSatisfaction *> SatisfactionCache; // The current stack of constraint satisfactions, so we can exit-early. diff --git a/clang/lib/AST/ASTConcept.cpp b/clang/lib/AST/ASTConcept.cpp index 2b04821a157da4..ee8ef848cebe09 100644 --- a/clang/lib/AST/ASTConcept.cpp +++ b/clang/lib/AST/ASTConcept.cpp @@ -43,9 +43,9 @@ CreateUnsatisfiedConstraintRecord(const ASTContext &C, ASTConstraintSatisfaction::ASTConstraintSatisfaction( const ASTContext &C, const ConstraintSatisfaction &Satisfaction) - : NumRecords{Satisfaction.Details.size()}, - IsSatisfied{Satisfaction.IsSatisfied}, ContainsErrors{ - Satisfaction.ContainsErrors} { + : NumRecords(Satisfaction.Details.size()), + IsSatisfied(Satisfaction.IsSatisfied), + ContainsErrors(Satisfaction.ContainsErrors) { for (unsigned I = 0; I < NumRecords; ++I) CreateUnsatisfiedConstraintRecord(C, Satisfaction.Details[I], getTrailingObjects() + I); @@ -53,9 +53,9 @@ ASTConstraintSatisfaction::ASTConstraintSatisfaction( ASTConstraintSatisfaction::ASTConstraintSatisfaction( const ASTContext &C, const ASTConstraintSatisfaction &Satisfaction) - : NumRecords{Satisfaction.NumRecords}, - IsSatisfied{Satisfaction.IsSatisfied}, - ContainsErrors{Satisfaction.ContainsErrors} { + : NumRecords(Satisfaction.NumRecords), + IsSatisfied(Satisfaction.IsSatisfied), + ContainsErrors(Satisfaction.ContainsErrors) { for (unsigned I = 0; I < NumRecords; ++I) CreateUnsatisfiedConstraintRecord(C, *(Satisfaction.begin() + I), getTrailingObjects() + I); diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 22c3af47f451f6..10ee9b75afec0f 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -1075,8 +1075,8 @@ StringRef ASTNodeImporter::ImportASTStringRef(StringRef FromStr) { Error ASTNodeImporter::ImportConstraintSatisfaction( const ASTConstraintSatisfaction &FromSat, ConstraintSatisfaction &ToSat) { - ToSat.IsSatisfied = FromSat.IsSatisfied; - ToSat.ContainsErrors = FromSat.ContainsErrors; + ToSat.IsSatisfied = FromSat.isSatisfied(); + ToSat.ContainsErrors = FromSat.containsErrors(); if (!ToSat.IsSatisfied) { for (auto Record = FromSat.begin(); Record != FromSat.end(); ++Record) { if (const Expr *E = Record->dyn_cast<const Expr *>()) { diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp index 7ea1c67ff77389..c4bf3681a79122 100644 --- a/clang/lib/AST/ComputeDependence.cpp +++ b/clang/lib/AST/ComputeDependence.cpp @@ -955,7 +955,7 @@ ExprDependence clang::computeDependence(ConceptSpecializationExpr *E, ExprDependence D = ValueDependent ? ExprDependence::Value : ExprDependence::None; auto Res = D | toExprDependence(TA); - if(!ValueDependent && E->getSatisfaction().ContainsErrors) + if (!ValueDependent && E->getSatisfaction().containsErrors()) Res |= ExprDependence::Error; return Res; } diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp index e49b1cba7b1dde..54d5e3eba66af8 100644 --- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp +++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp @@ -433,7 +433,7 @@ TemplateParameterListBuilder::constructConceptSpecializationExpr( // Constraint satisfaction is used to construct the // ConceptSpecailizationExpr, and represents the 2nd Template Argument, // located at the bottom of the sample AST above. - const ConstraintSatisfaction CS(CD, {ConceptTA}); + ConstraintSatisfaction CS; TemplateArgumentLoc TAL = S.getTrivialTemplateArgumentLoc(ConceptTA, QualType(), SourceLocation()); diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index 21f71d7f8b40e2..25ead1308c537a 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -328,7 +328,7 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, GlobalNewDeleteDeclared(false), DisableTypoCorrection(false), TyposCorrected(0), IsBuildingRecoveryCallExpr(false), CurrentInstantiationScope(nullptr), NonInstantiationEntries(0), - ArgPackSubstIndex(std::nullopt), SatisfactionCache(Context) { + ArgPackSubstIndex(std::nullopt) { assert(pp.TUKind == TUKind); TUScope = nullptr; @@ -626,11 +626,7 @@ Sema::~Sema() { ExternalSema->ForgetSema(); // Delete cached satisfactions. - std::vector<ConstraintSatisfaction *> Satisfactions; - Satisfactions.reserve(SatisfactionCache.size()); - for (auto &Node : SatisfactionCache) - Satisfactions.push_back(&Node); - for (auto *Node : Satisfactions) + for (auto [_, Node] : SatisfactionCache) delete Node; threadSafety::threadSafetyCleanup(ThreadSafetyDeclCache); diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index fb66bdd6d2bd04..15b6b9c0c8b396 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -1323,14 +1323,12 @@ bool Sema::CheckConstraintSatisfaction( llvm::FoldingSetNodeID ID; ConstraintSatisfaction::Profile(ID, Context, Owner, FlattenedArgs); - llvm::FoldingSetInsertToken Token; - if (auto *Cached = SatisfactionCache.lookup(ID, Token)) { + if (auto *Cached = SatisfactionCache.lookup(ID)) { OutSatisfaction = *Cached; return false; } - auto Satisfaction = - std::make_unique<ConstraintSatisfaction>(Owner, FlattenedArgs); + auto Satisfaction = std::make_unique<ConstraintSatisfaction>(); if (::CheckConstraintSatisfaction( *this, Template, AssociatedConstraints, TemplateArgsLists, TemplateIDRange, *Satisfaction, ConvertedExpr, TopLevelConceptId)) { @@ -1338,7 +1336,7 @@ bool Sema::CheckConstraintSatisfaction( return true; } - if (auto *Cached = SatisfactionCache.lookup(ID, Token)) { + if (auto *Cached = SatisfactionCache.lookup(ID)) { // The evaluation of this constraint resulted in us trying to re-evaluate it // recursively. This isn't really possible, except we try to form a // RecoveryExpr as a part of the evaluation. If this is the case, just @@ -1354,7 +1352,7 @@ bool Sema::CheckConstraintSatisfaction( // Else we can simply add this satisfaction to the list. OutSatisfaction = *Satisfaction; // Note that entries of SatisfactionCache are deleted in Sema's destructor. - SatisfactionCache.insert(Satisfaction.release()); + SatisfactionCache.insert({ID, Satisfaction.release()}); return false; } @@ -2060,7 +2058,7 @@ void Sema::DiagnoseUnsatisfiedConstraint( const ASTConstraintSatisfaction &Satisfaction = ConstraintExpr->getSatisfaction(); - assert(!Satisfaction.IsSatisfied && + assert(!Satisfaction.isSatisfied() && "Attempted to diagnose a satisfied constraint"); ::DiagnoseUnsatisfiedConstraint(*this, Satisfaction.records(), diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index ea628f29d8a005..af8e53d4aea2ed 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -18092,7 +18092,7 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc, dyn_cast_or_null<ConceptSpecializationExpr>(InnerCond)) { const ASTConstraintSatisfaction &Satisfaction = ConceptIDExpr->getSatisfaction(); - if (!Satisfaction.ContainsErrors || Satisfaction.NumRecords) { + if (!Satisfaction.containsErrors() || Satisfaction.record_size()) { Diag(AssertExpr->getBeginLoc(), diag::err_static_assert_failed) << !HasMessage << Msg.str() << AssertExpr->getSourceRange(); // Drill down into concept specialization expressions to see why they diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index deb7bdd09fdc7d..e65b8240255c96 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -8121,7 +8121,7 @@ Sema::BuildExprRequirement( if (!HasError) { SubstitutedConstraintExpr = cast<ConceptSpecializationExpr>(Constraint.get()); - if (SubstitutedConstraintExpr->getSatisfaction().ContainsErrors) + if (SubstitutedConstraintExpr->getSatisfaction().containsErrors()) HasError = true; } if (HasError) { @@ -8191,14 +8191,6 @@ Sema::BuildNestedRequirement(Expr *Constraint) { Satisfaction); } -concepts::NestedRequirement * -Sema::BuildNestedRequirement(StringRef InvalidConstraintEntity, - const ASTConstraintSatisfaction &Satisfaction) { - return new (Context) concepts::NestedRequirement( - InvalidConstraintEntity, - ASTConstraintSatisfaction::Rebuild(Context, Satisfaction)); -} - RequiresExprBodyDecl * Sema::ActOnStartRequiresExpr(SourceLocation RequiresKWLoc, ArrayRef<ParmVarDecl *> LocalParameters, diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index c8458fda58a88e..841228d9272050 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -3872,8 +3872,8 @@ class TreeTransform { concepts::NestedRequirement * RebuildNestedRequirement(StringRef InvalidConstraintEntity, const ASTConstraintSatisfaction &Satisfaction) { - return SemaRef.BuildNestedRequirement(InvalidConstraintEntity, - Satisfaction); + return new (getSema().Context) + concepts::NestedRequirement(InvalidConstraintEntity, &Satisfaction); } concepts::NestedRequirement *RebuildNestedRequirement(Expr *Constraint) { diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp index 513c832c38ffd9..dea4d34fb73f80 100644 --- a/clang/lib/Serialization/ASTWriterStmt.cpp +++ b/clang/lib/Serialization/ASTWriterStmt.cpp @@ -490,10 +490,10 @@ void ASTStmtWriter::VisitDependentCoawaitExpr(DependentCoawaitExpr *E) { static void addConstraintSatisfaction(ASTRecordWriter &Record, const ASTConstraintSatisfaction &Satisfaction) { - Record.push_back(Satisfaction.IsSatisfied); - Record.push_back(Satisfaction.ContainsErrors); - if (!Satisfaction.IsSatisfied) { - Record.push_back(Satisfaction.NumRecords); + Record.push_back(Satisfaction.isSatisfied()); + Record.push_back(Satisfaction.containsErrors()); + if (!Satisfaction.isSatisfied()) { + Record.push_back(Satisfaction.record_size()); for (const auto &DetailRecord : Satisfaction) { if (auto *Diag = dyn_cast<const ConstraintSubstitutionDiagnostic *>( DetailRecord)) { >From 2e21c48624922a0d57d1f56e6f16119f4a7150a9 Mon Sep 17 00:00:00 2001 From: Younan Zhang <[email protected]> Date: Fri, 9 Oct 2026 19:09:26 +0800 Subject: [PATCH 2/2] Remove duplicate cache lookup --- clang/lib/Sema/SemaConcept.cpp | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index 15b6b9c0c8b396..b02faa5f8ff957 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -597,9 +597,11 @@ class ConstraintSatisfactionChecker { auto &Cached = Iter->second.Satisfaction; Satisfaction.ContainsErrors = Cached.ContainsErrors; Satisfaction.IsSatisfied = Cached.IsSatisfied; - Satisfaction.Details.insert(Satisfaction.Details.begin() + - PreviousDetailsSize, - Cached.Details.begin(), Cached.Details.end()); + SmallVector<UnsatisfiedConstraintRecord, 1> Details( + Satisfaction.Details.begin(), + Satisfaction.Details.begin() + PreviousDetailsSize); + Details.append(Cached.Details.begin(), Cached.Details.end()); + std::swap(Details, Satisfaction.Details); return &Iter->second; } @@ -1336,23 +1338,12 @@ bool Sema::CheckConstraintSatisfaction( return true; } - if (auto *Cached = SatisfactionCache.lookup(ID)) { - // The evaluation of this constraint resulted in us trying to re-evaluate it - // recursively. This isn't really possible, except we try to form a - // RecoveryExpr as a part of the evaluation. If this is the case, just - // return the 'cached' version (which will have the same result), and save - // ourselves the extra-insert. If it ever becomes possible to legitimately - // recursively check a constraint, we should skip checking the 'inner' one - // above, and replace the cached version with this one, as it would be more - // specific. - OutSatisfaction = *Cached; - return false; - } - // Else we can simply add this satisfaction to the list. OutSatisfaction = *Satisfaction; // Note that entries of SatisfactionCache are deleted in Sema's destructor. - SatisfactionCache.insert({ID, Satisfaction.release()}); + auto [Iter, Inserted] = SatisfactionCache.insert({ID, nullptr}); + if (Inserted) + Iter->second = Satisfaction.release(); return false; } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
