[clang] [Clang][WIP] Normalize constraints before checking for satisfaction (PR #141776)

via cfe-commits cfe-commits at lists.llvm.org
Fri May 30 05:46:41 PDT 2025


https://github.com/cor3ntin updated https://github.com/llvm/llvm-project/pull/141776

>From 7a832e48d4750a248c0fc4ac684f5e6f156a7dec Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Wed, 28 May 2025 16:15:22 +0200
Subject: [PATCH 1/2] [Clang][WIP] Normalize constraints before checking for
 satisfaction

In the standard, constraint satisfaction checking is done on the normalized
form of a constraint.

Clang instead substitute on the non-normalized form, which cause us
to report substitution failures in template arguments or concept ids,
which is non-conforming but unavoidable witjout a parameter mapping

This patch normalizes before satisfaction checking.
However, we preserve concept-id nodes in the normalized form,
solely for diagnostics purposes.

This is a very incomplete attempt at addressing #61811
and related concepts related conformance bugs, ideally
to make the implementation of concept template parameters easier

There is stil ~20 failing test files, mostly caused by poor
adjustement of the template depth.
---
 clang/include/clang/AST/ASTConcept.h          |    6 +-
 clang/include/clang/AST/TemplateBase.h        |    1 -
 clang/include/clang/Sema/Sema.h               |   65 +-
 clang/include/clang/Sema/SemaConcept.h        |  379 ++++--
 clang/include/clang/Sema/Template.h           |   21 +-
 clang/lib/AST/ASTConcept.cpp                  |   11 +-
 clang/lib/AST/ASTContext.cpp                  |    1 +
 clang/lib/Sema/SemaConcept.cpp                | 1171 +++++++++--------
 clang/lib/Sema/SemaDeclCXX.cpp                |   16 +-
 clang/lib/Sema/SemaExprCXX.cpp                |   11 +-
 clang/lib/Sema/SemaTemplate.cpp               |   37 +-
 clang/lib/Sema/SemaTemplateDeduction.cpp      |    8 +
 clang/lib/Sema/SemaTemplateInstantiate.cpp    | 1051 ++++++++-------
 clang/lib/Sema/SemaTemplateVariadic.cpp       |    2 +
 clang/lib/Sema/TreeTransform.h                |   42 +-
 clang/lib/Serialization/ASTWriterStmt.cpp     |    5 +-
 clang/test/CXX/drs/cwg25xx.cpp                |   12 +-
 .../CXX/expr/expr.prim/expr.prim.id/p3.cpp    |    4 +-
 .../constrant-satisfaction-conversions.cpp    |    3 +-
 .../temp.constr/temp.constr.normal/p1.cpp     |   10 +-
 clang/test/SemaCXX/cxx23-assume.cpp           |   10 +-
 .../SemaTemplate/concepts-recovery-expr.cpp   |   32 +-
 .../instantiate-expanded-type-constraint.cpp  |    1 -
 23 files changed, 1669 insertions(+), 1230 deletions(-)

diff --git a/clang/include/clang/AST/ASTConcept.h b/clang/include/clang/AST/ASTConcept.h
index c8f6330a73bb1..c98dd4629abfc 100644
--- a/clang/include/clang/AST/ASTConcept.h
+++ b/clang/include/clang/AST/ASTConcept.h
@@ -27,6 +27,7 @@
 namespace clang {
 
 class ConceptDecl;
+class ConceptReference;
 class Expr;
 class NamedDecl;
 struct PrintingPolicy;
@@ -34,6 +35,7 @@ struct PrintingPolicy;
 /// The result of a constraint satisfaction check, containing the necessary
 /// information to diagnose an unsatisfied constraint.
 class ConstraintSatisfaction : public llvm::FoldingSetNode {
+private:
   // The template-like entity that 'owns' the constraint checked here (can be a
   // constrained entity or a concept).
   const NamedDecl *ConstraintOwner = nullptr;
@@ -48,7 +50,7 @@ class ConstraintSatisfaction : public llvm::FoldingSetNode {
       : ConstraintOwner(ConstraintOwner), TemplateArgs(TemplateArgs) {}
 
   using SubstitutionDiagnostic = std::pair<SourceLocation, StringRef>;
-  using Detail = llvm::PointerUnion<Expr *, SubstitutionDiagnostic *>;
+  using Detail = llvm::PointerUnion<const Expr *, SubstitutionDiagnostic *>;
 
   bool IsSatisfied = false;
   bool ContainsErrors = false;
@@ -79,7 +81,7 @@ class ConstraintSatisfaction : public llvm::FoldingSetNode {
 /// substituted into them, or a diagnostic if substitution resulted in
 /// an invalid expression.
 using UnsatisfiedConstraintRecord =
-    llvm::PointerUnion<Expr *, std::pair<SourceLocation, StringRef> *>;
+    llvm::PointerUnion<const Expr *, std::pair<SourceLocation, StringRef> *>;
 
 /// \brief The result of a constraint satisfaction check, containing the
 /// necessary information to diagnose an unsatisfied constraint.
diff --git a/clang/include/clang/AST/TemplateBase.h b/clang/include/clang/AST/TemplateBase.h
index 279feb858e665..8b658ac9db503 100644
--- a/clang/include/clang/AST/TemplateBase.h
+++ b/clang/include/clang/AST/TemplateBase.h
@@ -473,7 +473,6 @@ class TemplateArgument {
   /// Used to insert TemplateArguments into FoldingSets.
   void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context) const;
 };
-
 /// Location information for a TemplateArgument.
 struct TemplateArgumentLocInfo {
 private:
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index bbc5c181c6a10..b1f19ef6c067c 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -11703,7 +11703,8 @@ class Sema final : public SemaBase {
   CheckConceptTemplateId(const CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
                          const DeclarationNameInfo &ConceptNameInfo,
                          NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
-                         const TemplateArgumentListInfo *TemplateArgs);
+                         const TemplateArgumentListInfo *TemplateArgs,
+                         bool DoCheckConstraintSatisfaction = true);
 
   void diagnoseMissingTemplateArguments(TemplateName Name, SourceLocation Loc);
   void diagnoseMissingTemplateArguments(const CXXScopeSpec &SS,
@@ -12777,6 +12778,9 @@ class Sema final : public SemaBase {
   void MarkUsedTemplateParameters(ArrayRef<TemplateArgument> TemplateArgs,
                                   unsigned Depth, llvm::SmallBitVector &Used);
 
+  void MarkUsedTemplateParameters(ArrayRef<TemplateArgumentLoc> TemplateArgs,
+                                  unsigned Depth, llvm::SmallBitVector &Used);
+
   void
   MarkDeducedTemplateParameters(const FunctionTemplateDecl *FunctionTemplate,
                                 llvm::SmallBitVector &Deduced) {
@@ -13336,6 +13340,11 @@ class Sema final : public SemaBase {
                          const MultiLevelTemplateArgumentList &TemplateArgs,
                          TemplateArgumentListInfo &Outputs);
 
+  bool SubstTemplateArgumentsInParameterMapping(
+      ArrayRef<TemplateArgumentLoc> Args,
+      const MultiLevelTemplateArgumentList &TemplateArgs,
+      TemplateArgumentListInfo &Out);
+
   /// Retrieve the template argument list(s) that should be used to
   /// instantiate the definition of the given declaration.
   ///
@@ -14666,6 +14675,10 @@ class Sema final : public SemaBase {
     SatisfactionStack.swap(NewSS);
   }
 
+  using ConstrainedDeclOrNestedRequirement =
+      llvm::PointerUnion<const NamedDecl *,
+                         const concepts::NestedRequirement *>;
+
   /// Check whether the given expression is a valid constraint expression.
   /// A diagnostic is emitted if it is not, false is returned, and
   /// PossibleNonPrimary will be set to true if the failure might be due to a
@@ -14690,44 +14703,11 @@ class Sema final : public SemaBase {
   /// \returns true if an error occurred and satisfaction could not be checked,
   /// false otherwise.
   bool CheckConstraintSatisfaction(
-      const NamedDecl *Template,
+      ConstrainedDeclOrNestedRequirement Entity,
       ArrayRef<AssociatedConstraint> AssociatedConstraints,
       const MultiLevelTemplateArgumentList &TemplateArgLists,
-      SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction) {
-    llvm::SmallVector<Expr *, 4> Converted;
-    return CheckConstraintSatisfaction(Template, AssociatedConstraints,
-                                       Converted, TemplateArgLists,
-                                       TemplateIDRange, Satisfaction);
-  }
-
-  /// \brief Check whether the given list of constraint expressions are
-  /// satisfied (as if in a 'conjunction') given template arguments.
-  /// Additionally, takes an empty list of Expressions which is populated with
-  /// the instantiated versions of the ConstraintExprs.
-  /// \param Template the template-like entity that triggered the constraints
-  /// check (either a concept or a constrained entity).
-  /// \param ConstraintExprs a list of constraint expressions, treated as if
-  /// they were 'AND'ed together.
-  /// \param ConvertedConstraints a out parameter that will get populated with
-  /// the instantiated version of the ConstraintExprs if we successfully checked
-  /// satisfaction.
-  /// \param TemplateArgList the multi-level list of template arguments to
-  /// substitute into the constraint expression. This should be relative to the
-  /// top-level (hence multi-level), since we need to instantiate fully at the
-  /// time of checking.
-  /// \param TemplateIDRange The source range of the template id that
-  /// caused the constraints check.
-  /// \param Satisfaction if true is returned, will contain details of the
-  /// satisfaction, with enough information to diagnose an unsatisfied
-  /// expression.
-  /// \returns true if an error occurred and satisfaction could not be checked,
-  /// false otherwise.
-  bool CheckConstraintSatisfaction(
-      const NamedDecl *Template,
-      ArrayRef<AssociatedConstraint> AssociatedConstraints,
-      llvm::SmallVectorImpl<Expr *> &ConvertedConstraints,
-      const MultiLevelTemplateArgumentList &TemplateArgList,
-      SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction);
+      SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction,
+      const ConceptReference *TopLevelConceptId = nullptr);
 
   /// \brief Check whether the given non-dependent constraint expression is
   /// satisfied. Returns false and updates Satisfaction with the satisfaction
@@ -14793,16 +14773,16 @@ class Sema final : public SemaBase {
   /// \param First whether this is the first time an unsatisfied constraint is
   /// diagnosed for this error.
   void DiagnoseUnsatisfiedConstraint(const ConstraintSatisfaction &Satisfaction,
+                                     SourceLocation Loc = {},
                                      bool First = true);
 
   /// \brief Emit diagnostics explaining why a constraint expression was deemed
   /// unsatisfied.
-  void
-  DiagnoseUnsatisfiedConstraint(const ASTConstraintSatisfaction &Satisfaction,
-                                bool First = true);
+  void DiagnoseUnsatisfiedConstraint(
+      const ConceptSpecializationExpr *ConstraintExpr);
 
   const NormalizedConstraint *getNormalizedAssociatedConstraints(
-      const NamedDecl *ConstrainedDecl,
+      ConstrainedDeclOrNestedRequirement Entity,
       ArrayRef<AssociatedConstraint> AssociatedConstraints);
 
   /// \brief Check whether the given declaration's associated constraints are
@@ -14837,7 +14817,8 @@ class Sema final : public SemaBase {
   /// constrained declarations). If an error occurred while normalizing the
   /// associated constraints of the template or concept, nullptr will be cached
   /// here.
-  llvm::DenseMap<const NamedDecl *, NormalizedConstraint *> NormalizationCache;
+  llvm::DenseMap<ConstrainedDeclOrNestedRequirement, NormalizedConstraint *>
+      NormalizationCache;
 
   llvm::ContextualFoldingSet<ConstraintSatisfaction, const ASTContext &>
       SatisfactionCache;
diff --git a/clang/include/clang/Sema/SemaConcept.h b/clang/include/clang/Sema/SemaConcept.h
index 648a9c51ae6c1..0a79a6b5cc43f 100644
--- a/clang/include/clang/Sema/SemaConcept.h
+++ b/clang/include/clang/Sema/SemaConcept.h
@@ -16,130 +16,355 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/Expr.h"
+#include "clang/AST/ExprConcepts.h"
 #include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/UnsignedOrNone.h"
+#include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/FoldingSet.h"
-#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/STLForwardCompat.h"
 #include "llvm/ADT/STLFunctionalExtras.h"
+#include "llvm/ADT/SmallBitVector.h"
 #include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Compiler.h"
 #include <optional>
 #include <utility>
 
 namespace clang {
 class Sema;
+class MultiLevelTemplateArgumentList;
 
-enum { ConstraintAlignment = 8 };
+/// \brief A normalized constraint, as defined in C++ [temp.constr.normal], is
+/// either an atomic constraint, a conjunction of normalized constraints or a
+/// disjunction of normalized constraints.
+
+struct NormalizedConstraint {
+
+  enum class ConstraintKind {
+    Atomic = 0,
+    ConceptId,
+    FoldExpanded,
+    Compound,
+  };
+
+  enum CompoundConstraintKind { CCK_Conjunction, CCK_Disjunction };
+  enum class FoldOperatorKind : unsigned int { And, Or };
+
+  using OccurenceList = llvm::SmallBitVector;
+
+  friend bool
+  substituteParameterMappings(Sema &S, NormalizedConstraint &N,
+                              const MultiLevelTemplateArgumentList &MLTAL,
+                              const ASTTemplateArgumentListInfo *ArgsAsWritten,
+                              TemplateParameterList *TemplateParams,
+                              const NamedDecl *D);
+
+protected:
+  using ExprOrConcept =
+      llvm::PointerUnion<const Expr *, const ConceptReference *>;
+
+  struct AtomicBits {
+    LLVM_PREFERRED_TYPE(ConstraintKind)
+    unsigned Kind : 5;
+    unsigned : 1;
+    unsigned PackSubstitutionIndex : 26;
+    llvm::SmallBitVector Indexes;
+    TemplateArgumentLoc *Args;
+    ExprOrConcept ConstraintExpr;
+    const NamedDecl *ConstraintDecl;
+  };
 
-struct alignas(ConstraintAlignment) AtomicConstraint {
-  const Expr *ConstraintExpr;
-  const NamedDecl *ConstraintDecl;
-  std::optional<ArrayRef<TemplateArgumentLoc>> ParameterMapping;
+  struct FoldExpandedBits {
+    LLVM_PREFERRED_TYPE(ConstraintKind)
+    unsigned Kind : 5;
+    LLVM_PREFERRED_TYPE(FoldOperatorKind)
+    unsigned FoldOperator : 1;
+    unsigned : 26;
+    OccurenceList Indexes;
+    TemplateArgumentLoc *Args;
+    const Expr *Pattern;
+    NormalizedConstraint *Constraint;
+  };
 
-  AtomicConstraint(const Expr *ConstraintExpr, const NamedDecl *ConstraintDecl)
-      : ConstraintExpr(ConstraintExpr), ConstraintDecl(ConstraintDecl) {};
+  struct ConceptIdBits : AtomicBits {
+    NormalizedConstraint *Sub;
+  };
+
+  struct CompoundBits {
+    LLVM_PREFERRED_TYPE(ConstraintKind)
+    unsigned Kind : 5;
+    LLVM_PREFERRED_TYPE(CompoundConstraintKind)
+    unsigned CCK : 1;
+    NormalizedConstraint *LHS;
+    NormalizedConstraint *RHS;
+  };
+
+  union {
+    AtomicBits Atomic;
+    FoldExpandedBits FoldExpanded;
+    ConceptIdBits ConceptId;
+    CompoundBits Compound;
+  };
+
+  ~NormalizedConstraint() {
+    if (getKind() != ConstraintKind::Compound)
+      Atomic.Indexes.llvm::SmallBitVector::~SmallBitVector();
+  }
+
+  NormalizedConstraint(const Expr *ConstraintExpr,
+                       const NamedDecl *ConstraintDecl,
+                       UnsignedOrNone PackIndex)
+      : Atomic{llvm::to_underlying(ConstraintKind::Atomic),
+               PackIndex.toInternalRepresentation(),
+               /*Indexes=*/{},
+               /*Args=*/nullptr,
+               ConstraintExpr,
+               ConstraintDecl} {}
+
+  NormalizedConstraint(const Expr *Pattern, FoldOperatorKind OpKind,
+                       NormalizedConstraint *Constraint)
+      : FoldExpanded{llvm::to_underlying(ConstraintKind::FoldExpanded),
+                     llvm::to_underlying(OpKind),
+                     /*Indexes=*/{},
+                     /*Args=*/nullptr,
+                     Pattern,
+                     Constraint} {}
+
+  NormalizedConstraint(const ConceptReference *ConceptId,
+                       const NamedDecl *ConstraintDecl,
+                       NormalizedConstraint *SubConstraint,
+                       UnsignedOrNone PackIndex)
+      : ConceptId{{llvm::to_underlying(ConstraintKind::ConceptId),
+                   PackIndex.toInternalRepresentation(), /*Indexes=*/{},
+                   /*Args=*/nullptr, ConceptId, ConstraintDecl},
+                  SubConstraint} {}
+
+  NormalizedConstraint(NormalizedConstraint *LHS, CompoundConstraintKind CCK,
+                       NormalizedConstraint *RHS)
+      : Compound{llvm::to_underlying(ConstraintKind::Compound), CCK, LHS, RHS} {
+  }
+
+  bool hasParameterMapping() const {
+    assert(getKind() != ConstraintKind::Compound);
+    return Atomic.Args != nullptr;
+  }
+
+  const OccurenceList &mappingOccurenceList() const {
+    assert(hasParameterMapping() && "This constraint has no parameter mapping");
+    return Atomic.Indexes;
+  }
+
+  llvm::MutableArrayRef<TemplateArgumentLoc> getParameterMapping() const {
+    return {Atomic.Args, Atomic.Indexes.count()};
+  }
+
+  void InitParameterMapping(TemplateParameterList *TemplateParams, const Expr *,
+                            const ASTTemplateArgumentListInfo *ArgsAsWritten);
+
+  void updateParameterMapping(OccurenceList Indexes,
+                              llvm::MutableArrayRef<TemplateArgumentLoc> Args) {
+    assert(getKind() != ConstraintKind::Compound);
+    assert(Indexes.count() == Args.size());
+    Atomic.Indexes = Indexes;
+    Atomic.Args = Args.data();
+  }
 
   bool hasMatchingParameterMapping(ASTContext &C,
-                                   const AtomicConstraint &Other) const {
-    if (!ParameterMapping != !Other.ParameterMapping)
+                                   const NormalizedConstraint &Other) const {
+    assert(getKind() != ConstraintKind::Compound);
+
+    if (hasParameterMapping() != Other.hasParameterMapping())
       return false;
-    if (!ParameterMapping)
+    if (!hasParameterMapping())
       return true;
-    if (ParameterMapping->size() != Other.ParameterMapping->size())
+
+    llvm::ArrayRef<TemplateArgumentLoc> ParameterMapping =
+        getParameterMapping();
+    llvm::ArrayRef<TemplateArgumentLoc> OtherParameterMapping =
+        Other.getParameterMapping();
+
+    if (ParameterMapping.size() != OtherParameterMapping.size())
       return false;
 
-    for (unsigned I = 0, S = ParameterMapping->size(); I < S; ++I) {
+    for (unsigned I = 0, S = ParameterMapping.size(); I < S; ++I) {
       llvm::FoldingSetNodeID IDA, IDB;
-      C.getCanonicalTemplateArgument((*ParameterMapping)[I].getArgument())
+      C.getCanonicalTemplateArgument(ParameterMapping[I].getArgument())
           .Profile(IDA, C);
-      C.getCanonicalTemplateArgument((*Other.ParameterMapping)[I].getArgument())
+      C.getCanonicalTemplateArgument(OtherParameterMapping[I].getArgument())
           .Profile(IDB, C);
       if (IDA != IDB)
         return false;
     }
     return true;
   }
-};
 
-struct alignas(ConstraintAlignment) NormalizedConstraintPair;
-struct alignas(ConstraintAlignment) FoldExpandedConstraint;
+public:
+  ConstraintKind getKind() const {
+    return static_cast<ConstraintKind>(Atomic.Kind);
+  }
+
+  SourceLocation getBeginLoc() const {
+    switch (getKind()) {
+    case ConstraintKind::Atomic:
+      return cast<const Expr *>(Atomic.ConstraintExpr)->getBeginLoc();
+    case ConstraintKind::ConceptId:
+      return cast<const ConceptReference *>(Atomic.ConstraintExpr)
+          ->getBeginLoc();
+    case ConstraintKind::Compound:
+      return Compound.LHS->getBeginLoc();
+    case ConstraintKind::FoldExpanded:
+      return FoldExpanded.Pattern->getBeginLoc();
+    }
+  }
 
-/// \brief A normalized constraint, as defined in C++ [temp.constr.normal], is
-/// either an atomic constraint, a conjunction of normalized constraints or a
-/// disjunction of normalized constraints.
-struct NormalizedConstraint {
+  SourceLocation getEndLoc() const {
+    switch (getKind()) {
+    case ConstraintKind::Atomic:
+      return cast<const Expr *>(Atomic.ConstraintExpr)->getEndLoc();
+    case ConstraintKind::ConceptId:
+      return cast<const ConceptReference *>(Atomic.ConstraintExpr)->getEndLoc();
+    case ConstraintKind::Compound:
+      return Compound.RHS->getEndLoc();
+    case ConstraintKind::FoldExpanded:
+      return FoldExpanded.Pattern->getEndLoc();
+    }
+  }
+
+  SourceRange getSourceRange() const { return {getBeginLoc(), getEndLoc()}; }
+
+private:
   friend class Sema;
+  static NormalizedConstraint *
+  fromAssociatedConstraints(Sema &S, const NamedDecl *D,
+                            ArrayRef<AssociatedConstraint> ACs);
+  static NormalizedConstraint *fromConstraintExpr(Sema &S, const NamedDecl *D,
+                                                  const Expr *E);
+};
 
-  enum CompoundConstraintKind { CCK_Conjunction, CCK_Disjunction };
+class CompoundConstraint : public NormalizedConstraint {
+  using NormalizedConstraint::NormalizedConstraint;
+
+public:
+  static CompoundConstraint *Create(ASTContext &Ctx, NormalizedConstraint *LHS,
+                                    CompoundConstraintKind CCK,
+                                    NormalizedConstraint *RHS) {
+    return new (Ctx) CompoundConstraint(LHS, CCK, RHS);
+  }
+
+  static CompoundConstraint *CreateConjunction(ASTContext &Ctx,
+                                               NormalizedConstraint *LHS,
+                                               NormalizedConstraint *RHS) {
+    return new (Ctx) CompoundConstraint(LHS, CCK_Conjunction, RHS);
+  }
 
-  using CompoundConstraint = llvm::PointerIntPair<NormalizedConstraintPair *, 1,
-                                                  CompoundConstraintKind>;
+  const NormalizedConstraint &getLHS() const { return *Compound.LHS; }
 
-  llvm::PointerUnion<AtomicConstraint *, FoldExpandedConstraint *,
-                     CompoundConstraint>
-      Constraint;
+  NormalizedConstraint &getLHS() { return *Compound.LHS; }
 
-  NormalizedConstraint(AtomicConstraint *C): Constraint{C} { };
-  NormalizedConstraint(FoldExpandedConstraint *C) : Constraint{C} {};
+  const NormalizedConstraint &getRHS() const { return *Compound.RHS; }
 
-  NormalizedConstraint(ASTContext &C, NormalizedConstraint LHS,
-                       NormalizedConstraint RHS, CompoundConstraintKind Kind);
+  NormalizedConstraint &getRHS() { return *Compound.RHS; }
 
-  NormalizedConstraint(ASTContext &C, const NormalizedConstraint &Other);
-  NormalizedConstraint(NormalizedConstraint &&Other):
-      Constraint(Other.Constraint) {
-    Other.Constraint = nullptr;
+  CompoundConstraintKind getCompoundKind() const {
+    return static_cast<CompoundConstraintKind>(Compound.CCK);
   }
-  NormalizedConstraint &operator=(const NormalizedConstraint &Other) = delete;
-  NormalizedConstraint &operator=(NormalizedConstraint &&Other) {
-    if (&Other != this) {
-      NormalizedConstraint Temp(std::move(Other));
-      std::swap(Constraint, Temp.Constraint);
-    }
-    return *this;
+};
+
+class NormalizedConstraintWithParamMapping : public NormalizedConstraint {
+protected:
+  using NormalizedConstraint::NormalizedConstraint;
+
+public:
+  using NormalizedConstraint::getParameterMapping;
+  using NormalizedConstraint::hasMatchingParameterMapping;
+  using NormalizedConstraint::hasParameterMapping;
+  using NormalizedConstraint::mappingOccurenceList;
+  using NormalizedConstraint::updateParameterMapping;
+
+  const NamedDecl *getConstraintDecl() const { return Atomic.ConstraintDecl; }
+
+  UnsignedOrNone getPackSubstitutionIndex() const {
+    return UnsignedOrNone::fromInternalRepresentation(
+        Atomic.PackSubstitutionIndex);
+  }
+};
+
+class AtomicConstraint : public NormalizedConstraintWithParamMapping {
+  using NormalizedConstraintWithParamMapping::
+      NormalizedConstraintWithParamMapping;
+
+public:
+  static AtomicConstraint *Create(ASTContext &Ctx, const Expr *ConstraintExpr,
+                                  const NamedDecl *ConstraintDecl,
+                                  UnsignedOrNone PackIndex) {
+    return new (Ctx)
+        AtomicConstraint(ConstraintExpr, ConstraintDecl, PackIndex);
   }
 
-  bool isAtomic() const { return llvm::isa<AtomicConstraint *>(Constraint); }
-  bool isFoldExpanded() const {
-    return llvm::isa<FoldExpandedConstraint *>(Constraint);
+  const Expr *getConstraintExpr() const {
+    return cast<const Expr *>(Atomic.ConstraintExpr);
   }
-  bool isCompound() const { return llvm::isa<CompoundConstraint>(Constraint); }
 
-  CompoundConstraintKind getCompoundKind() const;
+  void InitParameterMapping(const ASTTemplateArgumentListInfo *ArgsAsWritten) {
+    NormalizedConstraint::InitParameterMapping(
+        cast<TemplateDecl>(Atomic.ConstraintDecl)->getTemplateParameters(),
+        getConstraintExpr(), ArgsAsWritten);
+  }
+};
 
-  NormalizedConstraint &getLHS() const;
-  NormalizedConstraint &getRHS() const;
+class FoldExpandedConstraint : public NormalizedConstraint {
+  using NormalizedConstraint::NormalizedConstraint;
 
-  AtomicConstraint *getAtomicConstraint() const;
+public:
+  static FoldExpandedConstraint *Create(ASTContext &Ctx, const Expr *Pattern,
+                                        FoldOperatorKind OpKind,
+                                        NormalizedConstraint *Constraint) {
+    return new (Ctx) FoldExpandedConstraint(Pattern, OpKind, Constraint);
+  }
 
-  FoldExpandedConstraint *getFoldExpandedConstraint() const;
+  using NormalizedConstraint::hasMatchingParameterMapping;
 
-private:
-  static std::optional<NormalizedConstraint>
-  fromAssociatedConstraints(Sema &S, const NamedDecl *D,
-                            ArrayRef<AssociatedConstraint> ACs);
-  static std::optional<NormalizedConstraint>
-  fromConstraintExpr(Sema &S, const NamedDecl *D, const Expr *E);
-};
+  FoldOperatorKind getFoldOperator() const {
+    return static_cast<FoldOperatorKind>(FoldExpanded.FoldOperator);
+  }
 
-struct alignas(ConstraintAlignment) NormalizedConstraintPair {
-  NormalizedConstraint LHS, RHS;
-};
+  const Expr *getPattern() const { return FoldExpanded.Pattern; }
 
-struct alignas(ConstraintAlignment) FoldExpandedConstraint {
-  enum class FoldOperatorKind { And, Or } Kind;
-  NormalizedConstraint Constraint;
-  const Expr *Pattern;
+  const NormalizedConstraint &getNormalizedPattern() const {
+    return *FoldExpanded.Constraint;
+  }
 
-  FoldExpandedConstraint(FoldOperatorKind K, NormalizedConstraint C,
-                         const Expr *Pattern)
-      : Kind(K), Constraint(std::move(C)), Pattern(Pattern) {};
+  NormalizedConstraint &getNormalizedPattern() {
+    return *FoldExpanded.Constraint;
+  }
 
   static bool AreCompatibleForSubsumption(const FoldExpandedConstraint &A,
                                           const FoldExpandedConstraint &B);
 };
 
-const NormalizedConstraint *getNormalizedAssociatedConstraints(
-    Sema &S, const NamedDecl *ConstrainedDecl,
-    ArrayRef<AssociatedConstraint> AssociatedConstraints);
+class ConceptIdConstraint : public NormalizedConstraintWithParamMapping {
+  using NormalizedConstraintWithParamMapping::
+      NormalizedConstraintWithParamMapping;
+
+public:
+  static ConceptIdConstraint *Create(ASTContext &Ctx,
+                                     const ConceptReference *ConceptId,
+                                     NormalizedConstraint *SubConstraint,
+                                     const NamedDecl *ConstraintDecl,
+                                     UnsignedOrNone PackIndex) {
+    return new (Ctx) ConceptIdConstraint(ConceptId, ConstraintDecl,
+                                         SubConstraint, PackIndex);
+  }
+
+  const ConceptReference *getConceptId() const {
+    return cast<const ConceptReference *>(ConceptId.ConstraintExpr);
+  }
+
+  const NormalizedConstraint &getNormalizedConstraint() const {
+    return *ConceptId.Sub;
+  }
+
+  NormalizedConstraint &getNormalizedConstraint() { return *ConceptId.Sub; }
+};
 
 /// \brief SubsumptionChecker establishes subsumption
 /// between two set of constraints.
@@ -189,13 +414,13 @@ class SubsumptionChecker {
   };
 
   struct MappedAtomicConstraint {
-    AtomicConstraint *Constraint;
+    const AtomicConstraint *Constraint;
     Literal ID;
   };
 
   struct FoldExpendedConstraintKey {
     FoldExpandedConstraint::FoldOperatorKind Kind;
-    AtomicConstraint *Constraint;
+    const AtomicConstraint *Constraint;
     Literal ID;
   };
 
@@ -207,7 +432,7 @@ class SubsumptionChecker {
 
   // A map from a literal to a corresponding associated constraint.
   // We do not have enough bits left for a pointer union here :(
-  llvm::DenseMap<uint16_t, void *> ReverseMap;
+  llvm::DenseMap<uint16_t, const void *> ReverseMap;
 
   // Fold expanded constraints ask us to recursively establish subsumption.
   // This caches the result.
@@ -234,8 +459,8 @@ class SubsumptionChecker {
   FormulaType Normalize(const NormalizedConstraint &C);
   void AddUniqueClauseToFormula(Formula &F, Clause C);
 
-  Literal find(AtomicConstraint *);
-  Literal find(FoldExpandedConstraint *);
+  Literal find(const AtomicConstraint *);
+  Literal find(const FoldExpandedConstraint *);
 
   uint16_t getNewLiteralId();
 };
diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h
index f9a10cfafb1f7..e6962e5bb1ae5 100644
--- a/clang/include/clang/Sema/Template.h
+++ b/clang/include/clang/Sema/Template.h
@@ -239,10 +239,12 @@ enum class TemplateSubstitutionKind : char {
              "Replacing in an empty list?");
 
       if (!TemplateArgumentLists.empty()) {
-        assert((TemplateArgumentLists[0].AssociatedDeclAndFinal.getPointer() ||
-                TemplateArgumentLists[0].AssociatedDeclAndFinal.getPointer() ==
-                    AssociatedDecl) &&
-               "Trying to change incorrect declaration?");
+        // assert((!TemplateArgumentLists[0].AssociatedDeclAndFinal.getPointer()
+        // ||
+        //         TemplateArgumentLists[0].AssociatedDeclAndFinal.getPointer()
+        //         ==
+        //             AssociatedDecl) &&
+        //         "Trying to change incorrect declaration?");
         TemplateArgumentLists[0].Args = Args;
       } else {
         --NumRetainedOuterLevels;
@@ -251,6 +253,17 @@ enum class TemplateSubstitutionKind : char {
       }
     }
 
+    void replaceOutermostTemplateArguments(Decl *AssociatedDecl, ArgList Args) {
+      assert((!TemplateArgumentLists.empty()) && "Replacing in an empty list?");
+      // assert((!TemplateArgumentLists.back().AssociatedDeclAndFinal.getPointer()
+      // ||
+      //         TemplateArgumentLists.back().AssociatedDeclAndFinal.getPointer()
+      //         ==
+      //             AssociatedDecl) &&
+      //        "Trying to change incorrect declaration?");
+      TemplateArgumentLists.back().Args = Args;
+    }
+
     /// Add an outermost level that we are not substituting. We have no
     /// arguments at this level, and do not remove it from the depth of inner
     /// template parameters that we instantiate.
diff --git a/clang/lib/AST/ASTConcept.cpp b/clang/lib/AST/ASTConcept.cpp
index c9cfec6bd64b5..d4dfc75fd25d5 100644
--- a/clang/lib/AST/ASTConcept.cpp
+++ b/clang/lib/AST/ASTConcept.cpp
@@ -23,7 +23,9 @@ static void
 CreateUnsatisfiedConstraintRecord(const ASTContext &C,
                                   const UnsatisfiedConstraintRecord &Detail,
                                   UnsatisfiedConstraintRecord *TrailingObject) {
-  if (auto *E = dyn_cast<Expr *>(Detail))
+  if (Detail.isNull())
+    new (TrailingObject) UnsatisfiedConstraintRecord(nullptr);
+  else if (const auto *E = llvm::dyn_cast<const Expr *>(Detail))
     new (TrailingObject) UnsatisfiedConstraintRecord(E);
   else {
     auto &SubstitutionDiagnostic =
@@ -73,9 +75,10 @@ ASTConstraintSatisfaction *ASTConstraintSatisfaction::Rebuild(
   return new (Mem) ASTConstraintSatisfaction(C, Satisfaction);
 }
 
-void ConstraintSatisfaction::Profile(
-    llvm::FoldingSetNodeID &ID, const ASTContext &C,
-    const NamedDecl *ConstraintOwner, ArrayRef<TemplateArgument> TemplateArgs) {
+void ConstraintSatisfaction::Profile(llvm::FoldingSetNodeID &ID,
+                                     const ASTContext &C,
+                                     const NamedDecl *ConstraintOwner,
+                                     ArrayRef<TemplateArgument> TemplateArgs) {
   ID.AddPointer(ConstraintOwner);
   ID.AddInteger(TemplateArgs.size());
   for (auto &Arg : TemplateArgs)
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index b5417fcf20ddd..698ac64e05232 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -5495,6 +5495,7 @@ QualType ASTContext::getSubstTemplateTypeParmType(QualType Replacement,
   llvm::FoldingSetNodeID ID;
   SubstTemplateTypeParmType::Profile(ID, Replacement, AssociatedDecl, Index,
                                      PackIndex, Final);
+
   void *InsertPos = nullptr;
   SubstTemplateTypeParmType *SubstParm =
       SubstTemplateTypeParmTypes.FindNodeOrInsertPos(ID, InsertPos);
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index c6a54dc141ded..7c8d80ba4ca34 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -12,10 +12,16 @@
 
 #include "clang/Sema/SemaConcept.h"
 #include "TreeTransform.h"
+#include "clang/AST/ASTConcept.h"
 #include "clang/AST/ASTLambda.h"
+#include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/Expr.h"
 #include "clang/AST/ExprConcepts.h"
+#include "clang/AST/TemplateBase.h"
 #include "clang/Basic/OperatorPrecedence.h"
+#include "clang/Basic/UnsignedOrNone.h"
 #include "clang/Sema/EnterExpressionEvaluationContext.h"
 #include "clang/Sema/Initialization.h"
 #include "clang/Sema/Overload.h"
@@ -24,9 +30,12 @@
 #include "clang/Sema/SemaInternal.h"
 #include "clang/Sema/Template.h"
 #include "clang/Sema/TemplateDeduction.h"
+#include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringExtras.h"
+#include <cstddef>
 #include <optional>
 
 using namespace clang;
@@ -200,6 +209,50 @@ DiagRecursiveConstraintEval(Sema &S, llvm::FoldingSetNodeID &ID,
   return false;
 }
 
+// Figure out the to-translation-unit depth for this function declaration for
+// the purpose of seeing if they differ by constraints. This isn't the same as
+// getTemplateDepth, because it includes already instantiated parents.
+static unsigned
+CalculateTemplateDepthForConstraints(Sema &S, const NamedDecl *ND,
+                                     bool SkipForSpecialization = false) {
+  MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
+      ND, ND->getLexicalDeclContext(), /*Final=*/false,
+      /*Innermost=*/std::nullopt,
+      /*RelativeToPrimary=*/true,
+      /*Pattern=*/nullptr,
+      /*ForConstraintInstantiation=*/true, SkipForSpecialization);
+  return MLTAL.getNumLevels();
+}
+
+namespace {
+class AdjustConstraintDepth : public TreeTransform<AdjustConstraintDepth> {
+  unsigned TemplateDepth = 0;
+
+public:
+  using inherited = TreeTransform<AdjustConstraintDepth>;
+  AdjustConstraintDepth(Sema &SemaRef, unsigned TemplateDepth)
+      : inherited(SemaRef), TemplateDepth(TemplateDepth) {}
+
+  using inherited::TransformTemplateTypeParmType;
+  QualType TransformTemplateTypeParmType(TypeLocBuilder &TLB,
+                                         TemplateTypeParmTypeLoc TL, bool) {
+    const TemplateTypeParmType *T = TL.getTypePtr();
+
+    TemplateTypeParmDecl *NewTTPDecl = nullptr;
+    if (TemplateTypeParmDecl *OldTTPDecl = T->getDecl())
+      NewTTPDecl = cast_or_null<TemplateTypeParmDecl>(
+          TransformDecl(TL.getNameLoc(), OldTTPDecl));
+
+    QualType Result = getSema().Context.getTemplateTypeParmType(
+        T->getDepth() + TemplateDepth, T->getIndex(), T->isParameterPack(),
+        NewTTPDecl);
+    TemplateTypeParmTypeLoc NewTL = TLB.push<TemplateTypeParmTypeLoc>(Result);
+    NewTL.setNameLoc(TL.getNameLoc());
+    return Result;
+  }
+};
+} // namespace
+
 static ExprResult EvaluateAtomicConstraint(
     Sema &S, const Expr *AtomicExpr, const NamedDecl *Template,
     SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
@@ -209,7 +262,7 @@ static ExprResult EvaluateAtomicConstraint(
       Sema::ReuseLambdaContextDecl);
 
   // Atomic constraint - substitute arguments and check satisfaction.
-  ExprResult SubstitutedExpression;
+  ExprResult SubstitutedExpression = const_cast<Expr *>(AtomicExpr);
   {
     TemplateDeductionInfo Info(TemplateNameLoc);
     Sema::InstantiatingTemplate Inst(
@@ -220,7 +273,7 @@ static ExprResult EvaluateAtomicConstraint(
     if (Inst.isInvalid())
       return ExprError();
 
-    llvm::FoldingSetNodeID ID;
+    /*llvm::FoldingSetNodeID ID;
     if (Template &&
         DiagRecursiveConstraintEval(S, ID, Template, AtomicExpr, MLTAL)) {
       Satisfaction.IsSatisfied = false;
@@ -229,9 +282,18 @@ static ExprResult EvaluateAtomicConstraint(
     }
 
     SatisfactionStackRAII StackRAII(S, Template, ID);
+    */
 
     // We do not want error diagnostics escaping here.
     Sema::SFINAETrap Trap(S);
+
+    if (Template && Template->getTemplateDepth() > 0) {
+      SubstitutedExpression =
+          AdjustConstraintDepth(S, Template->getTemplateDepth() - 1)
+              .TransformExpr(const_cast<Expr *>(AtomicExpr))
+              .get();
+    }
+
     SubstitutedExpression =
         S.SubstConstraintExpr(const_cast<Expr *>(AtomicExpr), MLTAL);
 
@@ -289,217 +351,82 @@ static ExprResult EvaluateAtomicConstraint(
   return SubstitutedExpression;
 }
 
-static UnsignedOrNone EvaluateFoldExpandedConstraintSize(
-    Sema &S, const CXXFoldExpr *FE, const NamedDecl *Template,
+static bool calculateConstraintSatisfaction(
+    Sema &S, const NormalizedConstraint &Constraint, const NamedDecl *Template,
     SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
-    ConstraintSatisfaction &Satisfaction) {
+    ConstraintSatisfaction &Satisfaction, UnsignedOrNone PackSubstitutionIndex);
 
-  // We should ignore errors in the presence of packs of different size.
-  Sema::SFINAETrap Trap(S);
+static std::optional<MultiLevelTemplateArgumentList>
+SubstitutionInTemplateArguments(
+    Sema &S, const NormalizedConstraintWithParamMapping &Constraint,
+    const NamedDecl *Template, MultiLevelTemplateArgumentList MLTAL,
+    llvm::SmallVector<TemplateArgument> &SubstitutedOuterMost) {
 
-  Expr *Pattern = FE->getPattern();
-
-  SmallVector<UnexpandedParameterPack, 2> Unexpanded;
-  S.collectUnexpandedParameterPacks(Pattern, Unexpanded);
-  assert(!Unexpanded.empty() && "Pack expansion without parameter packs?");
-  bool Expand = true;
-  bool RetainExpansion = false;
-  UnsignedOrNone NumExpansions = FE->getNumExpansions();
-  if (S.CheckParameterPacksForExpansion(
-          FE->getEllipsisLoc(), Pattern->getSourceRange(), Unexpanded, MLTAL,
-          Expand, RetainExpansion, NumExpansions) ||
-      !Expand || RetainExpansion)
-    return std::nullopt;
+  Sema::SFINAETrap Trap(S);
 
-  if (NumExpansions && S.getLangOpts().BracketDepth < *NumExpansions) {
-    S.Diag(FE->getEllipsisLoc(),
-           clang::diag::err_fold_expression_limit_exceeded)
-        << *NumExpansions << S.getLangOpts().BracketDepth
-        << FE->getSourceRange();
-    S.Diag(FE->getEllipsisLoc(), diag::note_bracket_depth);
+  Sema::InstantiatingTemplate Inst(
+      S, Constraint.getBeginLoc(),
+      Sema::InstantiatingTemplate::ParameterMappingSubstitution{},
+      // FIXME: improve const-correctness of InstantiatingTemplate
+      const_cast<NamedDecl *>(Template), Constraint.getSourceRange());
+  if (Inst.isInvalid())
     return std::nullopt;
-  }
-  return NumExpansions;
-}
 
-static ExprResult calculateConstraintSatisfaction(
-    Sema &S, const Expr *ConstraintExpr, const NamedDecl *Template,
-    SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
-    ConstraintSatisfaction &Satisfaction);
+  // TODO substitute at the appropriate depth
+  // Template->getTemplateDepth();
 
-static ExprResult calculateConstraintSatisfaction(
-    Sema &S, const Expr *LHS, OverloadedOperatorKind Op, const Expr *RHS,
-    const NamedDecl *Template, SourceLocation TemplateNameLoc,
-    const MultiLevelTemplateArgumentList &MLTAL,
-    ConstraintSatisfaction &Satisfaction) {
-  size_t EffectiveDetailEndIndex = Satisfaction.Details.size();
-
-  ExprResult LHSRes = calculateConstraintSatisfaction(
-      S, LHS, Template, TemplateNameLoc, MLTAL, Satisfaction);
-
-  if (LHSRes.isInvalid())
-    return ExprError();
-
-  bool IsLHSSatisfied = Satisfaction.IsSatisfied;
-
-  if (Op == clang::OO_PipePipe && IsLHSSatisfied)
-    // [temp.constr.op] p3
-    //    A disjunction is a constraint taking two operands. To determine if
-    //    a disjunction is satisfied, the satisfaction of the first operand
-    //    is checked. If that is satisfied, the disjunction is satisfied.
-    //    Otherwise, the disjunction is satisfied if and only if the second
-    //    operand is satisfied.
-    // LHS is instantiated while RHS is not. Skip creating invalid BinaryOp.
-    return LHSRes;
-
-  if (Op == clang::OO_AmpAmp && !IsLHSSatisfied)
-    // [temp.constr.op] p2
-    //    A conjunction is a constraint taking two operands. To determine if
-    //    a conjunction is satisfied, the satisfaction of the first operand
-    //    is checked. If that is not satisfied, the conjunction is not
-    //    satisfied. Otherwise, the conjunction is satisfied if and only if
-    //    the second operand is satisfied.
-    // LHS is instantiated while RHS is not. Skip creating invalid BinaryOp.
-    return LHSRes;
-
-  ExprResult RHSRes = calculateConstraintSatisfaction(
-      S, RHS, Template, TemplateNameLoc, MLTAL, Satisfaction);
-  if (RHSRes.isInvalid())
-    return ExprError();
-
-  bool IsRHSSatisfied = Satisfaction.IsSatisfied;
-  // Current implementation adds diagnostic information about the falsity
-  // of each false atomic constraint expression when it evaluates them.
-  // When the evaluation results to `false || true`, the information
-  // generated during the evaluation of left-hand side is meaningless
-  // because the whole expression evaluates to true.
-  // The following code removes the irrelevant diagnostic information.
-  // FIXME: We should probably delay the addition of diagnostic information
-  // until we know the entire expression is false.
-  if (Op == clang::OO_PipePipe && IsRHSSatisfied) {
-    auto EffectiveDetailEnd = Satisfaction.Details.begin();
-    std::advance(EffectiveDetailEnd, EffectiveDetailEndIndex);
-    Satisfaction.Details.erase(EffectiveDetailEnd, Satisfaction.Details.end());
-  }
-
-  if (!LHSRes.isUsable() || !RHSRes.isUsable())
-    return ExprEmpty();
-
-  return BinaryOperator::Create(S.Context, LHSRes.get(), RHSRes.get(),
-                                BinaryOperator::getOverloadedOpcode(Op),
-                                S.Context.BoolTy, VK_PRValue, OK_Ordinary,
-                                LHS->getBeginLoc(), FPOptionsOverride{});
-}
-
-static ExprResult calculateConstraintSatisfaction(
-    Sema &S, const CXXFoldExpr *FE, const NamedDecl *Template,
-    SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
-    ConstraintSatisfaction &Satisfaction) {
-  bool Conjunction = FE->getOperator() == BinaryOperatorKind::BO_LAnd;
-  size_t EffectiveDetailEndIndex = Satisfaction.Details.size();
-
-  ExprResult Out;
-  if (FE->isLeftFold() && FE->getInit()) {
-    Out = calculateConstraintSatisfaction(S, FE->getInit(), Template,
-                                          TemplateNameLoc, MLTAL, Satisfaction);
-    if (Out.isInvalid())
-      return ExprError();
-
-    // If the first clause of a conjunction is not satisfied,
-    // or if the first clause of a disjection is satisfied,
-    // we have established satisfaction of the whole constraint
-    // and we should not continue further.
-    if (Conjunction != Satisfaction.IsSatisfied)
-      return Out;
-  }
-  UnsignedOrNone NumExpansions = EvaluateFoldExpandedConstraintSize(
-      S, FE, Template, TemplateNameLoc, MLTAL, Satisfaction);
-  if (!NumExpansions)
-    return ExprError();
-  for (unsigned I = 0; I < *NumExpansions; I++) {
-    Sema::ArgPackSubstIndexRAII SubstIndex(S, I);
-    ExprResult Res = calculateConstraintSatisfaction(
-        S, FE->getPattern(), Template, TemplateNameLoc, MLTAL, Satisfaction);
-    if (Res.isInvalid())
-      return ExprError();
-    bool IsRHSSatisfied = Satisfaction.IsSatisfied;
-    if (!Conjunction && IsRHSSatisfied) {
-      auto EffectiveDetailEnd = Satisfaction.Details.begin();
-      std::advance(EffectiveDetailEnd, EffectiveDetailEndIndex);
-      Satisfaction.Details.erase(EffectiveDetailEnd,
-                                 Satisfaction.Details.end());
-    }
-    if (Out.isUnset())
-      Out = Res;
-    else if (!Res.isUnset()) {
-      Out = BinaryOperator::Create(
-          S.Context, Out.get(), Res.get(), FE->getOperator(), S.Context.BoolTy,
-          VK_PRValue, OK_Ordinary, FE->getBeginLoc(), FPOptionsOverride{});
-    }
-    if (Conjunction != IsRHSSatisfied)
-      return Out;
-  }
-
-  if (FE->isRightFold() && FE->getInit()) {
-    ExprResult Res = calculateConstraintSatisfaction(
-        S, FE->getInit(), Template, TemplateNameLoc, MLTAL, Satisfaction);
-    if (Out.isInvalid())
-      return ExprError();
-
-    if (Out.isUnset())
-      Out = Res;
-    else if (!Res.isUnset()) {
-      Out = BinaryOperator::Create(
-          S.Context, Out.get(), Res.get(), FE->getOperator(), S.Context.BoolTy,
-          VK_PRValue, OK_Ordinary, FE->getBeginLoc(), FPOptionsOverride{});
-    }
-  }
-
-  if (Out.isUnset()) {
-    Satisfaction.IsSatisfied = Conjunction;
-    Out = S.BuildEmptyCXXFoldExpr(FE->getBeginLoc(), FE->getOperator());
+  TemplateArgumentListInfo SubstArgs;
+  if (Constraint.hasParameterMapping()) {
+    if (S.SubstTemplateArgumentsInParameterMapping(
+            Constraint.getParameterMapping(), MLTAL, SubstArgs) ||
+        Trap.hasErrorOccurred())
+      return std::nullopt;
+    NormalizedConstraint::OccurenceList Used =
+        Constraint.mappingOccurenceList();
+    SubstitutedOuterMost =
+        llvm::to_vector_of<TemplateArgument>(MLTAL.getOutermost());
+    for (unsigned I = 0, MappedIndex = 0; I < SubstArgs.size(); I++)
+      if (I < Used.size() && Used[I]) {
+        // SubstitutedOuterMost[I].dump();
+        // SubstArgs[MappedIndex].getArgument().dump();
+        if (I < SubstitutedOuterMost.size())
+          SubstitutedOuterMost[I] = SubstArgs[MappedIndex++].getArgument();
+        else
+          SubstitutedOuterMost.push_back(
+              SubstArgs[MappedIndex++].getArgument());
+      }
+    MLTAL.replaceOutermostTemplateArguments(
+        const_cast<NamedDecl *>(Constraint.getConstraintDecl()),
+        SubstitutedOuterMost);
   }
-  return Out;
+  return std::move(MLTAL);
 }
 
-static ExprResult calculateConstraintSatisfaction(
-    Sema &S, const Expr *ConstraintExpr, const NamedDecl *Template,
+static bool calculateConstraintSatisfaction(
+    Sema &S, const AtomicConstraint &Constraint, const NamedDecl *Template,
     SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
-    ConstraintSatisfaction &Satisfaction) {
-  ConstraintExpr = ConstraintExpr->IgnoreParenImpCasts();
-
-  if (LogicalBinOp BO = ConstraintExpr)
-    return calculateConstraintSatisfaction(
-        S, BO.getLHS(), BO.getOp(), BO.getRHS(), Template, TemplateNameLoc,
-        MLTAL, Satisfaction);
+    ConstraintSatisfaction &Satisfaction,
+    UnsignedOrNone PackSubstitutionIndex) {
+
+  llvm::SmallVector<TemplateArgument> SubstitutedOuterMost;
+  std::optional<MultiLevelTemplateArgumentList> SubstitutedArgs =
+      SubstitutionInTemplateArguments(S, Constraint, Template, MLTAL,
+                                      SubstitutedOuterMost);
+  if (!SubstitutedArgs)
+    return false;
 
-  if (auto *C = dyn_cast<ExprWithCleanups>(ConstraintExpr)) {
-    // These aren't evaluated, so we don't care about cleanups, so we can just
-    // evaluate these as if the cleanups didn't exist.
-    return calculateConstraintSatisfaction(
-        S, C->getSubExpr(), Template, TemplateNameLoc, MLTAL, Satisfaction);
-  }
+  Sema::ArgPackSubstIndexRAII(S, PackSubstitutionIndex);
+  ExprResult SubstitutedAtomicExpr =
+      EvaluateAtomicConstraint(S, Constraint.getConstraintExpr(), Template,
+                               TemplateNameLoc, *SubstitutedArgs, Satisfaction);
 
-  if (auto *FE = dyn_cast<CXXFoldExpr>(ConstraintExpr);
-      FE && S.getLangOpts().CPlusPlus26 &&
-      (FE->getOperator() == BinaryOperatorKind::BO_LAnd ||
-       FE->getOperator() == BinaryOperatorKind::BO_LOr)) {
-    return calculateConstraintSatisfaction(S, FE, Template, TemplateNameLoc,
-                                           MLTAL, Satisfaction);
+  if (SubstitutedAtomicExpr.isInvalid()) {
+    return false;
   }
 
-  // FIXME: We should not treat ConceptSpecializationExpr as atomic constraints.
-
-  // An atomic constraint expression
-  ExprResult SubstitutedAtomicExpr = EvaluateAtomicConstraint(
-      S, ConstraintExpr, Template, TemplateNameLoc, MLTAL, Satisfaction);
-
-  if (SubstitutedAtomicExpr.isInvalid())
-    return ExprError();
-
   if (!SubstitutedAtomicExpr.isUsable())
     // Evaluator has decided satisfaction without yielding an expression.
-    return ExprEmpty();
+    return true;
 
   // We don't have the ability to evaluate this, since it contains a
   // RecoveryExpr, so we want to fail overload resolution.  Otherwise,
@@ -521,7 +448,13 @@ static ExprResult calculateConstraintSatisfaction(
         new (S.Context) ConstraintSatisfaction::SubstitutionDiagnostic{
             SubstitutedAtomicExpr.get()->getBeginLoc(),
             StringRef(Mem, MessageSize)});
-    return SubstitutedAtomicExpr;
+    return true;
+  }
+
+  if (SubstitutedAtomicExpr.get()->isValueDependent()) {
+    Satisfaction.IsSatisfied = true;
+    Satisfaction.ContainsErrors = false;
+    return true;
   }
 
   EnterExpressionEvaluationContext ConstantEvaluated(
@@ -539,7 +472,7 @@ static ExprResult calculateConstraintSatisfaction(
         << SubstitutedAtomicExpr.get()->getSourceRange();
     for (const PartialDiagnosticAt &PDiag : EvaluationDiags)
       S.Diag(PDiag.first, PDiag.second);
-    return ExprError();
+    return false;
   }
 
   assert(EvalResult.Val.isInt() &&
@@ -548,24 +481,215 @@ static ExprResult calculateConstraintSatisfaction(
   if (!Satisfaction.IsSatisfied)
     Satisfaction.Details.emplace_back(SubstitutedAtomicExpr.get());
 
-  return SubstitutedAtomicExpr;
+  return SubstitutedAtomicExpr.isUsable();
 }
 
-static ExprResult calculateConstraintSatisfaction(
-    Sema &S, const NamedDecl *Template, SourceLocation TemplateNameLoc,
-    const MultiLevelTemplateArgumentList &MLTAL, const Expr *ConstraintExpr,
+static UnsignedOrNone EvaluateFoldExpandedConstraintSize(
+    Sema &S, const FoldExpandedConstraint &FE, const NamedDecl *Template,
+    SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
     ConstraintSatisfaction &Satisfaction) {
 
-  return calculateConstraintSatisfaction(S, ConstraintExpr, Template,
-                                         TemplateNameLoc, MLTAL, Satisfaction);
+  // We should ignore errors in the presence of packs of different size.
+  Sema::SFINAETrap Trap(S);
+
+  Expr *Pattern = const_cast<Expr *>(FE.getPattern());
+
+  SmallVector<UnexpandedParameterPack, 2> Unexpanded;
+  S.collectUnexpandedParameterPacks(Pattern, Unexpanded);
+  assert(!Unexpanded.empty() && "Pack expansion without parameter packs?");
+  bool Expand = true;
+  bool RetainExpansion = false;
+  UnsignedOrNone NumExpansions(std::nullopt);
+  if (S.CheckParameterPacksForExpansion(
+          Pattern->getExprLoc(), Pattern->getSourceRange(), Unexpanded, MLTAL,
+          Expand, RetainExpansion, NumExpansions) ||
+      !Expand || RetainExpansion)
+    return std::nullopt;
+
+  if (NumExpansions && S.getLangOpts().BracketDepth < *NumExpansions) {
+    S.Diag(Pattern->getExprLoc(),
+           clang::diag::err_fold_expression_limit_exceeded)
+        << *NumExpansions << S.getLangOpts().BracketDepth
+        << Pattern->getSourceRange();
+    S.Diag(Pattern->getExprLoc(), diag::note_bracket_depth);
+    return std::nullopt;
+  }
+  return NumExpansions;
+}
+
+static bool calculateConstraintSatisfaction(
+    Sema &S, const FoldExpandedConstraint &FE, const NamedDecl *Template,
+    SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
+    ConstraintSatisfaction &Satisfaction) {
+
+  bool Conjunction =
+      FE.getFoldOperator() == FoldExpandedConstraint::FoldOperatorKind::And;
+  size_t EffectiveDetailEndIndex = Satisfaction.Details.size();
+
+  llvm::SmallVector<TemplateArgument> SubstitutedOuterMost;
+  std::optional<MultiLevelTemplateArgumentList> SubstitutedArgs =
+      SubstitutionInTemplateArguments(
+          S,
+          static_cast<const NormalizedConstraintWithParamMapping &>(
+              FE.getNormalizedPattern()),
+          Template, MLTAL, SubstitutedOuterMost);
+  if (!SubstitutedArgs)
+    return false;
+
+  ExprResult Out;
+  UnsignedOrNone NumExpansions = EvaluateFoldExpandedConstraintSize(
+      S, FE, Template, TemplateNameLoc, *SubstitutedArgs, Satisfaction);
+  if (!NumExpansions)
+    return false;
+
+  for (unsigned I = 0; I < *NumExpansions; I++) {
+    Sema::ArgPackSubstIndexRAII SubstIndex(S, I);
+    bool Success = calculateConstraintSatisfaction(
+        S, FE.getNormalizedPattern(), Template, TemplateNameLoc,
+        *SubstitutedArgs, Satisfaction, UnsignedOrNone(I));
+    if (!Success)
+      return false;
+    bool IsRHSSatisfied = Satisfaction.IsSatisfied;
+    if (!Conjunction && IsRHSSatisfied) {
+      auto EffectiveDetailEnd = Satisfaction.Details.begin();
+      std::advance(EffectiveDetailEnd, EffectiveDetailEndIndex);
+      Satisfaction.Details.erase(EffectiveDetailEnd,
+                                 Satisfaction.Details.end());
+      break;
+    }
+  }
+  return true;
+}
+
+static bool calculateConstraintSatisfaction(
+    Sema &S, const ConceptIdConstraint &Constraint, const NamedDecl *Template,
+    SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
+    ConstraintSatisfaction &Satisfaction,
+    UnsignedOrNone PackSubstitutionIndex) {
+
+  llvm::SmallVector<TemplateArgument> SubstitutedOuterMost;
+  std::optional<MultiLevelTemplateArgumentList> SubstitutedArgs =
+      SubstitutionInTemplateArguments(S, Constraint, Template, MLTAL,
+                                      SubstitutedOuterMost);
+
+  Sema::InstantiatingTemplate Tpl(
+      S, Constraint.getConceptId()->getBeginLoc(),
+      Sema::InstantiatingTemplate::ConstraintsCheck{},
+      Constraint.getConceptId()->getNamedConcept(), MLTAL.getInnermost(),
+      Constraint.getSourceRange());
+
+  auto Size = Satisfaction.Details.size();
+
+  bool Ok = calculateConstraintSatisfaction(
+      S, Constraint.getNormalizedConstraint(), Template, TemplateNameLoc, MLTAL,
+      Satisfaction, PackSubstitutionIndex);
+
+  if (Size != Satisfaction.Details.size()) {
+
+    if (!SubstitutedArgs)
+      return Ok;
+
+    Sema::SFINAETrap Trap(S);
+
+    const ASTTemplateArgumentListInfo *Ori =
+        Constraint.getConceptId()->getTemplateArgsAsWritten();
+    TemplateArgumentListInfo OutArgs(Ori->LAngleLoc, Ori->RAngleLoc);
+    if (Template && Template->getTemplateDepth() > 0) {
+      TemplateArgumentListInfo TransArgs(Ori->LAngleLoc, Ori->RAngleLoc);
+      AdjustConstraintDepth Adjust(S, Template->getTemplateDepth() - 1);
+      if (Adjust.TransformTemplateArguments(Ori->getTemplateArgs(),
+                                            Ori->NumTemplateArgs, TransArgs))
+        return Ok;
+      if (S.SubstTemplateArguments(TransArgs.arguments(), MLTAL, OutArgs))
+        return Ok;
+    } else {
+      if (S.SubstTemplateArguments(Ori->arguments(), MLTAL, OutArgs))
+        return Ok;
+    }
+
+    CXXScopeSpec SS;
+    SS.Adopt(Constraint.getConceptId()->getNestedNameSpecifierLoc());
+    ExprResult SubstitutedConceptId = S.CheckConceptTemplateId(
+        SS, Constraint.getConceptId()->getTemplateKWLoc(),
+        Constraint.getConceptId()->getConceptNameInfo(),
+        Constraint.getConceptId()->getFoundDecl(),
+        Constraint.getConceptId()->getNamedConcept(), &OutArgs,
+        /*CheckConstraintSatisfaction=*/false);
+
+    if (SubstitutedConceptId.isInvalid() || Trap.hasErrorOccurred())
+      return Ok;
+
+    Satisfaction.Details.insert(
+        Satisfaction.Details.begin() + Size,
+        ConstraintSatisfaction::Detail(SubstitutedConceptId.get()));
+
+    Satisfaction.Details.push_back(nullptr);
+  }
+  return Ok;
+}
+
+static bool calculateConstraintSatisfaction(
+    Sema &S, const CompoundConstraint &Constraint, const NamedDecl *Template,
+    SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
+    ConstraintSatisfaction &Satisfaction,
+    UnsignedOrNone PackSubstitutionIndex) {
+
+  bool Ok = calculateConstraintSatisfaction(
+      S, Constraint.getLHS(), Template, TemplateNameLoc, MLTAL, Satisfaction,
+      PackSubstitutionIndex);
+
+  if (!Ok || Satisfaction.ContainsErrors)
+    return false;
+
+  if (Satisfaction.IsSatisfied &&
+      Constraint.getCompoundKind() == NormalizedConstraint::CCK_Disjunction) {
+    return true;
+  }
+  if (!Satisfaction.IsSatisfied &&
+      Constraint.getCompoundKind() == NormalizedConstraint::CCK_Conjunction)
+    return true;
+
+  return calculateConstraintSatisfaction(S, Constraint.getRHS(), Template,
+                                         TemplateNameLoc, MLTAL, Satisfaction,
+                                         PackSubstitutionIndex);
+}
+
+static bool calculateConstraintSatisfaction(
+    Sema &S, const NormalizedConstraint &Constraint, const NamedDecl *Template,
+    SourceLocation TemplateNameLoc, const MultiLevelTemplateArgumentList &MLTAL,
+    ConstraintSatisfaction &Satisfaction,
+    UnsignedOrNone PackSubstitutionIndex) {
+
+  switch (Constraint.getKind()) {
+  case NormalizedConstraint::ConstraintKind::Atomic:
+    return calculateConstraintSatisfaction(
+        S, static_cast<const AtomicConstraint &>(Constraint), Template,
+        TemplateNameLoc, MLTAL, Satisfaction, PackSubstitutionIndex);
+
+  case NormalizedConstraint::ConstraintKind::FoldExpanded:
+    return calculateConstraintSatisfaction(
+        S, static_cast<const FoldExpandedConstraint &>(Constraint), Template,
+        TemplateNameLoc, MLTAL, Satisfaction);
+
+  case NormalizedConstraint::ConstraintKind::ConceptId:
+    return calculateConstraintSatisfaction(
+        S, static_cast<const ConceptIdConstraint &>(Constraint), Template,
+        TemplateNameLoc, MLTAL, Satisfaction, PackSubstitutionIndex);
+
+  case NormalizedConstraint::ConstraintKind::Compound:
+    return calculateConstraintSatisfaction(
+        S, static_cast<const CompoundConstraint &>(Constraint), Template,
+        TemplateNameLoc, MLTAL, Satisfaction, PackSubstitutionIndex);
+  }
 }
 
 static bool CheckConstraintSatisfaction(
     Sema &S, const NamedDecl *Template,
     ArrayRef<AssociatedConstraint> AssociatedConstraints,
-    llvm::SmallVectorImpl<Expr *> &Converted,
     const MultiLevelTemplateArgumentList &TemplateArgsLists,
-    SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction) {
+    SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction,
+    const ConceptReference *TopLevelConceptId = nullptr) {
+
   if (AssociatedConstraints.empty()) {
     Satisfaction.IsSatisfied = true;
     return false;
@@ -577,54 +701,51 @@ static bool CheckConstraintSatisfaction(
     return false;
   }
 
-  ArrayRef<TemplateArgument> TemplateArgs =
-      TemplateArgsLists.getNumSubstitutedLevels() > 0
-          ? TemplateArgsLists.getOutermost()
-          : ArrayRef<TemplateArgument>{};
-  Sema::InstantiatingTemplate Inst(S, TemplateIDRange.getBegin(),
-      Sema::InstantiatingTemplate::ConstraintsCheck{},
-      const_cast<NamedDecl *>(Template), TemplateArgs, TemplateIDRange);
-  if (Inst.isInvalid())
-    return true;
+  llvm::ArrayRef<TemplateArgument> Args;
+  if (TemplateArgsLists.getNumLevels() != 0)
+    Args = TemplateArgsLists.getInnermost();
 
-  for (const AssociatedConstraint &AC : AssociatedConstraints) {
-    Sema::ArgPackSubstIndexRAII _(S, AC.ArgPackSubstIndex);
-    ExprResult Res = calculateConstraintSatisfaction(
-        S, Template, TemplateIDRange.getBegin(), TemplateArgsLists,
-        AC.ConstraintExpr, Satisfaction);
-    if (Res.isInvalid())
-      return true;
+  std::optional<Sema::InstantiatingTemplate> SynthesisContext;
+  if (!TopLevelConceptId) {
+    SynthesisContext.emplace(S, TemplateIDRange.getBegin(),
+                             Sema::InstantiatingTemplate::ConstraintsCheck{},
+                             const_cast<NamedDecl *>(Template), Args,
+                             TemplateIDRange);
+  }
 
-    Converted.push_back(Res.get());
-    if (!Satisfaction.IsSatisfied) {
-      // Backfill the 'converted' list with nulls so we can keep the Converted
-      // and unconverted lists in sync.
-      Converted.append(AssociatedConstraints.size() - Converted.size(),
-                       nullptr);
-      // [temp.constr.op] p2
-      // [...] To determine if a conjunction is satisfied, the satisfaction
-      // of the first operand is checked. If that is not satisfied, the
-      // conjunction is not satisfied. [...]
-      return false;
-    }
+  const NormalizedConstraint *C =
+      S.getNormalizedAssociatedConstraints(Template, AssociatedConstraints);
+  if (!C) {
+    Satisfaction.IsSatisfied = false;
+    return true;
   }
-  return false;
+
+  if (TopLevelConceptId) {
+    C = ConceptIdConstraint::Create(S.getASTContext(), TopLevelConceptId,
+                                    const_cast<NormalizedConstraint *>(C),
+                                    Template, S.ArgPackSubstIndex);
+  }
+
+  return !calculateConstraintSatisfaction(
+      S, *C, Template, TemplateIDRange.getBegin(), TemplateArgsLists,
+      Satisfaction, S.ArgPackSubstIndex);
 }
 
 bool Sema::CheckConstraintSatisfaction(
-    const NamedDecl *Template,
+    ConstrainedDeclOrNestedRequirement Entity,
     ArrayRef<AssociatedConstraint> AssociatedConstraints,
-    llvm::SmallVectorImpl<Expr *> &ConvertedConstraints,
     const MultiLevelTemplateArgumentList &TemplateArgsLists,
-    SourceRange TemplateIDRange, ConstraintSatisfaction &OutSatisfaction) {
+    SourceRange TemplateIDRange, ConstraintSatisfaction &OutSatisfaction,
+    const ConceptReference *TopLevelConceptId) {
   if (AssociatedConstraints.empty()) {
     OutSatisfaction.IsSatisfied = true;
     return false;
   }
+  const auto *Template = Entity.dyn_cast<const NamedDecl *>();
   if (!Template) {
-    return ::CheckConstraintSatisfaction(
-        *this, nullptr, AssociatedConstraints, ConvertedConstraints,
-        TemplateArgsLists, TemplateIDRange, OutSatisfaction);
+    return ::CheckConstraintSatisfaction(*this, nullptr, AssociatedConstraints,
+                                         TemplateArgsLists, TemplateIDRange,
+                                         OutSatisfaction, TopLevelConceptId);
   }
   // Invalid templates could make their way here. Substituting them could result
   // in dependent expressions.
@@ -641,8 +762,12 @@ bool Sema::CheckConstraintSatisfaction(
   for (auto List : TemplateArgsLists)
     llvm::append_range(FlattenedArgs, List.Args);
 
+  const NamedDecl *Owner = Template;
+  if (TopLevelConceptId)
+    Owner = TopLevelConceptId->getNamedConcept();
+
   llvm::FoldingSetNodeID ID;
-  ConstraintSatisfaction::Profile(ID, Context, Template, FlattenedArgs);
+  ConstraintSatisfaction::Profile(ID, Context, Owner, FlattenedArgs);
   void *InsertPos;
   if (auto *Cached = SatisfactionCache.FindNodeOrInsertPos(ID, InsertPos)) {
     OutSatisfaction = *Cached;
@@ -650,10 +775,10 @@ bool Sema::CheckConstraintSatisfaction(
   }
 
   auto Satisfaction =
-      std::make_unique<ConstraintSatisfaction>(Template, FlattenedArgs);
+      std::make_unique<ConstraintSatisfaction>(Owner, FlattenedArgs);
   if (::CheckConstraintSatisfaction(*this, Template, AssociatedConstraints,
-                                    ConvertedConstraints, TemplateArgsLists,
-                                    TemplateIDRange, *Satisfaction)) {
+                                    TemplateArgsLists, TemplateIDRange,
+                                    *Satisfaction, TopLevelConceptId)) {
     OutSatisfaction = *Satisfaction;
     return true;
   }
@@ -684,14 +809,18 @@ bool Sema::CheckConstraintSatisfaction(
     const ConceptSpecializationExpr *ConstraintExpr,
     ConstraintSatisfaction &Satisfaction) {
 
+  llvm::SmallVector<AssociatedConstraint, 1> Constraints;
+  Constraints.emplace_back(
+      ConstraintExpr->getNamedConcept()->getConstraintExpr());
+
   MultiLevelTemplateArgumentList MLTAL(ConstraintExpr->getNamedConcept(),
                                        ConstraintExpr->getTemplateArguments(),
                                        true);
 
-  return calculateConstraintSatisfaction(
-             *this, ConstraintExpr, ConstraintExpr->getNamedConcept(),
-             ConstraintExpr->getConceptNameLoc(), MLTAL, Satisfaction)
-      .isInvalid();
+  return CheckConstraintSatisfaction(
+      ConstraintExpr->getNamedConcept(), Constraints, MLTAL,
+      ConstraintExpr->getSourceRange(), Satisfaction,
+      ConstraintExpr->getConceptReference());
 }
 
 bool Sema::SetupConstraintScope(
@@ -850,50 +979,6 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
       Satisfaction);
 }
 
-
-// Figure out the to-translation-unit depth for this function declaration for
-// the purpose of seeing if they differ by constraints. This isn't the same as
-// getTemplateDepth, because it includes already instantiated parents.
-static unsigned
-CalculateTemplateDepthForConstraints(Sema &S, const NamedDecl *ND,
-                                     bool SkipForSpecialization = false) {
-  MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
-      ND, ND->getLexicalDeclContext(), /*Final=*/false,
-      /*Innermost=*/std::nullopt,
-      /*RelativeToPrimary=*/true,
-      /*Pattern=*/nullptr,
-      /*ForConstraintInstantiation=*/true, SkipForSpecialization);
-  return MLTAL.getNumLevels();
-}
-
-namespace {
-  class AdjustConstraintDepth : public TreeTransform<AdjustConstraintDepth> {
-  unsigned TemplateDepth = 0;
-  public:
-  using inherited = TreeTransform<AdjustConstraintDepth>;
-  AdjustConstraintDepth(Sema &SemaRef, unsigned TemplateDepth)
-      : inherited(SemaRef), TemplateDepth(TemplateDepth) {}
-
-  using inherited::TransformTemplateTypeParmType;
-  QualType TransformTemplateTypeParmType(TypeLocBuilder &TLB,
-                                         TemplateTypeParmTypeLoc TL, bool) {
-    const TemplateTypeParmType *T = TL.getTypePtr();
-
-    TemplateTypeParmDecl *NewTTPDecl = nullptr;
-    if (TemplateTypeParmDecl *OldTTPDecl = T->getDecl())
-      NewTTPDecl = cast_or_null<TemplateTypeParmDecl>(
-          TransformDecl(TL.getNameLoc(), OldTTPDecl));
-
-    QualType Result = getSema().Context.getTemplateTypeParmType(
-        T->getDepth() + TemplateDepth, T->getIndex(), T->isParameterPack(),
-        NewTTPDecl);
-    TemplateTypeParmTypeLoc NewTL = TLB.push<TemplateTypeParmTypeLoc>(Result);
-    NewTL.setNameLoc(TL.getNameLoc());
-    return Result;
-  }
-  };
-} // namespace
-
 static const Expr *SubstituteConstraintExpressionWithoutSatisfaction(
     Sema &S, const Sema::TemplateCompareNewDeclInfo &DeclInfo,
     const Expr *ConstrExpr) {
@@ -1146,20 +1231,7 @@ static void diagnoseUnsatisfiedRequirement(Sema &S,
     case concepts::ExprRequirement::SS_ConstraintsNotSatisfied: {
       ConceptSpecializationExpr *ConstraintExpr =
           Req->getReturnTypeRequirementSubstitutedConstraintExpr();
-      if (ConstraintExpr->getTemplateArgsAsWritten()->NumTemplateArgs == 1) {
-        // A simple case - expr type is the type being constrained and the concept
-        // was not provided arguments.
-        Expr *e = Req->getExpr();
-        S.Diag(e->getBeginLoc(),
-               diag::note_expr_requirement_constraints_not_satisfied_simple)
-            << (int)First << S.Context.getReferenceQualifiedType(e)
-            << ConstraintExpr->getNamedConcept();
-      } else {
-        S.Diag(ConstraintExpr->getBeginLoc(),
-               diag::note_expr_requirement_constraints_not_satisfied)
-            << (int)First << ConstraintExpr;
-      }
-      S.DiagnoseUnsatisfiedConstraint(ConstraintExpr->getSatisfaction());
+      S.DiagnoseUnsatisfiedConstraint(ConstraintExpr);
       break;
     }
     case concepts::ExprRequirement::SS_Satisfied:
@@ -1193,31 +1265,63 @@ static void diagnoseUnsatisfiedRequirement(Sema &S,
     return;
   }
 }
+
+static void
+diagnoseUnsatisfiedConceptIdExpr(Sema &S, const ConceptSpecializationExpr *CSE,
+                                 SourceLocation Loc, bool First) {
+  if (CSE->getTemplateArgsAsWritten()->NumTemplateArgs == 1) {
+    S.Diag(
+        Loc,
+        diag::
+            note_single_arg_concept_specialization_constraint_evaluated_to_false)
+        << (int)First
+        << CSE->getTemplateArgsAsWritten()->arguments()[0].getArgument()
+        << CSE->getNamedConcept();
+  } else {
+    S.Diag(Loc, diag::note_concept_specialization_constraint_evaluated_to_false)
+        << (int)First << CSE;
+  }
+}
+
 static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
-                                                        Expr *SubstExpr,
+                                                        const Expr *SubstExpr,
                                                         bool First = true);
 
 static void diagnoseUnsatisfiedRequirement(Sema &S,
                                            concepts::NestedRequirement *Req,
                                            bool First) {
   using SubstitutionDiagnostic = std::pair<SourceLocation, StringRef>;
+  std::vector<bool> Prevs;
   for (auto &Record : Req->getConstraintSatisfaction()) {
     if (auto *SubstDiag = Record.dyn_cast<SubstitutionDiagnostic *>())
       S.Diag(SubstDiag->first, diag::note_nested_requirement_substitution_error)
           << (int)First << Req->getInvalidConstraintEntity()
           << SubstDiag->second;
-    else
-      diagnoseWellFormedUnsatisfiedConstraintExpr(S, Record.dyn_cast<Expr *>(),
-                                                  First);
+    else {
+      if (Record.isNull()) {
+        First = Prevs.back();
+        Prevs.pop_back();
+        continue;
+      }
+      diagnoseWellFormedUnsatisfiedConstraintExpr(
+          S, Record.dyn_cast<const Expr *>(), First);
+      if (isa_and_nonnull<ConceptSpecializationExpr>(
+              Record.dyn_cast<const Expr *>())) {
+        Prevs.push_back(First);
+        First = true;
+        continue;
+      }
+      First = false;
+    }
     First = false;
   }
 }
 
 static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
-                                                        Expr *SubstExpr,
+                                                        const Expr *SubstExpr,
                                                         bool First) {
   SubstExpr = SubstExpr->IgnoreParenImpCasts();
-  if (BinaryOperator *BO = dyn_cast<BinaryOperator>(SubstExpr)) {
+  if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(SubstExpr)) {
     switch (BO->getOpcode()) {
     // These two cases will in practice only be reached when using fold
     // expressions with || and &&, since otherwise the || and && will have been
@@ -1278,22 +1382,6 @@ static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
     default:
       break;
     }
-  } else if (auto *CSE = dyn_cast<ConceptSpecializationExpr>(SubstExpr)) {
-    if (CSE->getTemplateArgsAsWritten()->NumTemplateArgs == 1) {
-      S.Diag(
-          CSE->getSourceRange().getBegin(),
-          diag::
-          note_single_arg_concept_specialization_constraint_evaluated_to_false)
-          << (int)First
-          << CSE->getTemplateArgsAsWritten()->arguments()[0].getArgument()
-          << CSE->getNamedConcept();
-    } else {
-      S.Diag(SubstExpr->getSourceRange().getBegin(),
-             diag::note_concept_specialization_constraint_evaluated_to_false)
-          << (int)First << CSE;
-    }
-    S.DiagnoseUnsatisfiedConstraint(CSE->getSatisfaction());
-    return;
   } else if (auto *RE = dyn_cast<RequiresExpr>(SubstExpr)) {
     // FIXME: RequiresExpr should store dependent diagnostics.
     for (concepts::Requirement *Req : RE->getRequirements())
@@ -1323,101 +1411,132 @@ static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
   S.DiagnoseTypeTraitDetails(SubstExpr);
 }
 
-template <typename SubstitutionDiagnostic>
-static void diagnoseUnsatisfiedConstraintExpr(
-    Sema &S, const llvm::PointerUnion<Expr *, SubstitutionDiagnostic *> &Record,
-    bool First = true) {
-  if (auto *Diag = Record.template dyn_cast<SubstitutionDiagnostic *>()) {
+static void
+diagnoseUnsatisfiedConstraintExpr(Sema &S,
+                                  const ConstraintSatisfaction::Detail &Record,
+                                  SourceLocation Loc, bool First = true) {
+  if (auto *Diag = Record.template dyn_cast<
+                   ConstraintSatisfaction::SubstitutionDiagnostic *>()) {
     S.Diag(Diag->first, diag::note_substituted_constraint_expr_is_ill_formed)
         << Diag->second;
     return;
   }
 
-  diagnoseWellFormedUnsatisfiedConstraintExpr(S, cast<Expr *>(Record), First);
+  const auto *Expr = cast<const class Expr *>(Record);
+  if (const auto *CSE = dyn_cast<ConceptSpecializationExpr>(Expr)) {
+    if (Loc.isInvalid())
+      Loc = CSE->getBeginLoc();
+    diagnoseUnsatisfiedConceptIdExpr(S, CSE, Loc, First);
+    return;
+  }
+  diagnoseWellFormedUnsatisfiedConstraintExpr(S, Expr, First);
 }
 
-void
-Sema::DiagnoseUnsatisfiedConstraint(const ConstraintSatisfaction& Satisfaction,
-                                    bool First) {
+void Sema::DiagnoseUnsatisfiedConstraint(
+    const ConstraintSatisfaction &Satisfaction, SourceLocation Loc,
+    bool First) {
   assert(!Satisfaction.IsSatisfied &&
          "Attempted to diagnose a satisfied constraint");
+  std::vector<bool> Prevs;
   for (auto &Record : Satisfaction.Details) {
-    diagnoseUnsatisfiedConstraintExpr(*this, Record, First);
+    if (Record.isNull()) {
+      First = Prevs.back();
+      Prevs.pop_back();
+      continue;
+    }
+    diagnoseUnsatisfiedConstraintExpr(*this, Record, Loc, First);
     First = false;
+    Loc = {};
+    if (isa_and_nonnull<ConceptSpecializationExpr>(
+            Record.dyn_cast<const Expr *>())) {
+      Prevs.push_back(First);
+      First = true;
+    }
   }
 }
 
 void Sema::DiagnoseUnsatisfiedConstraint(
-    const ASTConstraintSatisfaction &Satisfaction,
-    bool First) {
+    const ConceptSpecializationExpr *ConstraintExpr) {
+  const ASTConstraintSatisfaction &Satisfaction =
+      ConstraintExpr->getSatisfaction();
+  bool First = true;
+  SourceLocation Loc = ConstraintExpr->getBeginLoc();
   assert(!Satisfaction.IsSatisfied &&
          "Attempted to diagnose a satisfied constraint");
+  std::vector<bool> Prevs;
   for (auto &Record : Satisfaction) {
-    diagnoseUnsatisfiedConstraintExpr(*this, Record, First);
-    First = false;
+    if (Record.isNull()) {
+      First = Prevs.back();
+      Prevs.pop_back();
+      continue;
+    }
+    diagnoseUnsatisfiedConstraintExpr(*this, Record, Loc, First);
+    Loc = {};
+    if (isa_and_nonnull<ConceptSpecializationExpr>(
+            Record.dyn_cast<const Expr *>())) {
+      Prevs.push_back(First);
+      First = true;
+      continue;
+    }
   }
 }
 
 const NormalizedConstraint *Sema::getNormalizedAssociatedConstraints(
-    const NamedDecl *ConstrainedDecl,
+    ConstrainedDeclOrNestedRequirement ConstrainedDeclOrNestedReq,
     ArrayRef<AssociatedConstraint> AssociatedConstraints) {
   // In case the ConstrainedDecl comes from modules, it is necessary to use
   // the canonical decl to avoid different atomic constraints with the 'same'
   // declarations.
-  ConstrainedDecl = cast<NamedDecl>(ConstrainedDecl->getCanonicalDecl());
 
-  auto CacheEntry = NormalizationCache.find(ConstrainedDecl);
+  if (!ConstrainedDeclOrNestedReq)
+    return NormalizedConstraint::fromAssociatedConstraints(
+        *this, nullptr, AssociatedConstraints);
+
+  const NamedDecl *ND =
+      ConstrainedDeclOrNestedReq.dyn_cast<const NamedDecl *>();
+  if (ND)
+    ND = cast<NamedDecl>(ND->getCanonicalDecl());
+
+  auto CacheEntry = NormalizationCache.find(ConstrainedDeclOrNestedReq);
   if (CacheEntry == NormalizationCache.end()) {
     auto Normalized = NormalizedConstraint::fromAssociatedConstraints(
-        *this, ConstrainedDecl, AssociatedConstraints);
+        *this, ND, AssociatedConstraints);
     CacheEntry =
-        NormalizationCache
-            .try_emplace(ConstrainedDecl,
-                         Normalized
-                             ? new (Context) NormalizedConstraint(
-                                 std::move(*Normalized))
-                             : nullptr)
+        NormalizationCache.try_emplace(ConstrainedDeclOrNestedReq, Normalized)
             .first;
   }
   return CacheEntry->second;
 }
 
-const NormalizedConstraint *clang::getNormalizedAssociatedConstraints(
-    Sema &S, const NamedDecl *ConstrainedDecl,
-    ArrayRef<AssociatedConstraint> AssociatedConstraints) {
-  return S.getNormalizedAssociatedConstraints(ConstrainedDecl,
-                                              AssociatedConstraints);
-}
-
 static bool
 substituteParameterMappings(Sema &S, NormalizedConstraint &N,
-                            ConceptDecl *Concept,
                             const MultiLevelTemplateArgumentList &MLTAL,
-                            const ASTTemplateArgumentListInfo *ArgsAsWritten) {
+                            const ASTTemplateArgumentListInfo *ArgsAsWritten);
 
-  if (N.isCompound()) {
-    if (substituteParameterMappings(S, N.getLHS(), Concept, MLTAL,
-                                    ArgsAsWritten))
-      return true;
-    return substituteParameterMappings(S, N.getRHS(), Concept, MLTAL,
-                                       ArgsAsWritten);
-  }
-
-  if (N.isFoldExpanded()) {
-    Sema::ArgPackSubstIndexRAII _(S, std::nullopt);
-    return substituteParameterMappings(
-        S, N.getFoldExpandedConstraint()->Constraint, Concept, MLTAL,
-        ArgsAsWritten);
-  }
-
-  TemplateParameterList *TemplateParams = Concept->getTemplateParameters();
+static bool
+substituteParameterMappings(Sema &S, NormalizedConstraintWithParamMapping &N,
+                            const MultiLevelTemplateArgumentList &MLTAL,
+                            const ASTTemplateArgumentListInfo *ArgsAsWritten) {
 
-  AtomicConstraint &Atomic = *N.getAtomicConstraint();
+  TemplateParameterList *TemplateParams =
+      cast<TemplateDecl>(N.getConstraintDecl())->getTemplateParameters();
   TemplateArgumentListInfo SubstArgs;
-  if (!Atomic.ParameterMapping) {
+  if (!N.hasParameterMapping()) {
     llvm::SmallBitVector OccurringIndices(TemplateParams->size());
-    S.MarkUsedTemplateParameters(Atomic.ConstraintExpr, /*OnlyDeduced=*/false,
-                                 /*Depth=*/0, OccurringIndices);
+
+    if (N.getKind() == NormalizedConstraint::ConstraintKind::Atomic) {
+      S.MarkUsedTemplateParameters(
+          static_cast<AtomicConstraint &>(N).getConstraintExpr(),
+          /*OnlyDeduced=*/false,
+          /*Depth=*/0, OccurringIndices);
+    } else if (N.getKind() == NormalizedConstraint::ConstraintKind::ConceptId) {
+      auto Args = static_cast<ConceptIdConstraint &>(N)
+                      .getConceptId()
+                      ->getTemplateArgsAsWritten();
+      if (Args)
+        S.MarkUsedTemplateParameters(Args->arguments(),
+                                     /*Depth=*/0, OccurringIndices);
+    }
     TemplateArgumentLoc *TempArgs =
         new (S.Context) TemplateArgumentLoc[OccurringIndices.count()];
     for (unsigned I = 0, J = 0, C = TemplateParams->size(); I != C; ++I)
@@ -1436,7 +1555,9 @@ substituteParameterMappings(Sema &S, NormalizedConstraint &N,
                 ArgsAsWritten->NumTemplateArgs > I
                     ? ArgsAsWritten->arguments()[I].getLocation()
                     : SourceLocation()));
-    Atomic.ParameterMapping.emplace(TempArgs,  OccurringIndices.count());
+    N.updateParameterMapping(OccurringIndices,
+                             MutableArrayRef<TemplateArgumentLoc>{
+                                 TempArgs, OccurringIndices.count()});
   }
   SourceLocation InstLocBegin =
       ArgsAsWritten->arguments().empty()
@@ -1449,21 +1570,69 @@ substituteParameterMappings(Sema &S, NormalizedConstraint &N,
   Sema::InstantiatingTemplate Inst(
       S, InstLocBegin,
       Sema::InstantiatingTemplate::ParameterMappingSubstitution{},
-      const_cast<NamedDecl *>(Atomic.ConstraintDecl),
+      const_cast<NamedDecl *>(N.getConstraintDecl()),
       {InstLocBegin, InstLocEnd});
   if (Inst.isInvalid())
     return true;
-  if (S.SubstTemplateArguments(*Atomic.ParameterMapping, MLTAL, SubstArgs))
+  if (S.SubstTemplateArgumentsInParameterMapping(N.getParameterMapping(), MLTAL,
+                                                 SubstArgs))
     return true;
 
   TemplateArgumentLoc *TempArgs =
       new (S.Context) TemplateArgumentLoc[SubstArgs.size()];
   std::copy(SubstArgs.arguments().begin(), SubstArgs.arguments().end(),
             TempArgs);
-  Atomic.ParameterMapping.emplace(TempArgs, SubstArgs.size());
+  N.updateParameterMapping(
+      N.mappingOccurenceList(),
+      MutableArrayRef<TemplateArgumentLoc>(TempArgs, SubstArgs.size()));
   return false;
 }
 
+static bool
+substituteParameterMappings(Sema &S, ConceptIdConstraint &N,
+                            const MultiLevelTemplateArgumentList &MLTAL,
+                            const ASTTemplateArgumentListInfo *ArgsAsWritten) {
+
+  {
+    if (N.getConstraintDecl()) {
+      substituteParameterMappings(
+          S, static_cast<NormalizedConstraintWithParamMapping &>(N), MLTAL,
+          ArgsAsWritten);
+    }
+  }
+  return substituteParameterMappings(S, N.getNormalizedConstraint(), MLTAL,
+                                     ArgsAsWritten);
+}
+
+static bool
+substituteParameterMappings(Sema &S, NormalizedConstraint &N,
+                            const MultiLevelTemplateArgumentList &MLTAL,
+                            const ASTTemplateArgumentListInfo *ArgsAsWritten) {
+
+  switch (N.getKind()) {
+  case NormalizedConstraint::ConstraintKind::Atomic:
+    return substituteParameterMappings(S, static_cast<AtomicConstraint &>(N),
+                                       MLTAL, ArgsAsWritten);
+  case NormalizedConstraint::ConstraintKind::FoldExpanded: {
+    Sema::ArgPackSubstIndexRAII _(S, std::nullopt);
+    return substituteParameterMappings(
+        S, static_cast<FoldExpandedConstraint &>(N).getNormalizedPattern(),
+        MLTAL, ArgsAsWritten);
+  }
+  case NormalizedConstraint::ConstraintKind::ConceptId:
+    return substituteParameterMappings(S, static_cast<ConceptIdConstraint &>(N),
+                                       MLTAL, ArgsAsWritten);
+
+  case NormalizedConstraint::ConstraintKind::Compound: {
+    auto &Compound = static_cast<CompoundConstraint &>(N);
+    if (substituteParameterMappings(S, Compound.getLHS(), MLTAL, ArgsAsWritten))
+      return true;
+    return substituteParameterMappings(S, Compound.getRHS(), MLTAL,
+                                       ArgsAsWritten);
+  }
+  }
+}
+
 static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N,
                                         const ConceptSpecializationExpr *CSE) {
   MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
@@ -1473,64 +1642,27 @@ static bool substituteParameterMappings(Sema &S, NormalizedConstraint &N,
       /*Pattern=*/nullptr,
       /*ForConstraintInstantiation=*/true);
 
-  return substituteParameterMappings(S, N, CSE->getNamedConcept(), MLTAL,
+  return substituteParameterMappings(S, N, MLTAL,
                                      CSE->getTemplateArgsAsWritten());
 }
 
-NormalizedConstraint::NormalizedConstraint(ASTContext &C,
-                                           NormalizedConstraint LHS,
-                                           NormalizedConstraint RHS,
-                                           CompoundConstraintKind Kind)
-    : Constraint{CompoundConstraint{
-          new(C) NormalizedConstraintPair{std::move(LHS), std::move(RHS)},
-          Kind}} {}
-
-NormalizedConstraint::NormalizedConstraint(ASTContext &C,
-                                           const NormalizedConstraint &Other) {
-  if (Other.isAtomic()) {
-    Constraint = new (C) AtomicConstraint(*Other.getAtomicConstraint());
-  } else if (Other.isFoldExpanded()) {
-    Constraint = new (C) FoldExpandedConstraint(
-        Other.getFoldExpandedConstraint()->Kind,
-        NormalizedConstraint(C, Other.getFoldExpandedConstraint()->Constraint),
-        Other.getFoldExpandedConstraint()->Pattern);
-  } else {
-    Constraint = CompoundConstraint(
-        new (C)
-            NormalizedConstraintPair{NormalizedConstraint(C, Other.getLHS()),
-                                     NormalizedConstraint(C, Other.getRHS())},
-        Other.getCompoundKind());
-  }
-}
-
-NormalizedConstraint &NormalizedConstraint::getLHS() const {
-  assert(isCompound() && "getLHS called on a non-compound constraint.");
-  return cast<CompoundConstraint>(Constraint).getPointer()->LHS;
-}
-
-NormalizedConstraint &NormalizedConstraint::getRHS() const {
-  assert(isCompound() && "getRHS called on a non-compound constraint.");
-  return cast<CompoundConstraint>(Constraint).getPointer()->RHS;
-}
-
-std::optional<NormalizedConstraint>
-NormalizedConstraint::fromAssociatedConstraints(
+NormalizedConstraint *NormalizedConstraint::fromAssociatedConstraints(
     Sema &S, const NamedDecl *D, ArrayRef<AssociatedConstraint> ACs) {
   assert(ACs.size() != 0);
   auto Conjunction = fromConstraintExpr(S, D, ACs[0].ConstraintExpr);
   if (!Conjunction)
-    return std::nullopt;
+    return nullptr;
   for (unsigned I = 1; I < ACs.size(); ++I) {
     auto Next = fromConstraintExpr(S, D, ACs[I].ConstraintExpr);
     if (!Next)
-      return std::nullopt;
-    *Conjunction = NormalizedConstraint(S.Context, std::move(*Conjunction),
-                                        std::move(*Next), CCK_Conjunction);
+      return nullptr;
+    Conjunction = CompoundConstraint::CreateConjunction(S.getASTContext(),
+                                                        Conjunction, Next);
   }
   return Conjunction;
 }
 
-std::optional<NormalizedConstraint>
+NormalizedConstraint *
 NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D,
                                          const Expr *E) {
   assert(E != nullptr);
@@ -1549,15 +1681,15 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D,
   if (LogicalBinOp BO = E) {
     auto LHS = fromConstraintExpr(S, D, BO.getLHS());
     if (!LHS)
-      return std::nullopt;
+      return nullptr;
     auto RHS = fromConstraintExpr(S, D, BO.getRHS());
     if (!RHS)
-      return std::nullopt;
+      return nullptr;
 
-    return NormalizedConstraint(S.Context, std::move(*LHS), std::move(*RHS),
-                                BO.isAnd() ? CCK_Conjunction : CCK_Disjunction);
+    return CompoundConstraint::Create(
+        S.Context, LHS, BO.isAnd() ? CCK_Conjunction : CCK_Disjunction, RHS);
   } else if (auto *CSE = dyn_cast<const ConceptSpecializationExpr>(E)) {
-    const NormalizedConstraint *SubNF;
+    NormalizedConstraint *SubNF;
     {
       Sema::InstantiatingTemplate Inst(
           S, CSE->getExprLoc(),
@@ -1565,7 +1697,7 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D,
           // FIXME: improve const-correctness of InstantiatingTemplate
           const_cast<NamedDecl *>(D), CSE->getSourceRange());
       if (Inst.isInvalid())
-        return std::nullopt;
+        return nullptr;
       // C++ [temp.constr.normal]p1.1
       // [...]
       // The normal form of an id-expression of the form C<A1, A2, ..., AN>,
@@ -1576,23 +1708,23 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D,
       // expression, the program is ill-formed; no diagnostic is required.
       // [...]
       ConceptDecl *CD = CSE->getNamedConcept();
-      SubNF = S.getNormalizedAssociatedConstraints(
-          CD, AssociatedConstraint(CD->getConstraintExpr()));
+      SubNF = NormalizedConstraint::fromAssociatedConstraints(
+          S, CSE->getNamedConcept(),
+          AssociatedConstraint(CD->getConstraintExpr()));
+
       if (!SubNF)
-        return std::nullopt;
+        return nullptr;
     }
+    if (substituteParameterMappings(S, *SubNF, CSE))
+      return nullptr;
 
-    std::optional<NormalizedConstraint> New;
-    New.emplace(S.Context, *SubNF);
+    return ConceptIdConstraint::Create(S.getASTContext(),
+                                       CSE->getConceptReference(), SubNF, D,
+                                       S.ArgPackSubstIndex);
 
-    if (substituteParameterMappings(S, *New, CSE))
-      return std::nullopt;
-
-    return New;
   } else if (auto *FE = dyn_cast<const CXXFoldExpr>(E);
-             FE && S.getLangOpts().CPlusPlus26 &&
-             (FE->getOperator() == BinaryOperatorKind::BO_LAnd ||
-              FE->getOperator() == BinaryOperatorKind::BO_LOr)) {
+             FE && (FE->getOperator() == BinaryOperatorKind::BO_LAnd ||
+                    FE->getOperator() == BinaryOperatorKind::BO_LOr)) {
 
     // Normalize fold expressions in C++26.
 
@@ -1605,28 +1737,28 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D,
       auto LHS = fromConstraintExpr(S, D, FE->getLHS());
       auto RHS = fromConstraintExpr(S, D, FE->getRHS());
       if (!LHS || !RHS)
-        return std::nullopt;
+        return nullptr;
 
       if (FE->isRightFold())
-        RHS = NormalizedConstraint{new (S.Context) FoldExpandedConstraint{
-            Kind, std::move(*RHS), FE->getPattern()}};
+        RHS = FoldExpandedConstraint::Create(S.getASTContext(),
+                                             FE->getPattern(), Kind, RHS);
       else
-        LHS = NormalizedConstraint{new (S.Context) FoldExpandedConstraint{
-            Kind, std::move(*LHS), FE->getPattern()}};
-
-      return NormalizedConstraint(
-          S.Context, std::move(*LHS), std::move(*RHS),
-          FE->getOperator() == BinaryOperatorKind::BO_LAnd ? CCK_Conjunction
-                                                           : CCK_Disjunction);
+        LHS = FoldExpandedConstraint::Create(S.getASTContext(),
+                                             FE->getPattern(), Kind, LHS);
+
+      return CompoundConstraint::Create(
+          S.getASTContext(), LHS,
+          (FE->getOperator() == BinaryOperatorKind::BO_LAnd ? CCK_Conjunction
+                                                            : CCK_Disjunction),
+          RHS);
     }
     auto Sub = fromConstraintExpr(S, D, FE->getPattern());
     if (!Sub)
-      return std::nullopt;
-    return NormalizedConstraint{new (S.Context) FoldExpandedConstraint{
-        Kind, std::move(*Sub), FE->getPattern()}};
+      return nullptr;
+    return FoldExpandedConstraint::Create(S.getASTContext(), FE->getPattern(),
+                                          Kind, Sub);
   }
-
-  return NormalizedConstraint{new (S.Context) AtomicConstraint(E, D)};
+  return AtomicConstraint::Create(S.getASTContext(), E, D, S.ArgPackSubstIndex);
 }
 
 bool FoldExpandedConstraint::AreCompatibleForSubsumption(
@@ -1637,8 +1769,10 @@ bool FoldExpandedConstraint::AreCompatibleForSubsumption(
   // if their respective constraints both contain an equivalent unexpanded pack.
 
   llvm::SmallVector<UnexpandedParameterPack> APacks, BPacks;
-  Sema::collectUnexpandedParameterPacks(const_cast<Expr *>(A.Pattern), APacks);
-  Sema::collectUnexpandedParameterPacks(const_cast<Expr *>(B.Pattern), BPacks);
+  Sema::collectUnexpandedParameterPacks(const_cast<Expr *>(A.getPattern()),
+                                        APacks);
+  Sema::collectUnexpandedParameterPacks(const_cast<Expr *>(B.getPattern()),
+                                        BPacks);
 
   for (const UnexpandedParameterPack &APack : APacks) {
     std::pair<unsigned, unsigned> DepthAndIndex = getDepthAndIndex(APack);
@@ -1730,7 +1864,7 @@ bool Sema::MaybeEmitAmbiguousAtomicConstraintsDiagnostic(
                                     const AtomicConstraint &B) {
     if (!A.hasMatchingParameterMapping(Context, B))
       return false;
-    const Expr *EA = A.ConstraintExpr, *EB = B.ConstraintExpr;
+    const Expr *EA = A.getConstraintExpr(), *EB = B.getConstraintExpr();
     if (EA == EB)
       return true;
 
@@ -1783,24 +1917,6 @@ bool Sema::MaybeEmitAmbiguousAtomicConstraintsDiagnostic(
   return true;
 }
 
-NormalizedConstraint::CompoundConstraintKind
-NormalizedConstraint::getCompoundKind() const {
-  assert(isCompound() && "getCompoundKind on a non-compound constraint..");
-  return cast<CompoundConstraint>(Constraint).getInt();
-}
-
-AtomicConstraint *NormalizedConstraint::getAtomicConstraint() const {
-  assert(isAtomic() && "getAtomicConstraint called on non-atomic constraint.");
-  return cast<AtomicConstraint *>(Constraint);
-}
-
-FoldExpandedConstraint *
-NormalizedConstraint::getFoldExpandedConstraint() const {
-  assert(isFoldExpanded() &&
-         "getFoldExpandedConstraint called on non-fold-expanded constraint.");
-  return cast<FoldExpandedConstraint *>(Constraint);
-}
-
 //
 //
 // ------------------------ Subsumption -----------------------------------
@@ -1843,8 +1959,8 @@ uint16_t SubsumptionChecker::getNewLiteralId() {
   return NextID++;
 }
 
-auto SubsumptionChecker::find(AtomicConstraint *Ori) -> Literal {
-  auto &Elems = AtomicMap[Ori->ConstraintExpr];
+auto SubsumptionChecker::find(const AtomicConstraint *Ori) -> Literal {
+  auto &Elems = AtomicMap[Ori->getConstraintExpr()];
   // C++ [temp.constr.order] p2
   //   - an atomic constraint A subsumes another atomic constraint B
   //     if and only if the A and B are identical [...]
@@ -1860,10 +1976,10 @@ auto SubsumptionChecker::find(AtomicConstraint *Ori) -> Literal {
   // subsumes another, their literal will be the same
 
   llvm::FoldingSetNodeID ID;
-  const auto &Mapping = Ori->ParameterMapping;
-  ID.AddBoolean(Mapping.has_value());
-  if (Mapping) {
-    for (const TemplateArgumentLoc &TAL : *Mapping) {
+  ID.AddBoolean(Ori->hasParameterMapping());
+  if (Ori->hasParameterMapping()) {
+    const auto &Mapping = Ori->getParameterMapping();
+    for (const TemplateArgumentLoc &TAL : Mapping) {
       SemaRef.getASTContext()
           .getCanonicalTemplateArgument(TAL.getArgument())
           .Profile(ID, SemaRef.getASTContext());
@@ -1881,11 +1997,11 @@ auto SubsumptionChecker::find(AtomicConstraint *Ori) -> Literal {
   return It->getSecond().ID;
 }
 
-auto SubsumptionChecker::find(FoldExpandedConstraint *Ori) -> Literal {
-  auto &Elems = FoldMap[Ori->Pattern];
+auto SubsumptionChecker::find(const FoldExpandedConstraint *Ori) -> Literal {
+  auto &Elems = FoldMap[Ori->getPattern()];
 
   FoldExpendedConstraintKey K;
-  K.Kind = Ori->Kind;
+  K.Kind = Ori->getFoldOperator();
 
   auto It = llvm::find_if(Elems, [&K](const FoldExpendedConstraintKey &Other) {
     return K.Kind == Other.Kind;
@@ -1929,38 +2045,47 @@ FormulaType SubsumptionChecker::Normalize(const NormalizedConstraint &NC) {
     AddUniqueClauseToFormula(Res, std::move(C));
   };
 
-  if (NC.isAtomic())
-    return {{find(NC.getAtomicConstraint())}};
+  switch (NC.getKind()) {
 
-  if (NC.isFoldExpanded())
-    return {{find(NC.getFoldExpandedConstraint())}};
+  case NormalizedConstraint::ConstraintKind::Atomic:
+    return {{find(&static_cast<const AtomicConstraint &>(NC))}};
 
-  FormulaType Left, Right;
-  SemaRef.runWithSufficientStackSpace(SourceLocation(), [&] {
-    Left = Normalize<FormulaType>(NC.getLHS());
-    Right = Normalize<FormulaType>(NC.getRHS());
-  });
+  case NormalizedConstraint::ConstraintKind::FoldExpanded:
+    return {{find(&static_cast<const FoldExpandedConstraint &>(NC))}};
 
-  if (NC.getCompoundKind() == FormulaType::Kind) {
-    auto SizeLeft = Left.size();
-    Res = std::move(Left);
-    Res.reserve(SizeLeft + Right.size());
-    std::for_each(std::make_move_iterator(Right.begin()),
-                  std::make_move_iterator(Right.end()), Add);
-    return Res;
-  }
+  case NormalizedConstraint::ConstraintKind::ConceptId:
+    return Normalize<FormulaType>(
+        static_cast<const ConceptIdConstraint &>(NC).getNormalizedConstraint());
+
+  case NormalizedConstraint::ConstraintKind::Compound: {
+    const auto &Compound = static_cast<const CompoundConstraint &>(NC);
+    FormulaType Left, Right;
+    SemaRef.runWithSufficientStackSpace(SourceLocation(), [&] {
+      Left = Normalize<FormulaType>(Compound.getLHS());
+      Right = Normalize<FormulaType>(Compound.getRHS());
+    });
+
+    if (Compound.getCompoundKind() == FormulaType::Kind) {
+      Res = std::move(Left);
+      Res.reserve(Left.size() + Right.size());
+      std::for_each(std::make_move_iterator(Right.begin()),
+                    std::make_move_iterator(Right.end()), Add);
+      return Res;
+    }
 
-  Res.reserve(Left.size() * Right.size());
-  for (const auto &LTransform : Left) {
-    for (const auto &RTransform : Right) {
-      Clause Combined;
-      Combined.reserve(LTransform.size() + RTransform.size());
-      llvm::append_range(Combined, LTransform);
-      llvm::append_range(Combined, RTransform);
-      Add(std::move(Combined));
+    Res.reserve(Left.size() * Right.size());
+    for (const auto &LTransform : Left) {
+      for (const auto &RTransform : Right) {
+        Clause Combined;
+        Combined.reserve(LTransform.size() + RTransform.size());
+        llvm::copy(LTransform, std::back_inserter(Combined));
+        llvm::copy(RTransform, std::back_inserter(Combined));
+        Add(std::move(Combined));
+      }
     }
+    return Res;
+  }
   }
-  return Res;
 }
 
 void SubsumptionChecker::AddUniqueClauseToFormula(Formula &F, Clause C) {
@@ -1975,12 +2100,12 @@ std::optional<bool> SubsumptionChecker::Subsumes(
     const NamedDecl *DP, ArrayRef<AssociatedConstraint> P, const NamedDecl *DQ,
     ArrayRef<AssociatedConstraint> Q) {
   const NormalizedConstraint *PNormalized =
-      getNormalizedAssociatedConstraints(SemaRef, DP, P);
+      SemaRef.getNormalizedAssociatedConstraints(DP, P);
   if (!PNormalized)
     return std::nullopt;
 
   const NormalizedConstraint *QNormalized =
-      getNormalizedAssociatedConstraints(SemaRef, DQ, Q);
+      SemaRef.getNormalizedAssociatedConstraints(DQ, Q);
   if (!QNormalized)
     return std::nullopt;
 
@@ -2030,9 +2155,9 @@ bool SubsumptionChecker::Subsumes(const FoldExpandedConstraint *A,
     // constraint B if they are compatible for subsumption, have the same
     // fold-operator, and the constraint of A subsumes that of B.
     bool DoesSubsume =
-        A->Kind == B->Kind &&
+        A->getFoldOperator() == B->getFoldOperator() &&
         FoldExpandedConstraint::AreCompatibleForSubsumption(*A, *B) &&
-        Subsumes(&A->Constraint, &B->Constraint);
+        Subsumes(&A->getNormalizedPattern(), &B->getNormalizedPattern());
     It = FoldSubsumptionCache.try_emplace(std::move(Key), DoesSubsume).first;
   }
   return It->second;
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 770ac9839eb98..8a0ca8fcc14cf 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -17913,13 +17913,15 @@ Decl *Sema::BuildStaticAssertDeclaration(SourceLocation StaticAssertLoc,
         findFailedBooleanCondition(Converted.get());
       if (const auto *ConceptIDExpr =
               dyn_cast_or_null<ConceptSpecializationExpr>(InnerCond)) {
-        // Drill down into concept specialization expressions to see why they
-        // weren't satisfied.
-        Diag(AssertExpr->getBeginLoc(), diag::err_static_assert_failed)
-            << !HasMessage << Msg.str() << AssertExpr->getSourceRange();
-        ConstraintSatisfaction Satisfaction;
-        if (!CheckConstraintSatisfaction(ConceptIDExpr, Satisfaction))
-          DiagnoseUnsatisfiedConstraint(Satisfaction);
+        const ASTConstraintSatisfaction &Satisfaction =
+            ConceptIDExpr->getSatisfaction();
+        if (!Satisfaction.ContainsErrors || Satisfaction.NumRecords) {
+          Diag(AssertExpr->getBeginLoc(), diag::err_static_assert_failed)
+              << !HasMessage << Msg.str() << AssertExpr->getSourceRange();
+          // Drill down into concept specialization expressions to see why they
+          // weren't satisfied.
+          DiagnoseUnsatisfiedConstraint(ConceptIDExpr);
+        }
       } else if (InnerCond && !isa<CXXBoolLiteralExpr>(InnerCond) &&
                  !isa<IntegerLiteral>(InnerCond)) {
         Diag(InnerCond->getBeginLoc(),
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index d8338e08dd0f7..3dbbbe1325675 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -8326,7 +8326,14 @@ Sema::BuildExprRequirement(
     auto *IDC = TC->getImmediatelyDeclaredConstraint();
     assert(IDC && "ImmediatelyDeclaredConstraint can't be null here.");
     ExprResult Constraint = SubstExpr(IDC, MLTAL);
-    if (Constraint.isInvalid()) {
+    bool HasError = Constraint.isInvalid();
+    if (!HasError) {
+      SubstitutedConstraintExpr =
+          cast<ConceptSpecializationExpr>(Constraint.get());
+      if (SubstitutedConstraintExpr->getSatisfaction().ContainsErrors)
+        HasError = true;
+    }
+    if (HasError) {
       return new (Context) concepts::ExprRequirement(
           createSubstDiagAt(IDC->getExprLoc(),
                             [&](llvm::raw_ostream &OS) {
@@ -8335,8 +8342,6 @@ Sema::BuildExprRequirement(
                             }),
           IsSimple, NoexceptLoc, ReturnTypeRequirement);
     }
-    SubstitutedConstraintExpr =
-        cast<ConceptSpecializationExpr>(Constraint.get());
     if (!SubstitutedConstraintExpr->isSatisfied())
       Status = concepts::ExprRequirement::SS_ConstraintsNotSatisfied;
   }
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 41c3f81639c82..d5f23db6bacb2 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -4648,13 +4648,11 @@ void Sema::diagnoseMissingTemplateArguments(const CXXScopeSpec &SS,
   diagnoseMissingTemplateArguments(Name, Loc);
 }
 
-ExprResult
-Sema::CheckConceptTemplateId(const CXXScopeSpec &SS,
-                             SourceLocation TemplateKWLoc,
-                             const DeclarationNameInfo &ConceptNameInfo,
-                             NamedDecl *FoundDecl,
-                             ConceptDecl *NamedConcept,
-                             const TemplateArgumentListInfo *TemplateArgs) {
+ExprResult Sema::CheckConceptTemplateId(
+    const CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
+    const DeclarationNameInfo &ConceptNameInfo, NamedDecl *FoundDecl,
+    ConceptDecl *NamedConcept, const TemplateArgumentListInfo *TemplateArgs,
+    bool DoCheckConstraintSatisfaction) {
   assert(NamedConcept && "A concept template id without a template?");
 
   if (NamedConcept->isInvalidDecl())
@@ -4685,19 +4683,25 @@ Sema::CheckConceptTemplateId(const CXXScopeSpec &SS,
   EnterExpressionEvaluationContext EECtx{
       *this, ExpressionEvaluationContext::Unevaluated, CSD};
 
-  if (!AreArgsDependent &&
-      CheckConstraintSatisfaction(
-          NamedConcept, AssociatedConstraint(NamedConcept->getConstraintExpr()),
-          MLTAL,
-          SourceRange(SS.isSet() ? SS.getBeginLoc() : ConceptNameInfo.getLoc(),
-                      TemplateArgs->getRAngleLoc()),
-          Satisfaction))
-    return ExprError();
   auto *CL = ConceptReference::Create(
       Context,
       SS.isSet() ? SS.getWithLocInContext(Context) : NestedNameSpecifierLoc{},
       TemplateKWLoc, ConceptNameInfo, FoundDecl, NamedConcept,
       ASTTemplateArgumentListInfo::Create(Context, *TemplateArgs));
+
+  bool Error = false;
+  if (!AreArgsDependent && DoCheckConstraintSatisfaction)
+    Error = CheckConstraintSatisfaction(
+        NamedConcept, AssociatedConstraint(NamedConcept->getConstraintExpr()),
+        MLTAL,
+        SourceRange(SS.isSet() ? SS.getBeginLoc() : ConceptNameInfo.getLoc(),
+                    TemplateArgs->getRAngleLoc()),
+        Satisfaction, CL);
+
+  if (!DoCheckConstraintSatisfaction)
+    Satisfaction.IsSatisfied = false;
+  Satisfaction.ContainsErrors = Error;
+
   return ConceptSpecializationExpr::Create(
       Context, CL, CSD, AreArgsDependent ? nullptr : &Satisfaction);
 }
@@ -5939,7 +5943,8 @@ bool Sema::CheckTemplateArgumentList(
         /*RelativeToPrimary=*/true,
         /*Pattern=*/nullptr,
         /*ForConceptInstantiation=*/true);
-    if (EnsureTemplateArgumentListConstraints(
+    if (!isa<ConceptDecl>(Template) &&
+        EnsureTemplateArgumentListConstraints(
             Template, MLTAL,
             SourceRange(TemplateLoc, TemplateArgs.getRAngleLoc()))) {
       if (ConstraintsNotSatisfied)
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 217d57d67f067..ab57368d0a6fc 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -7073,6 +7073,14 @@ void Sema::MarkUsedTemplateParameters(ArrayRef<TemplateArgument> TemplateArgs,
                                  /*OnlyDeduced=*/false, Depth, Used);
 }
 
+void Sema::MarkUsedTemplateParameters(
+    ArrayRef<TemplateArgumentLoc> TemplateArgs, unsigned Depth,
+    llvm::SmallBitVector &Used) {
+  for (unsigned I = 0, N = TemplateArgs.size(); I != N; ++I)
+    ::MarkUsedTemplateParameters(Context, TemplateArgs[I].getArgument(),
+                                 /*OnlyDeduced=*/false, Depth, Used);
+}
+
 void Sema::MarkDeducedTemplateParameters(
     ASTContext &Ctx, const FunctionTemplateDecl *FunctionTemplate,
     llvm::SmallBitVector &Deduced) {
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index cd20dfb4fe093..e94f376bdeba3 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1364,516 +1364,526 @@ getPackSubstitutedTemplateArgument(Sema &S, TemplateArgument Arg) {
 // Template Instantiation for Types
 //===----------------------------------------------------------------------===/
 namespace {
-  class TemplateInstantiator : public TreeTransform<TemplateInstantiator> {
-    const MultiLevelTemplateArgumentList &TemplateArgs;
-    SourceLocation Loc;
-    DeclarationName Entity;
-    // Whether to evaluate the C++20 constraints or simply substitute into them.
-    bool EvaluateConstraints = true;
-    // Whether Substitution was Incomplete, that is, we tried to substitute in
-    // any user provided template arguments which were null.
-    bool IsIncomplete = false;
-    // Whether an incomplete substituion should be treated as an error.
-    bool BailOutOnIncomplete;
-
-  public:
-    typedef TreeTransform<TemplateInstantiator> inherited;
-
-    TemplateInstantiator(Sema &SemaRef,
-                         const MultiLevelTemplateArgumentList &TemplateArgs,
-                         SourceLocation Loc, DeclarationName Entity,
-                         bool BailOutOnIncomplete = false)
-        : inherited(SemaRef), TemplateArgs(TemplateArgs), Loc(Loc),
-          Entity(Entity), BailOutOnIncomplete(BailOutOnIncomplete) {}
 
-    void setEvaluateConstraints(bool B) {
-      EvaluateConstraints = B;
-    }
-    bool getEvaluateConstraints() {
-      return EvaluateConstraints;
-    }
+class TemplateInstantiator : public TreeTransform<TemplateInstantiator> {
+  const MultiLevelTemplateArgumentList &TemplateArgs;
+  SourceLocation Loc;
+  DeclarationName Entity;
+  // Whether to evaluate the C++20 constraints or simply substitute into them.
+  bool EvaluateConstraints = true;
+  // Whether Substitution was Incomplete, that is, we tried to substitute in
+  // any user provided template arguments which were null.
+  bool IsIncomplete = false;
+  // Whether an incomplete substituion should be treated as an error.
+  bool BailOutOnIncomplete;
 
-    /// Determine whether the given type \p T has already been
-    /// transformed.
-    ///
-    /// For the purposes of template instantiation, a type has already been
-    /// transformed if it is NULL or if it is not dependent.
-    bool AlreadyTransformed(QualType T);
+  bool PreserveArgumentPacks = false;
 
-    /// Returns the location of the entity being instantiated, if known.
-    SourceLocation getBaseLocation() { return Loc; }
+public:
+  typedef TreeTransform<TemplateInstantiator> inherited;
 
-    /// Returns the name of the entity being instantiated, if any.
-    DeclarationName getBaseEntity() { return Entity; }
+  TemplateInstantiator(Sema &SemaRef,
+                       const MultiLevelTemplateArgumentList &TemplateArgs,
+                       SourceLocation Loc, DeclarationName Entity,
+                       bool BailOutOnIncomplete = false)
+      : inherited(SemaRef), TemplateArgs(TemplateArgs), Loc(Loc),
+        Entity(Entity), BailOutOnIncomplete(BailOutOnIncomplete) {}
+
+  inline static struct ForParameterMappingSubstitution_t {
+  } ForParameterMappingSubstitution;
+
+  TemplateInstantiator(ForParameterMappingSubstitution_t, Sema &SemaRef,
+                       const MultiLevelTemplateArgumentList &TemplateArgs)
+      : inherited(SemaRef), TemplateArgs(TemplateArgs),
+        BailOutOnIncomplete(false), PreserveArgumentPacks(true) {}
+
+  void setEvaluateConstraints(bool B) { EvaluateConstraints = B; }
+  bool getEvaluateConstraints() { return EvaluateConstraints; }
+
+  /// Determine whether the given type \p T has already been
+  /// transformed.
+  ///
+  /// For the purposes of template instantiation, a type has already been
+  /// transformed if it is NULL or if it is not dependent.
+  bool AlreadyTransformed(QualType T);
+
+  /// Returns the location of the entity being instantiated, if known.
+  SourceLocation getBaseLocation() { return Loc; }
+
+  /// Returns the name of the entity being instantiated, if any.
+  DeclarationName getBaseEntity() { return Entity; }
+
+  /// Returns whether any substitution so far was incomplete.
+  bool getIsIncomplete() const { return IsIncomplete; }
+
+  /// Sets the "base" location and entity when that
+  /// information is known based on another transformation.
+  void setBase(SourceLocation Loc, DeclarationName Entity) {
+    this->Loc = Loc;
+    this->Entity = Entity;
+  }
 
-    /// Returns whether any substitution so far was incomplete.
-    bool getIsIncomplete() const { return IsIncomplete; }
+  unsigned TransformTemplateDepth(unsigned Depth) {
+    return TemplateArgs.getNewDepth(Depth);
+  }
 
-    /// Sets the "base" location and entity when that
-    /// information is known based on another transformation.
-    void setBase(SourceLocation Loc, DeclarationName Entity) {
-      this->Loc = Loc;
-      this->Entity = Entity;
-    }
+  UnsignedOrNone getPackIndex(TemplateArgument Pack) {
+    UnsignedOrNone Index = getSema().ArgPackSubstIndex;
+    if (!Index)
+      return std::nullopt;
+    return Pack.pack_size() - 1 - *Index;
+  }
 
-    unsigned TransformTemplateDepth(unsigned Depth) {
-      return TemplateArgs.getNewDepth(Depth);
-    }
+  bool TryExpandParameterPacks(SourceLocation EllipsisLoc,
+                               SourceRange PatternRange,
+                               ArrayRef<UnexpandedParameterPack> Unexpanded,
+                               bool &ShouldExpand, bool &RetainExpansion,
+                               UnsignedOrNone &NumExpansions) {
+    return getSema().CheckParameterPacksForExpansion(
+        EllipsisLoc, PatternRange, Unexpanded, TemplateArgs, ShouldExpand,
+        RetainExpansion, NumExpansions);
+  }
 
-    UnsignedOrNone getPackIndex(TemplateArgument Pack) {
-      UnsignedOrNone Index = getSema().ArgPackSubstIndex;
-      if (!Index)
-        return std::nullopt;
-      return Pack.pack_size() - 1 - *Index;
-    }
+  void ExpandingFunctionParameterPack(ParmVarDecl *Pack) {
+    SemaRef.CurrentInstantiationScope->MakeInstantiatedLocalArgPack(Pack);
+  }
 
-    bool TryExpandParameterPacks(SourceLocation EllipsisLoc,
-                                 SourceRange PatternRange,
-                                 ArrayRef<UnexpandedParameterPack> Unexpanded,
-                                 bool &ShouldExpand, bool &RetainExpansion,
-                                 UnsignedOrNone &NumExpansions) {
-      return getSema().CheckParameterPacksForExpansion(EllipsisLoc,
-                                                       PatternRange, Unexpanded,
-                                                       TemplateArgs,
-                                                       ShouldExpand,
-                                                       RetainExpansion,
-                                                       NumExpansions);
+  TemplateArgument ForgetPartiallySubstitutedPack() {
+    TemplateArgument Result;
+    if (NamedDecl *PartialPack =
+            SemaRef.CurrentInstantiationScope->getPartiallySubstitutedPack()) {
+      MultiLevelTemplateArgumentList &TemplateArgs =
+          const_cast<MultiLevelTemplateArgumentList &>(this->TemplateArgs);
+      unsigned Depth, Index;
+      std::tie(Depth, Index) = getDepthAndIndex(PartialPack);
+      if (TemplateArgs.hasTemplateArgument(Depth, Index)) {
+        Result = TemplateArgs(Depth, Index);
+        TemplateArgs.setArgument(Depth, Index, TemplateArgument());
+      } else {
+        IsIncomplete = true;
+        if (BailOutOnIncomplete)
+          return TemplateArgument();
+      }
     }
 
-    void ExpandingFunctionParameterPack(ParmVarDecl *Pack) {
-      SemaRef.CurrentInstantiationScope->MakeInstantiatedLocalArgPack(Pack);
+    return Result;
+  }
+
+  void RememberPartiallySubstitutedPack(TemplateArgument Arg) {
+    if (Arg.isNull())
+      return;
+
+    if (NamedDecl *PartialPack =
+            SemaRef.CurrentInstantiationScope->getPartiallySubstitutedPack()) {
+      MultiLevelTemplateArgumentList &TemplateArgs =
+          const_cast<MultiLevelTemplateArgumentList &>(this->TemplateArgs);
+      unsigned Depth, Index;
+      std::tie(Depth, Index) = getDepthAndIndex(PartialPack);
+      TemplateArgs.setArgument(Depth, Index, Arg);
     }
+  }
 
-    TemplateArgument ForgetPartiallySubstitutedPack() {
-      TemplateArgument Result;
-      if (NamedDecl *PartialPack
-            = SemaRef.CurrentInstantiationScope->getPartiallySubstitutedPack()){
-        MultiLevelTemplateArgumentList &TemplateArgs
-          = const_cast<MultiLevelTemplateArgumentList &>(this->TemplateArgs);
-        unsigned Depth, Index;
-        std::tie(Depth, Index) = getDepthAndIndex(PartialPack);
-        if (TemplateArgs.hasTemplateArgument(Depth, Index)) {
-          Result = TemplateArgs(Depth, Index);
-          TemplateArgs.setArgument(Depth, Index, TemplateArgument());
-        } else {
-          IsIncomplete = true;
-          if (BailOutOnIncomplete)
-            return TemplateArgument();
-        }
-      }
+  bool ShouldPreserveTemplateArgumentsPacks() const {
+    return PreserveArgumentPacks;
+  }
 
-      return Result;
-    }
+  TemplateArgument
+  getTemplateArgumentPackPatternForRewrite(const TemplateArgument &TA) {
+    if (TA.getKind() != TemplateArgument::Pack)
+      return TA;
+    if (SemaRef.ArgPackSubstIndex)
+      return getPackSubstitutedTemplateArgument(SemaRef, TA);
+    assert(TA.pack_size() == 1 && TA.pack_begin()->isPackExpansion() &&
+           "unexpected pack arguments in template rewrite");
+    TemplateArgument Arg = *TA.pack_begin();
+    if (Arg.isPackExpansion())
+      Arg = Arg.getPackExpansionPattern();
+    return Arg;
+  }
 
-    void RememberPartiallySubstitutedPack(TemplateArgument Arg) {
-      if (Arg.isNull())
-        return;
-
-      if (NamedDecl *PartialPack
-            = SemaRef.CurrentInstantiationScope->getPartiallySubstitutedPack()){
-        MultiLevelTemplateArgumentList &TemplateArgs
-        = const_cast<MultiLevelTemplateArgumentList &>(this->TemplateArgs);
-        unsigned Depth, Index;
-        std::tie(Depth, Index) = getDepthAndIndex(PartialPack);
-        TemplateArgs.setArgument(Depth, Index, Arg);
-      }
-    }
+  /// Transform the given declaration by instantiating a reference to
+  /// this declaration.
+  Decl *TransformDecl(SourceLocation Loc, Decl *D);
 
-    TemplateArgument
-    getTemplateArgumentPackPatternForRewrite(const TemplateArgument &TA) {
-      if (TA.getKind() != TemplateArgument::Pack)
-        return TA;
-      if (SemaRef.ArgPackSubstIndex)
-        return getPackSubstitutedTemplateArgument(SemaRef, TA);
-      assert(TA.pack_size() == 1 && TA.pack_begin()->isPackExpansion() &&
-             "unexpected pack arguments in template rewrite");
-      TemplateArgument Arg = *TA.pack_begin();
-      if (Arg.isPackExpansion())
-        Arg = Arg.getPackExpansionPattern();
-      return Arg;
-    }
+  void transformAttrs(Decl *Old, Decl *New) {
+    SemaRef.InstantiateAttrs(TemplateArgs, Old, New);
+  }
 
-    /// Transform the given declaration by instantiating a reference to
-    /// this declaration.
-    Decl *TransformDecl(SourceLocation Loc, Decl *D);
+  void transformedLocalDecl(Decl *Old, ArrayRef<Decl *> NewDecls) {
+    if (Old->isParameterPack() &&
+        (NewDecls.size() != 1 || !NewDecls.front()->isParameterPack())) {
+      SemaRef.CurrentInstantiationScope->MakeInstantiatedLocalArgPack(Old);
+      for (auto *New : NewDecls)
+        SemaRef.CurrentInstantiationScope->InstantiatedLocalPackArg(
+            Old, cast<VarDecl>(New));
+      return;
+    }
 
-    void transformAttrs(Decl *Old, Decl *New) {
-      SemaRef.InstantiateAttrs(TemplateArgs, Old, New);
+    assert(NewDecls.size() == 1 &&
+           "should only have multiple expansions for a pack");
+    Decl *New = NewDecls.front();
+
+    // If we've instantiated the call operator of a lambda or the call
+    // operator template of a generic lambda, update the "instantiation of"
+    // information.
+    auto *NewMD = dyn_cast<CXXMethodDecl>(New);
+    if (NewMD && isLambdaCallOperator(NewMD)) {
+      auto *OldMD = dyn_cast<CXXMethodDecl>(Old);
+      if (auto *NewTD = NewMD->getDescribedFunctionTemplate())
+        NewTD->setInstantiatedFromMemberTemplate(
+            OldMD->getDescribedFunctionTemplate());
+      else
+        NewMD->setInstantiationOfMemberFunction(OldMD,
+                                                TSK_ImplicitInstantiation);
     }
 
-    void transformedLocalDecl(Decl *Old, ArrayRef<Decl *> NewDecls) {
-      if (Old->isParameterPack() &&
-          (NewDecls.size() != 1 || !NewDecls.front()->isParameterPack())) {
-        SemaRef.CurrentInstantiationScope->MakeInstantiatedLocalArgPack(Old);
-        for (auto *New : NewDecls)
-          SemaRef.CurrentInstantiationScope->InstantiatedLocalPackArg(
-              Old, cast<VarDecl>(New));
-        return;
-      }
+    SemaRef.CurrentInstantiationScope->InstantiatedLocal(Old, New);
 
-      assert(NewDecls.size() == 1 &&
-             "should only have multiple expansions for a pack");
-      Decl *New = NewDecls.front();
-
-      // If we've instantiated the call operator of a lambda or the call
-      // operator template of a generic lambda, update the "instantiation of"
-      // information.
-      auto *NewMD = dyn_cast<CXXMethodDecl>(New);
-      if (NewMD && isLambdaCallOperator(NewMD)) {
-        auto *OldMD = dyn_cast<CXXMethodDecl>(Old);
-        if (auto *NewTD = NewMD->getDescribedFunctionTemplate())
-          NewTD->setInstantiatedFromMemberTemplate(
-              OldMD->getDescribedFunctionTemplate());
-        else
-          NewMD->setInstantiationOfMemberFunction(OldMD,
-                                                  TSK_ImplicitInstantiation);
-      }
+    // We recreated a local declaration, but not by instantiating it. There
+    // may be pending dependent diagnostics to produce.
+    if (auto *DC = dyn_cast<DeclContext>(Old);
+        DC && DC->isDependentContext() && DC->isFunctionOrMethod())
+      SemaRef.PerformDependentDiagnostics(DC, TemplateArgs);
+  }
 
-      SemaRef.CurrentInstantiationScope->InstantiatedLocal(Old, New);
+  /// Transform the definition of the given declaration by
+  /// instantiating it.
+  Decl *TransformDefinition(SourceLocation Loc, Decl *D);
 
-      // We recreated a local declaration, but not by instantiating it. There
-      // may be pending dependent diagnostics to produce.
-      if (auto *DC = dyn_cast<DeclContext>(Old);
-          DC && DC->isDependentContext() && DC->isFunctionOrMethod())
-        SemaRef.PerformDependentDiagnostics(DC, TemplateArgs);
-    }
+  /// Transform the first qualifier within a scope by instantiating the
+  /// declaration.
+  NamedDecl *TransformFirstQualifierInScope(NamedDecl *D, SourceLocation Loc);
 
-    /// Transform the definition of the given declaration by
-    /// instantiating it.
-    Decl *TransformDefinition(SourceLocation Loc, Decl *D);
-
-    /// Transform the first qualifier within a scope by instantiating the
-    /// declaration.
-    NamedDecl *TransformFirstQualifierInScope(NamedDecl *D, SourceLocation Loc);
-
-    bool TransformExceptionSpec(SourceLocation Loc,
-                                FunctionProtoType::ExceptionSpecInfo &ESI,
-                                SmallVectorImpl<QualType> &Exceptions,
-                                bool &Changed);
-
-    /// Rebuild the exception declaration and register the declaration
-    /// as an instantiated local.
-    VarDecl *RebuildExceptionDecl(VarDecl *ExceptionDecl,
-                                  TypeSourceInfo *Declarator,
-                                  SourceLocation StartLoc,
-                                  SourceLocation NameLoc,
-                                  IdentifierInfo *Name);
-
-    /// Rebuild the Objective-C exception declaration and register the
-    /// declaration as an instantiated local.
-    VarDecl *RebuildObjCExceptionDecl(VarDecl *ExceptionDecl,
-                                      TypeSourceInfo *TSInfo, QualType T);
-
-    /// Check for tag mismatches when instantiating an
-    /// elaborated type.
-    QualType RebuildElaboratedType(SourceLocation KeywordLoc,
-                                   ElaboratedTypeKeyword Keyword,
-                                   NestedNameSpecifierLoc QualifierLoc,
-                                   QualType T);
-
-    TemplateName
-    TransformTemplateName(CXXScopeSpec &SS, TemplateName Name,
-                          SourceLocation NameLoc,
-                          QualType ObjectType = QualType(),
-                          NamedDecl *FirstQualifierInScope = nullptr,
-                          bool AllowInjectedClassName = false);
-
-    const AnnotateAttr *TransformAnnotateAttr(const AnnotateAttr *AA);
-    const CXXAssumeAttr *TransformCXXAssumeAttr(const CXXAssumeAttr *AA);
-    const LoopHintAttr *TransformLoopHintAttr(const LoopHintAttr *LH);
-    const NoInlineAttr *TransformStmtNoInlineAttr(const Stmt *OrigS,
-                                                  const Stmt *InstS,
-                                                  const NoInlineAttr *A);
-    const AlwaysInlineAttr *
-    TransformStmtAlwaysInlineAttr(const Stmt *OrigS, const Stmt *InstS,
-                                  const AlwaysInlineAttr *A);
-    const CodeAlignAttr *TransformCodeAlignAttr(const CodeAlignAttr *CA);
-    const OpenACCRoutineDeclAttr *
-    TransformOpenACCRoutineDeclAttr(const OpenACCRoutineDeclAttr *A);
-    ExprResult TransformPredefinedExpr(PredefinedExpr *E);
-    ExprResult TransformDeclRefExpr(DeclRefExpr *E);
-    ExprResult TransformCXXDefaultArgExpr(CXXDefaultArgExpr *E);
-
-    ExprResult TransformTemplateParmRefExpr(DeclRefExpr *E,
-                                            NonTypeTemplateParmDecl *D);
-    ExprResult TransformSubstNonTypeTemplateParmPackExpr(
-                                           SubstNonTypeTemplateParmPackExpr *E);
-    ExprResult TransformSubstNonTypeTemplateParmExpr(
-                                           SubstNonTypeTemplateParmExpr *E);
-
-    /// Rebuild a DeclRefExpr for a VarDecl reference.
-    ExprResult RebuildVarDeclRefExpr(ValueDecl *PD, SourceLocation Loc);
-
-    /// Transform a reference to a function or init-capture parameter pack.
-    ExprResult TransformFunctionParmPackRefExpr(DeclRefExpr *E, ValueDecl *PD);
-
-    /// Transform a FunctionParmPackExpr which was built when we couldn't
-    /// expand a function parameter pack reference which refers to an expanded
-    /// pack.
-    ExprResult TransformFunctionParmPackExpr(FunctionParmPackExpr *E);
-
-    QualType TransformFunctionProtoType(TypeLocBuilder &TLB,
-                                        FunctionProtoTypeLoc TL) {
-      // Call the base version; it will forward to our overridden version below.
-      return inherited::TransformFunctionProtoType(TLB, TL);
-    }
+  bool TransformExceptionSpec(SourceLocation Loc,
+                              FunctionProtoType::ExceptionSpecInfo &ESI,
+                              SmallVectorImpl<QualType> &Exceptions,
+                              bool &Changed);
+
+  /// Rebuild the exception declaration and register the declaration
+  /// as an instantiated local.
+  VarDecl *RebuildExceptionDecl(VarDecl *ExceptionDecl,
+                                TypeSourceInfo *Declarator,
+                                SourceLocation StartLoc, SourceLocation NameLoc,
+                                IdentifierInfo *Name);
+
+  /// Rebuild the Objective-C exception declaration and register the
+  /// declaration as an instantiated local.
+  VarDecl *RebuildObjCExceptionDecl(VarDecl *ExceptionDecl,
+                                    TypeSourceInfo *TSInfo, QualType T);
+
+  /// Check for tag mismatches when instantiating an
+  /// elaborated type.
+  QualType RebuildElaboratedType(SourceLocation KeywordLoc,
+                                 ElaboratedTypeKeyword Keyword,
+                                 NestedNameSpecifierLoc QualifierLoc,
+                                 QualType T);
+
+  TemplateName TransformTemplateName(CXXScopeSpec &SS, TemplateName Name,
+                                     SourceLocation NameLoc,
+                                     QualType ObjectType = QualType(),
+                                     NamedDecl *FirstQualifierInScope = nullptr,
+                                     bool AllowInjectedClassName = false);
+
+  const AnnotateAttr *TransformAnnotateAttr(const AnnotateAttr *AA);
+  const CXXAssumeAttr *TransformCXXAssumeAttr(const CXXAssumeAttr *AA);
+  const LoopHintAttr *TransformLoopHintAttr(const LoopHintAttr *LH);
+  const NoInlineAttr *TransformStmtNoInlineAttr(const Stmt *OrigS,
+                                                const Stmt *InstS,
+                                                const NoInlineAttr *A);
+  const AlwaysInlineAttr *
+  TransformStmtAlwaysInlineAttr(const Stmt *OrigS, const Stmt *InstS,
+                                const AlwaysInlineAttr *A);
+  const CodeAlignAttr *TransformCodeAlignAttr(const CodeAlignAttr *CA);
+  const OpenACCRoutineDeclAttr *
+  TransformOpenACCRoutineDeclAttr(const OpenACCRoutineDeclAttr *A);
+  ExprResult TransformPredefinedExpr(PredefinedExpr *E);
+  ExprResult TransformDeclRefExpr(DeclRefExpr *E);
+  ExprResult TransformCXXDefaultArgExpr(CXXDefaultArgExpr *E);
+
+  ExprResult TransformTemplateParmRefExpr(DeclRefExpr *E,
+                                          NonTypeTemplateParmDecl *D);
+  ExprResult TransformSubstNonTypeTemplateParmPackExpr(
+      SubstNonTypeTemplateParmPackExpr *E);
+  ExprResult
+  TransformSubstNonTypeTemplateParmExpr(SubstNonTypeTemplateParmExpr *E);
+
+  /// Rebuild a DeclRefExpr for a VarDecl reference.
+  ExprResult RebuildVarDeclRefExpr(ValueDecl *PD, SourceLocation Loc);
+
+  /// Transform a reference to a function or init-capture parameter pack.
+  ExprResult TransformFunctionParmPackRefExpr(DeclRefExpr *E, ValueDecl *PD);
+
+  /// Transform a FunctionParmPackExpr which was built when we couldn't
+  /// expand a function parameter pack reference which refers to an expanded
+  /// pack.
+  ExprResult TransformFunctionParmPackExpr(FunctionParmPackExpr *E);
+
+  QualType TransformFunctionProtoType(TypeLocBuilder &TLB,
+                                      FunctionProtoTypeLoc TL) {
+    // Call the base version; it will forward to our overridden version below.
+    return inherited::TransformFunctionProtoType(TLB, TL);
+  }
 
-    QualType TransformInjectedClassNameType(TypeLocBuilder &TLB,
-                                            InjectedClassNameTypeLoc TL) {
-      auto Type = inherited::TransformInjectedClassNameType(TLB, TL);
-      // Special case for transforming a deduction guide, we return a
-      // transformed TemplateSpecializationType.
-      if (Type.isNull() &&
-          SemaRef.CodeSynthesisContexts.back().Kind ==
-              Sema::CodeSynthesisContext::BuildingDeductionGuides) {
-        // Return a TemplateSpecializationType for transforming a deduction
-        // guide.
-        if (auto *ICT = TL.getType()->getAs<InjectedClassNameType>()) {
-          auto Type =
-              inherited::TransformType(ICT->getInjectedSpecializationType());
-          TLB.pushTrivial(SemaRef.Context, Type, TL.getNameLoc());
-          return Type;
-        }
+  QualType TransformInjectedClassNameType(TypeLocBuilder &TLB,
+                                          InjectedClassNameTypeLoc TL) {
+    auto Type = inherited::TransformInjectedClassNameType(TLB, TL);
+    // Special case for transforming a deduction guide, we return a
+    // transformed TemplateSpecializationType.
+    if (Type.isNull() &&
+        SemaRef.CodeSynthesisContexts.back().Kind ==
+            Sema::CodeSynthesisContext::BuildingDeductionGuides) {
+      // Return a TemplateSpecializationType for transforming a deduction
+      // guide.
+      if (auto *ICT = TL.getType()->getAs<InjectedClassNameType>()) {
+        auto Type =
+            inherited::TransformType(ICT->getInjectedSpecializationType());
+        TLB.pushTrivial(SemaRef.Context, Type, TL.getNameLoc());
+        return Type;
       }
-      return Type;
     }
-    // Override the default version to handle a rewrite-template-arg-pack case
-    // for building a deduction guide.
-    bool TransformTemplateArgument(const TemplateArgumentLoc &Input,
-                                   TemplateArgumentLoc &Output,
-                                   bool Uneval = false) {
-      const TemplateArgument &Arg = Input.getArgument();
-      std::vector<TemplateArgument> TArgs;
-      switch (Arg.getKind()) {
-      case TemplateArgument::Pack:
-        assert(SemaRef.CodeSynthesisContexts.empty() ||
-               SemaRef.CodeSynthesisContexts.back().Kind ==
-                   Sema::CodeSynthesisContext::BuildingDeductionGuides);
-        // Literally rewrite the template argument pack, instead of unpacking
-        // it.
-        for (auto &pack : Arg.getPackAsArray()) {
-          TemplateArgumentLoc Input = SemaRef.getTrivialTemplateArgumentLoc(
-              pack, QualType(), SourceLocation{});
-          TemplateArgumentLoc Output;
-          if (TransformTemplateArgument(Input, Output, Uneval))
-            return true; // fails
-          TArgs.push_back(Output.getArgument());
-        }
-        Output = SemaRef.getTrivialTemplateArgumentLoc(
-            TemplateArgument(llvm::ArrayRef(TArgs).copy(SemaRef.Context)),
-            QualType(), SourceLocation{});
-        return false;
-      default:
-        break;
+    return Type;
+  }
+  // Override the default version to handle a rewrite-template-arg-pack case
+  // for building a deduction guide.
+  bool TransformTemplateArgument(const TemplateArgumentLoc &Input,
+                                 TemplateArgumentLoc &Output,
+                                 bool Uneval = false) {
+    const TemplateArgument &Arg = Input.getArgument();
+    std::vector<TemplateArgument> TArgs;
+    switch (Arg.getKind()) {
+    case TemplateArgument::Pack:
+      assert(SemaRef.CodeSynthesisContexts.empty() ||
+             SemaRef.CodeSynthesisContexts.back().Kind ==
+                 Sema::CodeSynthesisContext::BuildingDeductionGuides);
+      // Literally rewrite the template argument pack, instead of unpacking
+      // it.
+      for (auto &pack : Arg.getPackAsArray()) {
+        TemplateArgumentLoc Input = SemaRef.getTrivialTemplateArgumentLoc(
+            pack, QualType(), SourceLocation{});
+        TemplateArgumentLoc Output;
+        if (TransformTemplateArgument(Input, Output, Uneval))
+          return true; // fails
+        TArgs.push_back(Output.getArgument());
       }
-      return inherited::TransformTemplateArgument(Input, Output, Uneval);
+      Output = SemaRef.getTrivialTemplateArgumentLoc(
+          TemplateArgument(llvm::ArrayRef(TArgs).copy(SemaRef.Context)),
+          QualType(), SourceLocation{});
+      return false;
+    default:
+      break;
     }
+    return inherited::TransformTemplateArgument(Input, Output, Uneval);
+  }
 
-    UnsignedOrNone ComputeSizeOfPackExprWithoutSubstitution(
-        ArrayRef<TemplateArgument> PackArgs) {
-      // Don't do this when rewriting template parameters for CTAD:
-      //   1) The heuristic needs the unpacked Subst* nodes to figure out the
-      //   expanded size, but this never applies since Subst* nodes are not
-      //   created in rewrite scenarios.
-      //
-      //   2) The heuristic substitutes into the pattern with pack expansion
-      //   suppressed, which does not meet the requirements for argument
-      //   rewriting when template arguments include a non-pack matching against
-      //   a pack, particularly when rewriting an alias CTAD.
-      if (TemplateArgs.isRewrite())
-        return std::nullopt;
-
-      return inherited::ComputeSizeOfPackExprWithoutSubstitution(PackArgs);
-    }
+  UnsignedOrNone ComputeSizeOfPackExprWithoutSubstitution(
+      ArrayRef<TemplateArgument> PackArgs) {
+    // Don't do this when rewriting template parameters for CTAD:
+    //   1) The heuristic needs the unpacked Subst* nodes to figure out the
+    //   expanded size, but this never applies since Subst* nodes are not
+    //   created in rewrite scenarios.
+    //
+    //   2) The heuristic substitutes into the pattern with pack expansion
+    //   suppressed, which does not meet the requirements for argument
+    //   rewriting when template arguments include a non-pack matching against
+    //   a pack, particularly when rewriting an alias CTAD.
+    if (TemplateArgs.isRewrite())
+      return std::nullopt;
 
-    template<typename Fn>
-    QualType TransformFunctionProtoType(TypeLocBuilder &TLB,
-                                        FunctionProtoTypeLoc TL,
-                                        CXXRecordDecl *ThisContext,
-                                        Qualifiers ThisTypeQuals,
-                                        Fn TransformExceptionSpec);
-
-    ParmVarDecl *TransformFunctionTypeParam(ParmVarDecl *OldParm,
-                                            int indexAdjustment,
-                                            UnsignedOrNone NumExpansions,
-                                            bool ExpectParameterPack);
-
-    using inherited::TransformTemplateTypeParmType;
-    /// Transforms a template type parameter type by performing
-    /// substitution of the corresponding template type argument.
-    QualType TransformTemplateTypeParmType(TypeLocBuilder &TLB,
-                                           TemplateTypeParmTypeLoc TL,
-                                           bool SuppressObjCLifetime);
-
-    QualType BuildSubstTemplateTypeParmType(
-        TypeLocBuilder &TLB, bool SuppressObjCLifetime, bool Final,
-        Decl *AssociatedDecl, unsigned Index, UnsignedOrNone PackIndex,
-        TemplateArgument Arg, SourceLocation NameLoc);
-
-    /// Transforms an already-substituted template type parameter pack
-    /// into either itself (if we aren't substituting into its pack expansion)
-    /// or the appropriate substituted argument.
-    using inherited::TransformSubstTemplateTypeParmPackType;
-    QualType
-    TransformSubstTemplateTypeParmPackType(TypeLocBuilder &TLB,
-                                           SubstTemplateTypeParmPackTypeLoc TL,
-                                           bool SuppressObjCLifetime);
-
-    CXXRecordDecl::LambdaDependencyKind
-    ComputeLambdaDependency(LambdaScopeInfo *LSI) {
-      if (auto TypeAlias =
-              TemplateInstArgsHelpers::getEnclosingTypeAliasTemplateDecl(
-                  getSema());
-          TypeAlias && TemplateInstArgsHelpers::isLambdaEnclosedByTypeAliasDecl(
-                           LSI->CallOperator, TypeAlias.PrimaryTypeAliasDecl)) {
-        unsigned TypeAliasDeclDepth = TypeAlias.Template->getTemplateDepth();
-        if (TypeAliasDeclDepth >= TemplateArgs.getNumSubstitutedLevels())
+    return inherited::ComputeSizeOfPackExprWithoutSubstitution(PackArgs);
+  }
+
+  template <typename Fn>
+  QualType TransformFunctionProtoType(TypeLocBuilder &TLB,
+                                      FunctionProtoTypeLoc TL,
+                                      CXXRecordDecl *ThisContext,
+                                      Qualifiers ThisTypeQuals,
+                                      Fn TransformExceptionSpec);
+
+  ParmVarDecl *TransformFunctionTypeParam(ParmVarDecl *OldParm,
+                                          int indexAdjustment,
+                                          UnsignedOrNone NumExpansions,
+                                          bool ExpectParameterPack);
+
+  using inherited::TransformTemplateTypeParmType;
+  /// Transforms a template type parameter type by performing
+  /// substitution of the corresponding template type argument.
+  QualType TransformTemplateTypeParmType(TypeLocBuilder &TLB,
+                                         TemplateTypeParmTypeLoc TL,
+                                         bool SuppressObjCLifetime);
+
+  QualType BuildSubstTemplateTypeParmType(TypeLocBuilder &TLB,
+                                          bool SuppressObjCLifetime, bool Final,
+                                          Decl *AssociatedDecl, unsigned Index,
+                                          UnsignedOrNone PackIndex,
+                                          TemplateArgument Arg,
+                                          SourceLocation NameLoc);
+
+  /// Transforms an already-substituted template type parameter pack
+  /// into either itself (if we aren't substituting into its pack expansion)
+  /// or the appropriate substituted argument.
+  using inherited::TransformSubstTemplateTypeParmPackType;
+  QualType
+  TransformSubstTemplateTypeParmPackType(TypeLocBuilder &TLB,
+                                         SubstTemplateTypeParmPackTypeLoc TL,
+                                         bool SuppressObjCLifetime);
+
+  CXXRecordDecl::LambdaDependencyKind
+  ComputeLambdaDependency(LambdaScopeInfo *LSI) {
+    if (auto TypeAlias =
+            TemplateInstArgsHelpers::getEnclosingTypeAliasTemplateDecl(
+                getSema());
+        TypeAlias && TemplateInstArgsHelpers::isLambdaEnclosedByTypeAliasDecl(
+                         LSI->CallOperator, TypeAlias.PrimaryTypeAliasDecl)) {
+      unsigned TypeAliasDeclDepth = TypeAlias.Template->getTemplateDepth();
+      if (TypeAliasDeclDepth >= TemplateArgs.getNumSubstitutedLevels())
+        return CXXRecordDecl::LambdaDependencyKind::LDK_AlwaysDependent;
+      for (const TemplateArgument &TA : TypeAlias.AssociatedTemplateArguments)
+        if (TA.isDependent())
           return CXXRecordDecl::LambdaDependencyKind::LDK_AlwaysDependent;
-        for (const TemplateArgument &TA : TypeAlias.AssociatedTemplateArguments)
-          if (TA.isDependent())
-            return CXXRecordDecl::LambdaDependencyKind::LDK_AlwaysDependent;
-      }
-      return inherited::ComputeLambdaDependency(LSI);
     }
+    return inherited::ComputeLambdaDependency(LSI);
+  }
 
-    ExprResult TransformLambdaExpr(LambdaExpr *E) {
-      // Do not rebuild lambdas to avoid creating a new type.
-      // Lambdas have already been processed inside their eval contexts.
-      if (SemaRef.RebuildingImmediateInvocation)
-        return E;
-      LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true,
-                                    /*InstantiatingLambdaOrBlock=*/true);
-      Sema::ConstraintEvalRAII<TemplateInstantiator> RAII(*this);
+  ExprResult TransformLambdaExpr(LambdaExpr *E) {
+    // Do not rebuild lambdas to avoid creating a new type.
+    // Lambdas have already been processed inside their eval contexts.
+    if (SemaRef.RebuildingImmediateInvocation)
+      return E;
+    LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true,
+                                  /*InstantiatingLambdaOrBlock=*/true);
+    Sema::ConstraintEvalRAII<TemplateInstantiator> RAII(*this);
 
-      return inherited::TransformLambdaExpr(E);
-    }
+    return inherited::TransformLambdaExpr(E);
+  }
 
-    ExprResult TransformBlockExpr(BlockExpr *E) {
-      LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true,
-                                    /*InstantiatingLambdaOrBlock=*/true);
-      return inherited::TransformBlockExpr(E);
-    }
+  ExprResult TransformBlockExpr(BlockExpr *E) {
+    LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true,
+                                  /*InstantiatingLambdaOrBlock=*/true);
+    return inherited::TransformBlockExpr(E);
+  }
 
-    ExprResult RebuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc,
-                                 LambdaScopeInfo *LSI) {
-      CXXMethodDecl *MD = LSI->CallOperator;
-      for (ParmVarDecl *PVD : MD->parameters()) {
-        assert(PVD && "null in a parameter list");
-        if (!PVD->hasDefaultArg())
-          continue;
-        Expr *UninstExpr = PVD->getUninstantiatedDefaultArg();
-        // FIXME: Obtain the source location for the '=' token.
-        SourceLocation EqualLoc = UninstExpr->getBeginLoc();
-        if (SemaRef.SubstDefaultArgument(EqualLoc, PVD, TemplateArgs)) {
-          // If substitution fails, the default argument is set to a
-          // RecoveryExpr that wraps the uninstantiated default argument so
-          // that downstream diagnostics are omitted.
-          ExprResult ErrorResult = SemaRef.CreateRecoveryExpr(
-              UninstExpr->getBeginLoc(), UninstExpr->getEndLoc(), {UninstExpr},
-              UninstExpr->getType());
-          if (ErrorResult.isUsable())
-            PVD->setDefaultArg(ErrorResult.get());
-        }
+  ExprResult RebuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc,
+                               LambdaScopeInfo *LSI) {
+    CXXMethodDecl *MD = LSI->CallOperator;
+    for (ParmVarDecl *PVD : MD->parameters()) {
+      assert(PVD && "null in a parameter list");
+      if (!PVD->hasDefaultArg())
+        continue;
+      Expr *UninstExpr = PVD->getUninstantiatedDefaultArg();
+      // FIXME: Obtain the source location for the '=' token.
+      SourceLocation EqualLoc = UninstExpr->getBeginLoc();
+      if (SemaRef.SubstDefaultArgument(EqualLoc, PVD, TemplateArgs)) {
+        // If substitution fails, the default argument is set to a
+        // RecoveryExpr that wraps the uninstantiated default argument so
+        // that downstream diagnostics are omitted.
+        ExprResult ErrorResult = SemaRef.CreateRecoveryExpr(
+            UninstExpr->getBeginLoc(), UninstExpr->getEndLoc(), {UninstExpr},
+            UninstExpr->getType());
+        if (ErrorResult.isUsable())
+          PVD->setDefaultArg(ErrorResult.get());
       }
-      return inherited::RebuildLambdaExpr(StartLoc, EndLoc, LSI);
     }
+    return inherited::RebuildLambdaExpr(StartLoc, EndLoc, LSI);
+  }
 
-    StmtResult TransformLambdaBody(LambdaExpr *E, Stmt *Body) {
-      // Currently, we instantiate the body when instantiating the lambda
-      // expression. However, `EvaluateConstraints` is disabled during the
-      // instantiation of the lambda expression, causing the instantiation
-      // failure of the return type requirement in the body. If p0588r1 is fully
-      // implemented, the body will be lazily instantiated, and this problem
-      // will not occur. Here, `EvaluateConstraints` is temporarily set to
-      // `true` to temporarily fix this issue.
-      // FIXME: This temporary fix can be removed after fully implementing
-      // p0588r1.
-      llvm::SaveAndRestore _(EvaluateConstraints, true);
-      return inherited::TransformLambdaBody(E, Body);
-    }
+  StmtResult TransformLambdaBody(LambdaExpr *E, Stmt *Body) {
+    // Currently, we instantiate the body when instantiating the lambda
+    // expression. However, `EvaluateConstraints` is disabled during the
+    // instantiation of the lambda expression, causing the instantiation
+    // failure of the return type requirement in the body. If p0588r1 is fully
+    // implemented, the body will be lazily instantiated, and this problem
+    // will not occur. Here, `EvaluateConstraints` is temporarily set to
+    // `true` to temporarily fix this issue.
+    // FIXME: This temporary fix can be removed after fully implementing
+    // p0588r1.
+    llvm::SaveAndRestore _(EvaluateConstraints, true);
+    return inherited::TransformLambdaBody(E, Body);
+  }
 
-    ExprResult TransformRequiresExpr(RequiresExpr *E) {
-      LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true);
-      ExprResult TransReq = inherited::TransformRequiresExpr(E);
-      if (TransReq.isInvalid())
-        return TransReq;
-      assert(TransReq.get() != E &&
-             "Do not change value of isSatisfied for the existing expression. "
-             "Create a new expression instead.");
-      if (E->getBody()->isDependentContext()) {
-        Sema::SFINAETrap Trap(SemaRef);
-        // We recreate the RequiresExpr body, but not by instantiating it.
-        // Produce pending diagnostics for dependent access check.
-        SemaRef.PerformDependentDiagnostics(E->getBody(), TemplateArgs);
-        // FIXME: Store SFINAE diagnostics in RequiresExpr for diagnosis.
-        if (Trap.hasErrorOccurred())
-          TransReq.getAs<RequiresExpr>()->setSatisfied(false);
-      }
+  ExprResult TransformRequiresExpr(RequiresExpr *E) {
+    LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true);
+    ExprResult TransReq = inherited::TransformRequiresExpr(E);
+    if (TransReq.isInvalid())
       return TransReq;
+    assert(TransReq.get() != E &&
+           "Do not change value of isSatisfied for the existing expression. "
+           "Create a new expression instead.");
+    if (E->getBody()->isDependentContext()) {
+      Sema::SFINAETrap Trap(SemaRef);
+      // We recreate the RequiresExpr body, but not by instantiating it.
+      // Produce pending diagnostics for dependent access check.
+      SemaRef.PerformDependentDiagnostics(E->getBody(), TemplateArgs);
+      // FIXME: Store SFINAE diagnostics in RequiresExpr for diagnosis.
+      if (Trap.hasErrorOccurred())
+        TransReq.getAs<RequiresExpr>()->setSatisfied(false);
     }
+    return TransReq;
+  }
 
-    bool TransformRequiresExprRequirements(
-        ArrayRef<concepts::Requirement *> Reqs,
-        SmallVectorImpl<concepts::Requirement *> &Transformed) {
-      bool SatisfactionDetermined = false;
-      for (concepts::Requirement *Req : Reqs) {
-        concepts::Requirement *TransReq = nullptr;
-        if (!SatisfactionDetermined) {
-          if (auto *TypeReq = dyn_cast<concepts::TypeRequirement>(Req))
-            TransReq = TransformTypeRequirement(TypeReq);
-          else if (auto *ExprReq = dyn_cast<concepts::ExprRequirement>(Req))
-            TransReq = TransformExprRequirement(ExprReq);
-          else
-            TransReq = TransformNestedRequirement(
-                cast<concepts::NestedRequirement>(Req));
-          if (!TransReq)
-            return true;
-          if (!TransReq->isDependent() && !TransReq->isSatisfied())
-            // [expr.prim.req]p6
-            //   [...]  The substitution and semantic constraint checking
-            //   proceeds in lexical order and stops when a condition that
-            //   determines the result of the requires-expression is
-            //   encountered. [..]
-            SatisfactionDetermined = true;
-        } else
-          TransReq = Req;
-        Transformed.push_back(TransReq);
-      }
-      return false;
+  bool TransformRequiresExprRequirements(
+      ArrayRef<concepts::Requirement *> Reqs,
+      SmallVectorImpl<concepts::Requirement *> &Transformed) {
+    bool SatisfactionDetermined = false;
+    for (concepts::Requirement *Req : Reqs) {
+      concepts::Requirement *TransReq = nullptr;
+      if (!SatisfactionDetermined) {
+        if (auto *TypeReq = dyn_cast<concepts::TypeRequirement>(Req))
+          TransReq = TransformTypeRequirement(TypeReq);
+        else if (auto *ExprReq = dyn_cast<concepts::ExprRequirement>(Req))
+          TransReq = TransformExprRequirement(ExprReq);
+        else
+          TransReq = TransformNestedRequirement(
+              cast<concepts::NestedRequirement>(Req));
+        if (!TransReq)
+          return true;
+        if (!TransReq->isDependent() && !TransReq->isSatisfied())
+          // [expr.prim.req]p6
+          //   [...]  The substitution and semantic constraint checking
+          //   proceeds in lexical order and stops when a condition that
+          //   determines the result of the requires-expression is
+          //   encountered. [..]
+          SatisfactionDetermined = true;
+      } else
+        TransReq = Req;
+      Transformed.push_back(TransReq);
     }
+    return false;
+  }
 
-    TemplateParameterList *TransformTemplateParameterList(
-                              TemplateParameterList *OrigTPL)  {
-      if (!OrigTPL || !OrigTPL->size()) return OrigTPL;
-
-      DeclContext *Owner = OrigTPL->getParam(0)->getDeclContext();
-      TemplateDeclInstantiator  DeclInstantiator(getSema(),
-                        /* DeclContext *Owner */ Owner, TemplateArgs);
-      DeclInstantiator.setEvaluateConstraints(EvaluateConstraints);
-      return DeclInstantiator.SubstTemplateParams(OrigTPL);
-    }
+  TemplateParameterList *
+  TransformTemplateParameterList(TemplateParameterList *OrigTPL) {
+    if (!OrigTPL || !OrigTPL->size())
+      return OrigTPL;
+
+    DeclContext *Owner = OrigTPL->getParam(0)->getDeclContext();
+    TemplateDeclInstantiator DeclInstantiator(getSema(),
+                                              /* DeclContext *Owner */ Owner,
+                                              TemplateArgs);
+    DeclInstantiator.setEvaluateConstraints(EvaluateConstraints);
+    return DeclInstantiator.SubstTemplateParams(OrigTPL);
+  }
 
-    concepts::TypeRequirement *
-    TransformTypeRequirement(concepts::TypeRequirement *Req);
-    concepts::ExprRequirement *
-    TransformExprRequirement(concepts::ExprRequirement *Req);
-    concepts::NestedRequirement *
-    TransformNestedRequirement(concepts::NestedRequirement *Req);
-    ExprResult TransformRequiresTypeParams(
-        SourceLocation KWLoc, SourceLocation RBraceLoc, const RequiresExpr *RE,
-        RequiresExprBodyDecl *Body, ArrayRef<ParmVarDecl *> Params,
-        SmallVectorImpl<QualType> &PTypes,
-        SmallVectorImpl<ParmVarDecl *> &TransParams,
-        Sema::ExtParameterInfoBuilder &PInfos);
-
-  private:
-    ExprResult
-    transformNonTypeTemplateParmRef(Decl *AssociatedDecl,
-                                    const NonTypeTemplateParmDecl *parm,
-                                    SourceLocation loc, TemplateArgument arg,
-                                    UnsignedOrNone PackIndex, bool Final);
-  };
-}
+  concepts::TypeRequirement *
+  TransformTypeRequirement(concepts::TypeRequirement *Req);
+  concepts::ExprRequirement *
+  TransformExprRequirement(concepts::ExprRequirement *Req);
+  concepts::NestedRequirement *
+  TransformNestedRequirement(concepts::NestedRequirement *Req);
+  ExprResult TransformRequiresTypeParams(
+      SourceLocation KWLoc, SourceLocation RBraceLoc, const RequiresExpr *RE,
+      RequiresExprBodyDecl *Body, ArrayRef<ParmVarDecl *> Params,
+      SmallVectorImpl<QualType> &PTypes,
+      SmallVectorImpl<ParmVarDecl *> &TransParams,
+      Sema::ExtParameterInfoBuilder &PInfos);
+
+private:
+  ExprResult
+  transformNonTypeTemplateParmRef(Decl *AssociatedDecl,
+                                  const NonTypeTemplateParmDecl *parm,
+                                  SourceLocation loc, TemplateArgument arg,
+                                  UnsignedOrNone PackIndex, bool Final);
+};
+} // namespace
 
 bool TemplateInstantiator::AlreadyTransformed(QualType T) {
   if (T.isNull())
@@ -2829,69 +2839,76 @@ TemplateInstantiator::TransformExprRequirement(concepts::ExprRequirement *Req) {
 concepts::NestedRequirement *
 TemplateInstantiator::TransformNestedRequirement(
     concepts::NestedRequirement *Req) {
-  if (!Req->isDependent() && !AlwaysRebuild())
-    return Req;
+
+  ASTContext &C = SemaRef.Context;
+
+  Expr *Constraint = Req->getConstraintExpr();
+  ExprResult TransConstraint = Constraint;
+  ConstraintSatisfaction Satisfaction;
+
+  auto NestedReqWithDiag = [&C, this](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));
+  };
+
   if (Req->hasInvalidConstraint()) {
     if (AlwaysRebuild())
       return RebuildNestedRequirement(Req->getInvalidConstraintEntity(),
                                       Req->getConstraintSatisfaction());
     return Req;
   }
-  Sema::InstantiatingTemplate ReqInst(SemaRef,
-      Req->getConstraintExpr()->getBeginLoc(), Req,
-      Sema::InstantiatingTemplate::ConstraintsCheck{},
-      Req->getConstraintExpr()->getSourceRange());
-  if (!getEvaluateConstraints()) {
-    ExprResult TransConstraint = TransformExpr(Req->getConstraintExpr());
-    if (TransConstraint.isInvalid() || !TransConstraint.get())
-      return nullptr;
-    if (TransConstraint.get()->isInstantiationDependent())
-      return new (SemaRef.Context)
-          concepts::NestedRequirement(TransConstraint.get());
-    ConstraintSatisfaction Satisfaction;
-    return new (SemaRef.Context) concepts::NestedRequirement(
-        SemaRef.Context, TransConstraint.get(), Satisfaction);
+
+  if (Req->isDependent() || AlwaysRebuild()) {
+    Sema::InstantiatingTemplate ReqInst(
+        SemaRef, Constraint->getBeginLoc(), Req,
+        Sema::InstantiatingTemplate::ConstraintsCheck{},
+        Constraint->getSourceRange());
+
+    Sema::SFINAETrap Trap(SemaRef);
+
+    TransConstraint = TransformExpr(const_cast<Expr *>(Constraint));
+    if (!TransConstraint.isUsable() || Trap.hasErrorOccurred()) {
+      return NestedReqWithDiag(Constraint, std::move(Satisfaction));
+    }
+    Constraint = TransConstraint.get();
   }
 
-  ExprResult TransConstraint;
-  ConstraintSatisfaction Satisfaction;
-  TemplateDeductionInfo Info(Req->getConstraintExpr()->getBeginLoc());
+  if (Constraint->isInstantiationDependent())
+    return new (C) concepts::NestedRequirement(Constraint);
+
+  bool Success;
+  TemplateDeductionInfo Info(Constraint->getBeginLoc());
   {
     EnterExpressionEvaluationContext ContextRAII(
         SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated);
-    Sema::SFINAETrap Trap(SemaRef);
-    Sema::InstantiatingTemplate ConstrInst(SemaRef,
-        Req->getConstraintExpr()->getBeginLoc(), Req, Info,
-        Req->getConstraintExpr()->getSourceRange());
+
+    Sema::InstantiatingTemplate ConstrInst(SemaRef, Constraint->getBeginLoc(),
+                                           Req, Info,
+                                           Constraint->getSourceRange());
     if (ConstrInst.isInvalid())
       return nullptr;
-    llvm::SmallVector<Expr *> Result;
-    if (!SemaRef.CheckConstraintSatisfaction(
-            nullptr,
-            AssociatedConstraint(Req->getConstraintExpr(),
-                                 SemaRef.ArgPackSubstIndex),
-            Result, TemplateArgs, Req->getConstraintExpr()->getSourceRange(),
-            Satisfaction) &&
-        !Result.empty())
-      TransConstraint = Result[0];
-    assert(!Trap.hasErrorOccurred() && "Substitution failures must be handled "
-                                       "by CheckConstraintSatisfaction.");
-  }
-  ASTContext &C = SemaRef.Context;
-  if (TransConstraint.isUsable() &&
-      TransConstraint.get()->isInstantiationDependent())
-    return new (C) concepts::NestedRequirement(TransConstraint.get());
-  if (TransConstraint.isInvalid() || !TransConstraint.get() ||
-      Satisfaction.HasSubstitutionFailure()) {
-    SmallString<128> Entity;
-    llvm::raw_svector_ostream OS(Entity);
-    Req->getConstraintExpr()->printPretty(OS, nullptr,
-                                          SemaRef.getPrintingPolicy());
-    return new (C) concepts::NestedRequirement(
-        SemaRef.Context, C.backupStr(Entity), Satisfaction);
+
+    Sema::SFINAETrap Trap(SemaRef);
+
+    Success = !SemaRef.CheckConstraintSatisfaction(
+        Req, AssociatedConstraint(Constraint, SemaRef.ArgPackSubstIndex),
+        TemplateArgs, Constraint->getSourceRange(), Satisfaction);
+
+    assert(!Success || !Trap.hasErrorOccurred() &&
+                           "Substitution failures must be handled "
+                           "by CheckConstraintSatisfaction.");
   }
-  return new (C)
-      concepts::NestedRequirement(C, TransConstraint.get(), Satisfaction);
+
+  if (!Success || Satisfaction.HasSubstitutionFailure())
+    return NestedReqWithDiag(Constraint, Satisfaction);
+
+  // FIXME: const correctness
+  return new (C) concepts::NestedRequirement(C, Constraint, Satisfaction);
 }
 
 TypeSourceInfo *Sema::SubstType(TypeSourceInfo *T,
@@ -4394,6 +4411,16 @@ bool Sema::SubstTemplateArguments(
   return Instantiator.TransformTemplateArguments(Args.begin(), Args.end(), Out);
 }
 
+bool Sema::SubstTemplateArgumentsInParameterMapping(
+    ArrayRef<TemplateArgumentLoc> Args,
+    const MultiLevelTemplateArgumentList &TemplateArgs,
+    TemplateArgumentListInfo &Out) {
+  TemplateInstantiator Instantiator(
+      TemplateInstantiator::ForParameterMappingSubstitution, *this,
+      TemplateArgs);
+  return Instantiator.TransformTemplateArguments(Args.begin(), Args.end(), Out);
+}
+
 ExprResult
 Sema::SubstExpr(Expr *E, const MultiLevelTemplateArgumentList &TemplateArgs) {
   if (!E)
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index 5f0e968ff18c4..58cb41cbcbbcb 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -823,6 +823,8 @@ bool Sema::CheckParameterPacksForExpansion(
         continue;
       }
 
+      // TemplateArgs(Depth, Index).dump();
+
       // Determine the size of the argument pack.
       ArrayRef<TemplateArgument> Pack =
           TemplateArgs(Depth, Index).getPackAsArray();
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 335e21d927b76..6e5fa67dfa67c 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -30,6 +30,7 @@
 #include "clang/AST/StmtOpenACC.h"
 #include "clang/AST/StmtOpenMP.h"
 #include "clang/AST/StmtSYCL.h"
+#include "clang/AST/TemplateBase.h"
 #include "clang/Basic/DiagnosticParse.h"
 #include "clang/Basic/OpenMPKinds.h"
 #include "clang/Sema/Designator.h"
@@ -46,8 +47,11 @@
 #include "clang/Sema/SemaPseudoObject.h"
 #include "clang/Sema/SemaSYCL.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/Support/ErrorHandling.h"
 #include <algorithm>
+#include <iterator>
 #include <optional>
 
 using namespace llvm::omp;
@@ -318,6 +322,8 @@ class TreeTransform {
   /// being expanded.
   void ExpandingFunctionParameterPack(ParmVarDecl *Pack) { }
 
+  bool ShouldPreserveTemplateArgumentsPacks() const { return false; }
+
   /// Transforms the given type into another type.
   ///
   /// By default, this routine transforms a type by creating a
@@ -656,6 +662,10 @@ class TreeTransform {
                                   TemplateArgumentListInfo &Outputs,
                                   bool Uneval = false);
 
+  bool TransformTemplateArguments(ArrayRef<TemplateArgument> Args,
+                                  SmallVector<TemplateArgument> &Outputs,
+                                  bool Uneval = false);
+
   /// Fakes up a TemplateArgumentLoc for a given TemplateArgument.
   void InventTemplateArgumentLoc(const TemplateArgument &Arg,
                                  TemplateArgumentLoc &ArgLoc);
@@ -5043,13 +5053,32 @@ bool TreeTransform<Derived>::TransformTemplateArguments(
       typedef TemplateArgumentLocInventIterator<Derived,
                                                 TemplateArgument::pack_iterator>
         PackLocIterator;
-      if (TransformTemplateArguments(PackLocIterator(*this,
-                                                 In.getArgument().pack_begin()),
-                                     PackLocIterator(*this,
-                                                   In.getArgument().pack_end()),
-                                     Outputs, Uneval))
+
+      TemplateArgumentListInfo *PackOutput = &Outputs;
+      TemplateArgumentListInfo New;
+
+      if (getDerived().ShouldPreserveTemplateArgumentsPacks()) {
+        PackOutput = &New;
+      }
+
+      if (TransformTemplateArguments(
+              PackLocIterator(*this, In.getArgument().pack_begin()),
+              PackLocIterator(*this, In.getArgument().pack_end()), *PackOutput,
+              Uneval))
         return true;
 
+      if (getDerived().ShouldPreserveTemplateArgumentsPacks()) {
+        SmallVector<TemplateArgument> Args;
+        Args.reserve(New.size());
+        llvm::transform(
+            New.arguments(), std::back_inserter(Args),
+            [](const TemplateArgumentLoc &Loc) { return Loc.getArgument(); });
+        TemplateArgumentLoc ArgLoc;
+        TemplateArgument Arg =
+            TemplateArgument::CreatePackCopy(SemaRef.getASTContext(), Args);
+        InventTemplateArgumentLoc(Arg, ArgLoc);
+        Outputs.addArgument(ArgLoc);
+      }
       continue;
     }
 
@@ -5142,7 +5171,6 @@ bool TreeTransform<Derived>::TransformTemplateArguments(
   }
 
   return false;
-
 }
 
 //===----------------------------------------------------------------------===//
@@ -6161,7 +6189,7 @@ ParmVarDecl *TreeTransform<Derived>::TransformFunctionTypeParam(
                                              /* DefArg */ nullptr);
   newParm->setScopeInfo(OldParm->getFunctionScopeDepth(),
                         OldParm->getFunctionScopeIndex() + indexAdjustment);
-  transformedLocalDecl(OldParm, {newParm});
+  getDerived().transformedLocalDecl(OldParm, {newParm});
   return newParm;
 }
 
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index b9eabd5ddb64c..a00aa913fa440 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -16,6 +16,7 @@
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/DeclTemplate.h"
+#include "clang/AST/Expr.h"
 #include "clang/AST/ExprOpenMP.h"
 #include "clang/AST/StmtVisitor.h"
 #include "clang/Lex/Token.h"
@@ -475,10 +476,10 @@ addConstraintSatisfaction(ASTRecordWriter &Record,
   if (!Satisfaction.IsSatisfied) {
     Record.push_back(Satisfaction.NumRecords);
     for (const auto &DetailRecord : Satisfaction) {
-      auto *E = dyn_cast<Expr *>(DetailRecord);
+      auto *E = dyn_cast<const Expr *>(DetailRecord);
       Record.push_back(/* IsDiagnostic */ E == nullptr);
       if (E)
-        Record.AddStmt(E);
+        Record.AddStmt(const_cast<Expr *>(E));
       else {
         auto *Diag = cast<std::pair<SourceLocation, StringRef> *>(DetailRecord);
         Record.AddSourceLocation(Diag->first);
diff --git a/clang/test/CXX/drs/cwg25xx.cpp b/clang/test/CXX/drs/cwg25xx.cpp
index d9a7d2bbb2671..f85ef219a93f7 100644
--- a/clang/test/CXX/drs/cwg25xx.cpp
+++ b/clang/test/CXX/drs/cwg25xx.cpp
@@ -223,15 +223,16 @@ namespace cwg2565 { // cwg2565: 16 open 2023-06-07
   //   since-cxx20-note@#cwg2565-VC {{because 'b' would be invalid: argument may not have 'void' type}}
 
   template<typename T>
-  concept ErrorRequires = requires (ErrorRequires auto x) {
+  concept ErrorRequires = requires (ErrorRequires auto x) { // #cwg2565-expr
   // since-cxx20-error at -1 {{a concept definition cannot refer to itself}}
   //   since-cxx20-note at -2 {{declared here}}
   // since-cxx20-error at -3 {{'auto' not allowed in requires expression parameter}}
     x;
   };
   static_assert(ErrorRequires<int>);
-  // since-cxx20-error at -1 {{static assertion failed}}
-  //   since-cxx20-note at -2 {{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
+  // since-cxx20-error at -1 {{static assertion failed}} \
+  //   since-cxx20-note at -1 {{because 'int' does not satisfy 'ErrorRequires'}} \
+  //   since-cxx20-note@#cwg2565-expr {{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
 
   template<typename T>
   concept NestedErrorInRequires = requires (T x) { // #cwg2565-NEIR
@@ -243,8 +244,9 @@ namespace cwg2565 { // cwg2565: 16 open 2023-06-07
     };
   };
   static_assert(NestedErrorInRequires<int>);
-  // since-cxx20-error at -1 {{static assertion failed}}
-  //   since-cxx20-note at -2 {{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
+  // since-cxx20-error at -1 {{static assertion failed}} \
+  //   since-cxx20-note at -1 {{because 'int' does not satisfy 'NestedErrorInRequires'}} \
+  //   since-cxx20-note@#cwg2565-NEIR {{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
 
 #endif
 } // namespace cwg2565
diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.id/p3.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.id/p3.cpp
index 28b5d0adcf054..f4a54535524f2 100644
--- a/clang/test/CXX/expr/expr.prim/expr.prim.id/p3.cpp
+++ b/clang/test/CXX/expr/expr.prim/expr.prim.id/p3.cpp
@@ -140,7 +140,9 @@ concept C7 = sizeof(T) == 1 || sizeof(
         ::type) == 1;
 
 static_assert(!C6<short>);
-static_assert(!C6<char>); // expected-note{{while checking the satisfaction of concept 'C6<char>' requested here}}
+static_assert(!C6<char>);
+// expected-error at -1 {{static assertion failed due to requirement '!C6<char>'}} \
+// expected-note at -1 {{while checking the satisfaction of concept 'C6<char>' requested here}}
 static_assert(C7<char>);
 static_assert(!C7<short>); // expected-note{{while checking the satisfaction of concept 'C7<short>' requested here}}
 
diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp
index 083e743818121..ef495421874e8 100644
--- a/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp
+++ b/clang/test/CXX/temp/temp.constr/temp.constr.atomic/constrant-satisfaction-conversions.cpp
@@ -29,8 +29,7 @@ template<typename T> requires requires {
   // expected-error at -1{{atomic constraint must be of type 'bool' (found 'S<int>')}}
   // expected-note at -2{{while checking the satisfaction}}
   // expected-note at -3{{in instantiation of requirement}}
-  // expected-note at -4{{while checking the satisfaction}}
-  // expected-note at -6{{while substituting template arguments}}
+  // expected-note at -5{{while substituting template arguments}}
   // expected-note@#F3INST{{while checking constraint satisfaction}}
   // expected-note@#F3INST{{in instantiation of function template specialization}}
   //
diff --git a/clang/test/CXX/temp/temp.constr/temp.constr.normal/p1.cpp b/clang/test/CXX/temp/temp.constr/temp.constr.normal/p1.cpp
index 3992835c44402..78f94f15eff05 100644
--- a/clang/test/CXX/temp/temp.constr/temp.constr.normal/p1.cpp
+++ b/clang/test/CXX/temp/temp.constr/temp.constr.normal/p1.cpp
@@ -8,9 +8,9 @@ template<typename T> requires Bar<T> && true struct S<T> { };
 
 template<typename T> concept True2 = sizeof(T) >= 0;
 template<typename T> concept Foo2 = True2<T*>;
-// expected-error at -1{{'type name' declared as a pointer to a reference of type 'type-parameter-0-0 &'}}
+// expected-error at -1 3{{'type name' declared as a pointer to a reference of type 'type-parameter-0-0 &'}}
 template<typename T> concept Bar2 = Foo2<T&>;
-// expected-note at -1{{while substituting into concept arguments here; substitution failures not allowed in concept arguments}}
+// expected-note at -1 3{{while substituting into concept arguments here; substitution failures not allowed in concept arguments}}
 template<typename T> requires Bar2<T> struct S2 { };
 // expected-note at -1{{template is declared here}}
 template<typename T> requires Bar2<T> && true struct S2<T> { };
@@ -71,13 +71,13 @@ namespace non_type_pack {
 namespace PR47174 {
 // This checks that we don't crash with a failed substitution on the first constrained argument when
 // performing normalization.
-template <Bar2 T, True U>
+template <Bar2 T, True U> // expected-note {{while calculating associated constraint of template 'S3' here}}
 requires true struct S3; // expected-note {{template is declared here}}
 template <True T, True U>
 requires true struct S3<T, U>; // expected-error {{class template partial specialization is not more specialized than the primary template}}
 
 // Same as above, for the second position (but this was already working).
-template <True T, Bar2 U>
+template <True T, Bar2 U> // expected-note {{while calculating associated constraint of template 'S4' here}}
 requires true struct S4; // expected-note {{template is declared here}}
 template <True T, True U>
 requires true struct S4<T, U>; // expected-error {{class template partial specialization is not more specialized than the primary template}}
@@ -96,7 +96,7 @@ template<class T> requires C1<T> && C2<T> void t1() = delete; // expected-note {
 template void t1<X>();
 void t1() { t1<X>(); } // expected-error {{call to deleted function 't1'}}
 
-template<class T> requires C1<T> void t2() {}; // expected-note 2 {{candidate function}} 
+template<class T> requires C1<T> void t2() {}; // expected-note 2 {{candidate function}}
 template<class T> requires C2<T> void t2() {}; // expected-note 2 {{candidate function}}
 template void t2<X>(); // expected-error {{partial ordering for explicit instantiation of 't2' is ambiguous}}
 void t2() { t2<X>(); } // expected-error {{call to 't2' is ambiguous}}
diff --git a/clang/test/SemaCXX/cxx23-assume.cpp b/clang/test/SemaCXX/cxx23-assume.cpp
index 7f80cdfe7d452..ea5ea71e555d3 100644
--- a/clang/test/SemaCXX/cxx23-assume.cpp
+++ b/clang/test/SemaCXX/cxx23-assume.cpp
@@ -119,13 +119,13 @@ struct F {
 
 template <typename T>
 constexpr int f5() requires C<T> { return 1; } // expected-note {{while checking the satisfaction}}
-                                               // expected-note at -1 {{while substituting template arguments}}
-                                               // expected-note at -2 {{candidate template ignored}}
+                                               // expected-note at -1 {{candidate template ignored}}
 
 template <typename T>
-constexpr int f5() requires (!C<T>) { return 2; } // expected-note 4 {{while checking the satisfaction}}
-                                                  // expected-note at -1 4 {{while substituting template arguments}}
-                                                  // expected-note at -2 {{candidate template ignored}}
+constexpr int f5() requires (!C<T>) { return 2; } // expected-note 4 {{while checking the satisfaction}} \
+                                                  // expected-note 4 {{while substituting template arguments}} \
+                                                  // expected-note {{candidate template ignored}} \
+                                                  // expected-note {{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
 
 static_assert(f5<int>() == 1);
 static_assert(f5<D>() == 1); // expected-note 3 {{while checking constraint satisfaction}}
diff --git a/clang/test/SemaTemplate/concepts-recovery-expr.cpp b/clang/test/SemaTemplate/concepts-recovery-expr.cpp
index b338f3bc271bf..4636f0d883c1b 100644
--- a/clang/test/SemaTemplate/concepts-recovery-expr.cpp
+++ b/clang/test/SemaTemplate/concepts-recovery-expr.cpp
@@ -4,7 +4,7 @@
 constexpr bool CausesRecoveryExpr = b;
 
 template<typename T>
-concept ReferencesCRE = CausesRecoveryExpr;
+concept ReferencesCRE = CausesRecoveryExpr; // #subst1
 
 template<typename T> requires CausesRecoveryExpr // #NVC1REQ
 void NoViableCands1(){} // #NVC1
@@ -19,16 +19,18 @@ void NVCUse() {
   NoViableCands1<int>();
   // expected-error at -1 {{no matching function for call to 'NoViableCands1'}}
   // expected-note@#NVC1{{candidate template ignored: constraints not satisfied}}
+  // expected-note@#NVC2REQ{{because 'int' does not satisfy 'ReferencesCRE'}}
   // expected-note@#NVC1REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
 
   NoViableCands2<int>();
   // expected-error at -1 {{no matching function for call to 'NoViableCands2'}}
   // expected-note@#NVC2{{candidate template ignored: constraints not satisfied}}
-  // expected-note@#NVC2REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
+  // expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
   NoViableCands3<int>();
   // expected-error at -1 {{no matching function for call to 'NoViableCands3'}}
   // expected-note@#NVC3{{candidate template ignored: constraints not satisfied}}
-  // expected-note@#NVC3REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
+  // expected-note@#NVC3REQ{{because 'int' does not satisfy 'ReferencesCRE'}}
+  // expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
 }
 
 template<typename T> requires CausesRecoveryExpr // #OVC1REQ
@@ -58,12 +60,14 @@ void OVCUse() {
   // expected-error at -1 {{no matching function for call to 'OtherViableCands2'}}
   // expected-note@#OVC2_ALT {{candidate function}}
   // expected-note@#OVC2 {{candidate template ignored: constraints not satisfied}}
-  // expected-note@#OVC2REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
+  // expected-note@#OVC2REQ{{because 'int' does not satisfy 'ReferencesCRE'}}
+  // expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
   OtherViableCands3<int>();
   // expected-error at -1 {{no matching function for call to 'OtherViableCands3'}}
   // expected-note@#OVC3_ALT {{candidate function}}
   // expected-note@#OVC3 {{candidate template ignored: constraints not satisfied}}
-  // expected-note@#OVC3REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
+  // expected-note@#OVC3REQ{{because 'int' does not satisfy 'ReferencesCRE'}}
+  // expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
 }
 
 template<typename T> requires CausesRecoveryExpr // #OBNVC1REQ
@@ -95,13 +99,15 @@ void OBNVCUse() {
   // expected-note@#OBNVC2_ALT {{candidate template ignored: constraints not satisfied}}
   // expected-note@#OBNVC2REQ_ALT {{because 'false' evaluated to false}}
   // expected-note@#OBNVC2 {{candidate template ignored: constraints not satisfied}}
-  // expected-note@#OBNVC2REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
+  // expected-note@#OBNVC2REQ{{because 'int' does not satisfy 'ReferencesCRE'}}
+  // expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
   OtherBadNoViableCands3<int>();
   // expected-error at -1 {{no matching function for call to 'OtherBadNoViableCands3'}}
   // expected-note@#OBNVC3_ALT {{candidate template ignored: constraints not satisfied}}
   // expected-note@#OBNVC3REQ_ALT {{because 'false' evaluated to false}}
   // expected-note@#OBNVC3 {{candidate template ignored: constraints not satisfied}}
-  // expected-note@#OBNVC3REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
+  // expected-note@#OBNVC3REQ{{because 'int' does not satisfy 'ReferencesCRE'}}
+  // expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
 }
 
 
@@ -136,12 +142,14 @@ void MemOVCUse() {
   // expected-error at -1 {{no matching member function for call to 'OtherViableCands2'}}
   // expected-note@#MEMOVC2_ALT {{candidate function}}
   // expected-note@#MEMOVC2 {{candidate template ignored: constraints not satisfied}}
-  // expected-note@#MEMOVC2REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
+  // expected-note@#MEMOVC2REQ{{because 'int' does not satisfy 'ReferencesCRE'}}
+  // expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
   S.OtherViableCands3<int>();
   // expected-error at -1 {{no matching member function for call to 'OtherViableCands3'}}
   // expected-note@#MEMOVC3_ALT {{candidate function}}
   // expected-note@#MEMOVC3 {{candidate template ignored: constraints not satisfied}}
-  // expected-note@#MEMOVC3REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
+  // expected-note@#MEMOVC3REQ{{because 'int' does not satisfy 'ReferencesCRE'}}
+  // expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
 }
 
 struct StaticOVC {
@@ -173,12 +181,14 @@ void StaticMemOVCUse() {
   // expected-error at -1 {{no matching function for call to 'OtherViableCands2'}}
   // expected-note@#SMEMOVC2_ALT {{candidate function}}
   // expected-note@#SMEMOVC2 {{candidate template ignored: constraints not satisfied}}
-  // expected-note@#SMEMOVC2REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
+  // expected-note@#SMEMOVC2REQ{{because 'int' does not satisfy 'ReferencesCRE'}}
+  // expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
   StaticOVC::OtherViableCands3<int>();
   // expected-error at -1 {{no matching function for call to 'OtherViableCands3'}}
   // expected-note@#SMEMOVC3_ALT {{candidate function}}
   // expected-note@#SMEMOVC3 {{candidate template ignored: constraints not satisfied}}
-  // expected-note@#SMEMOVC3REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
+  // expected-note@#SMEMOVC3REQ{{because 'int' does not satisfy 'ReferencesCRE'}}
+  // expected-note@#subst1{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
 }
 
 namespace GH58548 {
diff --git a/clang/test/SemaTemplate/instantiate-expanded-type-constraint.cpp b/clang/test/SemaTemplate/instantiate-expanded-type-constraint.cpp
index 3edf243982958..6f25c78346e08 100644
--- a/clang/test/SemaTemplate/instantiate-expanded-type-constraint.cpp
+++ b/clang/test/SemaTemplate/instantiate-expanded-type-constraint.cpp
@@ -8,7 +8,6 @@ constexpr bool is_same_v<T, T> = true;
 
 template<typename T, typename U>
 concept same_as = is_same_v<T, U>;
-// expected-note at -1{{because 'is_same_v<int, bool>' evaluated to false}}
 
 template<typename T, typename... Us>
 concept either = (is_same_v<T, Us> || ...);

>From e2794910d365888cd9a784dcc56e64ef10fdf917 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Fri, 30 May 2025 14:46:11 +0200
Subject: [PATCH 2/2] Fix handlind of concept-id which appear as an atomic
 constraint

---
 clang/include/clang/AST/ASTConcept.h          |  27 ++-
 clang/lib/AST/ASTConcept.cpp                  |  16 ++
 clang/lib/Sema/SemaConcept.cpp                | 196 ++++++++----------
 ...overload-resolution-deferred-templates.cpp |   3 +-
 4 files changed, 122 insertions(+), 120 deletions(-)

diff --git a/clang/include/clang/AST/ASTConcept.h b/clang/include/clang/AST/ASTConcept.h
index c98dd4629abfc..2064702e0cb06 100644
--- a/clang/include/clang/AST/ASTConcept.h
+++ b/clang/include/clang/AST/ASTConcept.h
@@ -32,6 +32,14 @@ class Expr;
 class NamedDecl;
 struct PrintingPolicy;
 
+/// Pairs of unsatisfied atomic constraint expressions along with the
+/// substituted constraint expr, if the template arguments could be
+/// substituted into them, or a diagnostic if substitution resulted in
+/// an invalid expression.
+using UnsatisfiedConstraintRecord =
+    llvm::PointerUnion<const Expr *, const ConceptReference *,
+                       std::pair<SourceLocation, StringRef> *>;
+
 /// The result of a constraint satisfaction check, containing the necessary
 /// information to diagnose an unsatisfied constraint.
 class ConstraintSatisfaction : public llvm::FoldingSetNode {
@@ -50,7 +58,6 @@ class ConstraintSatisfaction : public llvm::FoldingSetNode {
       : ConstraintOwner(ConstraintOwner), TemplateArgs(TemplateArgs) {}
 
   using SubstitutionDiagnostic = std::pair<SourceLocation, StringRef>;
-  using Detail = llvm::PointerUnion<const Expr *, SubstitutionDiagnostic *>;
 
   bool IsSatisfied = false;
   bool ContainsErrors = false;
@@ -58,7 +65,7 @@ class ConstraintSatisfaction : public llvm::FoldingSetNode {
   /// \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<Detail, 4> Details;
+  llvm::SmallVector<UnsatisfiedConstraintRecord, 4> Details;
 
   void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &C) {
     Profile(ID, C, ConstraintOwner, TemplateArgs);
@@ -76,13 +83,6 @@ class ConstraintSatisfaction : public llvm::FoldingSetNode {
   }
 };
 
-/// Pairs of unsatisfied atomic constraint expressions along with the
-/// substituted constraint expr, if the template arguments could be
-/// substituted into them, or a diagnostic if substitution resulted in
-/// an invalid expression.
-using UnsatisfiedConstraintRecord =
-    llvm::PointerUnion<const Expr *, std::pair<SourceLocation, StringRef> *>;
-
 /// \brief The result of a constraint satisfaction check, containing the
 /// necessary information to diagnose an unsatisfied constraint.
 ///
@@ -102,6 +102,10 @@ struct ASTConstraintSatisfaction final :
     return getTrailingObjects() + NumRecords;
   }
 
+  ArrayRef<UnsatisfiedConstraintRecord> records() const {
+    return {begin(), end()};
+  }
+
   ASTConstraintSatisfaction(const ASTContext &C,
                             const ConstraintSatisfaction &Satisfaction);
   ASTConstraintSatisfaction(const ASTContext &C,
@@ -287,6 +291,11 @@ class TypeConstraint {
   }
 };
 
+/// Insertion operator for diagnostics.  This allows sending TemplateName's
+/// into a diagnostic with <<.
+const StreamingDiagnostic &operator<<(const StreamingDiagnostic &DB,
+                                      const ConceptReference *C);
+
 } // clang
 
 #endif // LLVM_CLANG_AST_ASTCONCEPT_H
diff --git a/clang/lib/AST/ASTConcept.cpp b/clang/lib/AST/ASTConcept.cpp
index d4dfc75fd25d5..f0e4d442e40e3 100644
--- a/clang/lib/AST/ASTConcept.cpp
+++ b/clang/lib/AST/ASTConcept.cpp
@@ -27,6 +27,9 @@ CreateUnsatisfiedConstraintRecord(const ASTContext &C,
     new (TrailingObject) UnsatisfiedConstraintRecord(nullptr);
   else if (const auto *E = llvm::dyn_cast<const Expr *>(Detail))
     new (TrailingObject) UnsatisfiedConstraintRecord(E);
+  else if (const auto *Concept =
+               llvm::dyn_cast<const ConceptReference *>(Detail))
+    new (TrailingObject) UnsatisfiedConstraintRecord(Concept);
   else {
     auto &SubstitutionDiagnostic =
         *cast<std::pair<SourceLocation, StringRef> *>(Detail);
@@ -112,6 +115,19 @@ void ConceptReference::print(llvm::raw_ostream &OS,
   }
 }
 
+const StreamingDiagnostic &clang::operator<<(const StreamingDiagnostic &DB,
+                                             const ConceptReference *C) {
+  std::string NameStr;
+  llvm::raw_string_ostream OS(NameStr);
+  LangOptions LO;
+  LO.CPlusPlus = true;
+  LO.Bool = true;
+  OS << '\'';
+  C->print(OS, PrintingPolicy(LO));
+  OS << '\'';
+  return DB << NameStr;
+}
+
 concepts::ExprRequirement::ExprRequirement(
     Expr *E, bool IsSimple, SourceLocation NoexceptLoc,
     ReturnTypeRequirement Req, SatisfactionStatus Status,
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 7c8d80ba4ca34..22e8ba5b0eef3 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -185,27 +185,20 @@ struct SatisfactionStackRAII {
 };
 } // namespace
 
-static bool
-DiagRecursiveConstraintEval(Sema &S, llvm::FoldingSetNodeID &ID,
-                            const NamedDecl *Templ, const Expr *E,
-                            const MultiLevelTemplateArgumentList &MLTAL) {
+static bool DiagRecursiveConstraintEval(
+    Sema &S, llvm::FoldingSetNodeID &ID, const NamedDecl *Templ, const Expr *E,
+    const MultiLevelTemplateArgumentList *MLTAL = nullptr) {
   E->Profile(ID, S.Context, /*Canonical=*/true);
-  for (const auto &List : MLTAL)
-    for (const auto &TemplateArg : List.Args)
-      TemplateArg.Profile(ID, S.Context);
-
-  // Note that we have to do this with our own collection, because there are
-  // times where a constraint-expression check can cause us to need to evaluate
-  // other constriants that are unrelated, such as when evaluating a recovery
-  // expression, or when trying to determine the constexpr-ness of special
-  // members. Otherwise we could just use the
-  // Sema::InstantiatingTemplate::isAlreadyBeingInstantiated function.
+  if (MLTAL) {
+    for (const auto &List : *MLTAL)
+      for (const auto &TemplateArg : List.Args)
+        TemplateArg.Profile(ID, S.Context);
+  }
   if (S.SatisfactionStackContains(Templ, ID)) {
     S.Diag(E->getExprLoc(), diag::err_constraint_depends_on_self)
         << const_cast<Expr *>(E) << E->getSourceRange();
     return true;
   }
-
   return false;
 }
 
@@ -261,6 +254,15 @@ static ExprResult EvaluateAtomicConstraint(
       S, Sema::ExpressionEvaluationContext::ConstantEvaluated,
       Sema::ReuseLambdaContextDecl);
 
+  llvm::FoldingSetNodeID ID;
+  if (Template &&
+      DiagRecursiveConstraintEval(S, ID, Template, AtomicExpr, &MLTAL)) {
+    Satisfaction.IsSatisfied = false;
+    Satisfaction.ContainsErrors = true;
+    return ExprEmpty();
+  }
+  SatisfactionStackRAII StackRAII(S, Template, ID);
+
   // Atomic constraint - substitute arguments and check satisfaction.
   ExprResult SubstitutedExpression = const_cast<Expr *>(AtomicExpr);
   {
@@ -273,17 +275,6 @@ static ExprResult EvaluateAtomicConstraint(
     if (Inst.isInvalid())
       return ExprError();
 
-    /*llvm::FoldingSetNodeID ID;
-    if (Template &&
-        DiagRecursiveConstraintEval(S, ID, Template, AtomicExpr, MLTAL)) {
-      Satisfaction.IsSatisfied = false;
-      Satisfaction.ContainsErrors = true;
-      return ExprEmpty();
-    }
-
-    SatisfactionStackRAII StackRAII(S, Template, ID);
-    */
-
     // We do not want error diagnostics escaping here.
     Sema::SFINAETrap Trap(S);
 
@@ -621,7 +612,9 @@ static bool calculateConstraintSatisfaction(
 
     Satisfaction.Details.insert(
         Satisfaction.Details.begin() + Size,
-        ConstraintSatisfaction::Detail(SubstitutedConceptId.get()));
+        UnsatisfiedConstraintRecord(
+            SubstitutedConceptId.getAs<ConceptSpecializationExpr>()
+                ->getConceptReference()));
 
     Satisfaction.Details.push_back(nullptr);
   }
@@ -1266,57 +1259,56 @@ static void diagnoseUnsatisfiedRequirement(Sema &S,
   }
 }
 
-static void
-diagnoseUnsatisfiedConceptIdExpr(Sema &S, const ConceptSpecializationExpr *CSE,
-                                 SourceLocation Loc, bool First) {
-  if (CSE->getTemplateArgsAsWritten()->NumTemplateArgs == 1) {
+static void diagnoseUnsatisfiedConceptIdExpr(Sema &S,
+                                             const ConceptReference *Concept,
+                                             SourceLocation Loc, bool First) {
+  if (Concept->getTemplateArgsAsWritten()->NumTemplateArgs == 1) {
     S.Diag(
         Loc,
         diag::
             note_single_arg_concept_specialization_constraint_evaluated_to_false)
         << (int)First
-        << CSE->getTemplateArgsAsWritten()->arguments()[0].getArgument()
-        << CSE->getNamedConcept();
+        << Concept->getTemplateArgsAsWritten()->arguments()[0].getArgument()
+        << Concept->getNamedConcept();
   } else {
     S.Diag(Loc, diag::note_concept_specialization_constraint_evaluated_to_false)
-        << (int)First << CSE;
+        << (int)First << Concept;
   }
 }
 
-static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
-                                                        const Expr *SubstExpr,
-                                                        bool First = true);
+static void diagnoseUnsatisfiedConstraintExpr(
+    Sema &S, const UnsatisfiedConstraintRecord &Record, SourceLocation Loc,
+    bool First, concepts::NestedRequirement *Req = nullptr);
 
-static void diagnoseUnsatisfiedRequirement(Sema &S,
-                                           concepts::NestedRequirement *Req,
-                                           bool First) {
-  using SubstitutionDiagnostic = std::pair<SourceLocation, StringRef>;
+static void DiagnoseUnsatisfiedConstraint(
+    Sema &S, ArrayRef<UnsatisfiedConstraintRecord> Records, SourceLocation Loc,
+    bool First = true, concepts::NestedRequirement *Req = nullptr) {
   std::vector<bool> Prevs;
-  for (auto &Record : Req->getConstraintSatisfaction()) {
-    if (auto *SubstDiag = Record.dyn_cast<SubstitutionDiagnostic *>())
-      S.Diag(SubstDiag->first, diag::note_nested_requirement_substitution_error)
-          << (int)First << Req->getInvalidConstraintEntity()
-          << SubstDiag->second;
-    else {
-      if (Record.isNull()) {
-        First = Prevs.back();
-        Prevs.pop_back();
-        continue;
-      }
-      diagnoseWellFormedUnsatisfiedConstraintExpr(
-          S, Record.dyn_cast<const Expr *>(), First);
-      if (isa_and_nonnull<ConceptSpecializationExpr>(
-              Record.dyn_cast<const Expr *>())) {
-        Prevs.push_back(First);
-        First = true;
-        continue;
-      }
-      First = false;
+  for (auto &Record : Records) {
+    if (Record.isNull()) {
+      First = Prevs.back();
+      Prevs.pop_back();
+      continue;
+    }
+    diagnoseUnsatisfiedConstraintExpr(S, Record, Loc, First, Req);
+    Loc = {};
+    if (isa<const ConceptReference *>(Record)) {
+      Prevs.push_back(First);
+      First = true;
+      continue;
     }
     First = false;
   }
 }
 
+static void diagnoseUnsatisfiedRequirement(Sema &S,
+                                           concepts::NestedRequirement *Req,
+                                           bool First) {
+  DiagnoseUnsatisfiedConstraint(S, Req->getConstraintSatisfaction().records(),
+                                Req->getConstraintExpr()->getExprLoc(), First,
+                                Req);
+}
+
 static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
                                                         const Expr *SubstExpr,
                                                         bool First) {
@@ -1396,6 +1388,10 @@ static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
         break;
       }
     return;
+  } else if (auto *CSE = dyn_cast<ConceptSpecializationExpr>(SubstExpr)) {
+    // Drill down concept ids treated as atomic constraints
+    S.DiagnoseUnsatisfiedConstraint(CSE);
+    return;
   } else if (auto *TTE = dyn_cast<TypeTraitExpr>(SubstExpr);
              TTE && TTE->getTrait() == clang::TypeTrait::BTT_IsDeducible) {
     assert(TTE->getNumArgs() == 2);
@@ -1411,74 +1407,49 @@ static void diagnoseWellFormedUnsatisfiedConstraintExpr(Sema &S,
   S.DiagnoseTypeTraitDetails(SubstExpr);
 }
 
-static void
-diagnoseUnsatisfiedConstraintExpr(Sema &S,
-                                  const ConstraintSatisfaction::Detail &Record,
-                                  SourceLocation Loc, bool First = true) {
+static void diagnoseUnsatisfiedConstraintExpr(
+    Sema &S, const UnsatisfiedConstraintRecord &Record, SourceLocation Loc,
+    bool First, concepts::NestedRequirement *Req) {
   if (auto *Diag = Record.template dyn_cast<
                    ConstraintSatisfaction::SubstitutionDiagnostic *>()) {
-    S.Diag(Diag->first, diag::note_substituted_constraint_expr_is_ill_formed)
-        << Diag->second;
+    if (Req)
+      S.Diag(Diag->first, diag::note_nested_requirement_substitution_error)
+          << (int)First << Req->getInvalidConstraintEntity() << Diag->second;
+    else
+      S.Diag(Diag->first, diag::note_substituted_constraint_expr_is_ill_formed)
+          << Diag->second;
     return;
   }
-
-  const auto *Expr = cast<const class Expr *>(Record);
-  if (const auto *CSE = dyn_cast<ConceptSpecializationExpr>(Expr)) {
+  if (const auto *Concept = dyn_cast<const ConceptReference *>(Record)) {
     if (Loc.isInvalid())
-      Loc = CSE->getBeginLoc();
-    diagnoseUnsatisfiedConceptIdExpr(S, CSE, Loc, First);
+      Loc = Concept->getBeginLoc();
+    diagnoseUnsatisfiedConceptIdExpr(S, Concept, Loc, First);
     return;
   }
-  diagnoseWellFormedUnsatisfiedConstraintExpr(S, Expr, First);
+  diagnoseWellFormedUnsatisfiedConstraintExpr(
+      S, cast<const class Expr *>(Record), First);
 }
 
 void Sema::DiagnoseUnsatisfiedConstraint(
     const ConstraintSatisfaction &Satisfaction, SourceLocation Loc,
     bool First) {
+
   assert(!Satisfaction.IsSatisfied &&
          "Attempted to diagnose a satisfied constraint");
-  std::vector<bool> Prevs;
-  for (auto &Record : Satisfaction.Details) {
-    if (Record.isNull()) {
-      First = Prevs.back();
-      Prevs.pop_back();
-      continue;
-    }
-    diagnoseUnsatisfiedConstraintExpr(*this, Record, Loc, First);
-    First = false;
-    Loc = {};
-    if (isa_and_nonnull<ConceptSpecializationExpr>(
-            Record.dyn_cast<const Expr *>())) {
-      Prevs.push_back(First);
-      First = true;
-    }
-  }
+  ::DiagnoseUnsatisfiedConstraint(*this, Satisfaction.Details, Loc, First);
 }
 
 void Sema::DiagnoseUnsatisfiedConstraint(
     const ConceptSpecializationExpr *ConstraintExpr) {
+
   const ASTConstraintSatisfaction &Satisfaction =
       ConstraintExpr->getSatisfaction();
-  bool First = true;
-  SourceLocation Loc = ConstraintExpr->getBeginLoc();
+
   assert(!Satisfaction.IsSatisfied &&
          "Attempted to diagnose a satisfied constraint");
-  std::vector<bool> Prevs;
-  for (auto &Record : Satisfaction) {
-    if (Record.isNull()) {
-      First = Prevs.back();
-      Prevs.pop_back();
-      continue;
-    }
-    diagnoseUnsatisfiedConstraintExpr(*this, Record, Loc, First);
-    Loc = {};
-    if (isa_and_nonnull<ConceptSpecializationExpr>(
-            Record.dyn_cast<const Expr *>())) {
-      Prevs.push_back(First);
-      First = true;
-      continue;
-    }
-  }
+
+  ::DiagnoseUnsatisfiedConstraint(*this, Satisfaction.records(),
+                                  ConstraintExpr->getBeginLoc());
 }
 
 const NormalizedConstraint *Sema::getNormalizedAssociatedConstraints(
@@ -1673,6 +1644,12 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D,
   // [...]
   E = E->IgnoreParenImpCasts();
 
+  llvm::FoldingSetNodeID ID;
+  if (D && DiagRecursiveConstraintEval(S, ID, D, E)) {
+    return nullptr;
+  }
+  SatisfactionStackRAII StackRAII(S, D, ID);
+
   // C++2a [temp.param]p4:
   //     [...] If T is not a pack, then E is E', otherwise E is (E' && ...).
   // Fold expression is considered atomic constraints per current wording.
@@ -1723,8 +1700,9 @@ NormalizedConstraint::fromConstraintExpr(Sema &S, const NamedDecl *D,
                                        S.ArgPackSubstIndex);
 
   } else if (auto *FE = dyn_cast<const CXXFoldExpr>(E);
-             FE && (FE->getOperator() == BinaryOperatorKind::BO_LAnd ||
-                    FE->getOperator() == BinaryOperatorKind::BO_LOr)) {
+             FE && S.getLangOpts().CPlusPlus26 &&
+             (FE->getOperator() == BinaryOperatorKind::BO_LAnd ||
+              FE->getOperator() == BinaryOperatorKind::BO_LOr)) {
 
     // Normalize fold expressions in C++26.
 
diff --git a/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp b/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
index 4c5ac5dcbbd04..be3de8c6ad49a 100644
--- a/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
+++ b/clang/test/SemaCXX/overload-resolution-deferred-templates.cpp
@@ -94,7 +94,7 @@ static_assert(__is_constructible(Movable, int));
 // expected-error at -1{{static assertion failed due to requirement '__is_constructible(Movable, int)'}} \
 // expected-note at -1 2{{}}
 // expected-error@#err-self-constraint-1{{satisfaction of constraint '__is_constructible(Movable, T)' depends on itself}}
-// expected-note@#err-self-constraint-1 4{{}}
+// expected-note@#err-self-constraint-1 3{{}}
 
 template <typename T>
 struct Members {
@@ -191,7 +191,6 @@ void h(short n) { f(n); }
 // expected-note at -1{{while checking constraint satisfaction for template}}
 // expected-note@#GH62096-note1{{in instantiation}}
 // expected-note@#GH62096-note1{{while substituting template arguments into constraint expression here}}
-// expected-note@#GH62096-note2{{while substituting template arguments into constraint expression here}}
 // expected-note@#GH62096-note2{{while checking the satisfaction of concept}}
 // expected-note@#GH62096-err {{expression evaluates}}
 }



More information about the cfe-commits mailing list