[Lldb-commits] [clang] [lldb] [Clang] Initial support for P2841 (Variable template and concept template parameters) (PR #150823)

Corentin Jabot via lldb-commits lldb-commits at lists.llvm.org
Tue Jul 29 01:37:12 PDT 2025


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

>From ef82ffb4300c301b19965b421dc52ba524ea7bd3 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Sun, 27 Jul 2025 12:16:12 +0200
Subject: [PATCH 01/10] [WIP] Variable and concept template parameters

---
 clang/include/clang/AST/ASTConcept.h          |  16 +-
 clang/include/clang/AST/ASTContext.h          |  11 +-
 clang/include/clang/AST/DeclTemplate.h        |  47 ++-
 clang/include/clang/AST/ExprCXX.h             |  47 ++-
 clang/include/clang/AST/ExprConcepts.h        |   4 +-
 clang/include/clang/AST/TemplateBase.h        |   2 +
 clang/include/clang/AST/Type.h                |   8 +-
 clang/include/clang/AST/TypeLoc.h             |   2 +-
 clang/include/clang/AST/TypeProperties.td     |   4 +-
 .../clang/Basic/DiagnosticSemaKinds.td        |  19 +-
 clang/include/clang/Sema/Initialization.h     |   6 +-
 clang/include/clang/Sema/Ownership.h          |   1 +
 clang/include/clang/Sema/ParsedTemplate.h     | 212 +++++-----
 clang/include/clang/Sema/Sema.h               |  20 +-
 clang/include/clang/Sema/SemaInternal.h       |   1 +
 clang/lib/AST/ASTConcept.cpp                  |   2 +-
 clang/lib/AST/ASTContext.cpp                  |  26 +-
 clang/lib/AST/ASTImporter.cpp                 |   4 +-
 clang/lib/AST/ASTStructuralEquivalence.cpp    |   3 +-
 clang/lib/AST/ComputeDependence.cpp           |   2 +-
 clang/lib/AST/Decl.cpp                        |   3 +
 clang/lib/AST/DeclTemplate.cpp                |  41 +-
 clang/lib/AST/ExprCXX.cpp                     |  36 +-
 clang/lib/AST/ItaniumMangle.cpp               |   4 +-
 clang/lib/AST/TemplateBase.cpp                |  12 +
 clang/lib/AST/Type.cpp                        |   7 +-
 clang/lib/Parse/ParseDecl.cpp                 |   2 +-
 clang/lib/Parse/ParseTemplate.cpp             |  83 ++--
 clang/lib/Parse/ParseTentative.cpp            |   2 +-
 clang/lib/Parse/Parser.cpp                    |   7 +-
 clang/lib/Sema/SemaChecking.cpp               |   3 +-
 clang/lib/Sema/SemaExpr.cpp                   |   8 +-
 clang/lib/Sema/SemaInit.cpp                   |   2 +-
 clang/lib/Sema/SemaTemplate.cpp               | 385 +++++++++++++-----
 clang/lib/Sema/SemaTemplateDeduction.cpp      | 218 +++++++---
 clang/lib/Sema/SemaTemplateInstantiate.cpp    |  56 ++-
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |   6 +-
 clang/lib/Sema/SemaTemplateVariadic.cpp       |  10 +
 clang/lib/Sema/SemaType.cpp                   |  10 +-
 clang/lib/Sema/TreeTransform.h                |  79 +++-
 clang/lib/Serialization/ASTReaderDecl.cpp     |   1 +
 clang/lib/Serialization/ASTReaderStmt.cpp     |   8 +-
 clang/lib/Serialization/ASTWriterDecl.cpp     |   1 +
 clang/test/CodeGenCXX/mangle-concept.cpp      |   3 +-
 .../Parser/cxx-template-template-recovery.cpp |  12 +-
 .../test/Parser/cxx2a-concept-declaration.cpp |   5 -
 .../Parser/cxx2c-template-template-param.cpp  |  79 ++++
 .../SemaCXX/cxx2c-template-template-param.cpp | 372 +++++++++++++++++
 .../TableGen/ClangBuiltinTemplatesEmitter.cpp |   3 +-
 .../TypeSystem/Clang/TypeSystemClang.cpp      |  10 +-
 50 files changed, 1467 insertions(+), 438 deletions(-)
 create mode 100644 clang/test/Parser/cxx2c-template-template-param.cpp
 create mode 100644 clang/test/SemaCXX/cxx2c-template-template-param.cpp

diff --git a/clang/include/clang/AST/ASTConcept.h b/clang/include/clang/AST/ASTConcept.h
index c8f6330a73bb1..7ccac4481b14c 100644
--- a/clang/include/clang/AST/ASTConcept.h
+++ b/clang/include/clang/AST/ASTConcept.h
@@ -27,6 +27,7 @@
 namespace clang {
 
 class ConceptDecl;
+class TemplateDecl;
 class Expr;
 class NamedDecl;
 struct PrintingPolicy;
@@ -123,6 +124,7 @@ struct ASTConstraintSatisfaction final :
 ///   template <std::derives_from<Expr> T> void dump();
 ///             ~~~~~~~~~~~~~~~~~~~~~~~ (in TemplateTypeParmDecl)
 class ConceptReference {
+protected:
   // \brief The optional nested name specifier used when naming the concept.
   NestedNameSpecifierLoc NestedNameSpec;
 
@@ -140,7 +142,7 @@ class ConceptReference {
   NamedDecl *FoundDecl;
 
   /// \brief The concept named.
-  ConceptDecl *NamedConcept;
+  TemplateDecl *NamedConcept;
 
   /// \brief The template argument list source info used to specialize the
   /// concept.
@@ -148,7 +150,7 @@ class ConceptReference {
 
   ConceptReference(NestedNameSpecifierLoc NNS, SourceLocation TemplateKWLoc,
                    DeclarationNameInfo ConceptNameInfo, NamedDecl *FoundDecl,
-                   ConceptDecl *NamedConcept,
+                   TemplateDecl *NamedConcept,
                    const ASTTemplateArgumentListInfo *ArgsAsWritten)
       : NestedNameSpec(NNS), TemplateKWLoc(TemplateKWLoc),
         ConceptName(ConceptNameInfo), FoundDecl(FoundDecl),
@@ -158,7 +160,7 @@ class ConceptReference {
   static ConceptReference *
   Create(const ASTContext &C, NestedNameSpecifierLoc NNS,
          SourceLocation TemplateKWLoc, DeclarationNameInfo ConceptNameInfo,
-         NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
+         NamedDecl *FoundDecl, TemplateDecl *NamedConcept,
          const ASTTemplateArgumentListInfo *ArgsAsWritten);
 
   const NestedNameSpecifierLoc &getNestedNameSpecifierLoc() const {
@@ -197,9 +199,7 @@ class ConceptReference {
     return FoundDecl;
   }
 
-  ConceptDecl *getNamedConcept() const {
-    return NamedConcept;
-  }
+  TemplateDecl *getNamedConcept() const { return NamedConcept; }
 
   const ASTTemplateArgumentListInfo *getTemplateArgsAsWritten() const {
     return ArgsAsWritten;
@@ -252,7 +252,9 @@ class TypeConstraint {
 
   // FIXME: Instead of using these concept related functions the callers should
   // directly work with the corresponding ConceptReference.
-  ConceptDecl *getNamedConcept() const { return ConceptRef->getNamedConcept(); }
+  TemplateDecl *getNamedConcept() const {
+    return ConceptRef->getNamedConcept();
+  }
 
   SourceLocation getConceptNameLoc() const {
     return ConceptRef->getConceptNameLoc();
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 0273109f8a698..8bdcff8b17fda 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -1753,7 +1753,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
   QualType
   getAutoTypeInternal(QualType DeducedType, AutoTypeKeyword Keyword,
                       bool IsDependent, bool IsPack = false,
-                      ConceptDecl *TypeConstraintConcept = nullptr,
+                      TemplateDecl *TypeConstraintConcept = nullptr,
                       ArrayRef<TemplateArgument> TypeConstraintArgs = {},
                       bool IsCanon = false) const;
 
@@ -1973,10 +1973,11 @@ class ASTContext : public RefCountedBase<ASTContext> {
                                  UnaryTransformType::UTTKind UKind) const;
 
   /// C++11 deduced auto type.
-  QualType getAutoType(QualType DeducedType, AutoTypeKeyword Keyword,
-                       bool IsDependent, bool IsPack = false,
-                       ConceptDecl *TypeConstraintConcept = nullptr,
-                       ArrayRef<TemplateArgument> TypeConstraintArgs ={}) const;
+  QualType
+  getAutoType(QualType DeducedType, AutoTypeKeyword Keyword, bool IsDependent,
+              bool IsPack = false,
+              TemplateDecl *TypeConstraintConcept = nullptr,
+              ArrayRef<TemplateArgument> TypeConstraintArgs = {}) const;
 
   /// C++11 deduction pattern for 'auto' type.
   QualType getAutoDeductType() const;
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index 1ff6cc6fcb7d1..8e25f239fb97f 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -26,6 +26,7 @@
 #include "clang/Basic/LLVM.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/Specifiers.h"
+#include "clang/Basic/TemplateKinds.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/FoldingSet.h"
 #include "llvm/ADT/PointerIntPair.h"
@@ -1585,6 +1586,9 @@ class TemplateTemplateParmDecl final
       DefaultArgStorage<TemplateTemplateParmDecl, TemplateArgumentLoc *>;
   DefArgStorage DefaultArgument;
 
+  LLVM_PREFERRED_TYPE(TemplateNameKind)
+  unsigned ParameterKind : 3;
+
   /// Whether this template template parameter was declaration with
   /// the 'typename' keyword.
   ///
@@ -1607,13 +1611,16 @@ class TemplateTemplateParmDecl final
 
   TemplateTemplateParmDecl(DeclContext *DC, SourceLocation L, unsigned D,
                            unsigned P, bool ParameterPack, IdentifierInfo *Id,
-                           bool Typename, TemplateParameterList *Params)
+                           TemplateNameKind ParameterKind, bool Typename,
+                           TemplateParameterList *Params)
       : TemplateDecl(TemplateTemplateParm, DC, L, Id, Params),
-        TemplateParmPosition(D, P), Typename(Typename),
-        ParameterPack(ParameterPack), ExpandedParameterPack(false) {}
+        TemplateParmPosition(D, P), ParameterKind(ParameterKind),
+        Typename(Typename), ParameterPack(ParameterPack),
+        ExpandedParameterPack(false) {}
 
   TemplateTemplateParmDecl(DeclContext *DC, SourceLocation L, unsigned D,
-                           unsigned P, IdentifierInfo *Id, bool Typename,
+                           unsigned P, IdentifierInfo *Id,
+                           TemplateNameKind ParameterKind, bool Typename,
                            TemplateParameterList *Params,
                            ArrayRef<TemplateParameterList *> Expansions);
 
@@ -1624,15 +1631,16 @@ class TemplateTemplateParmDecl final
   friend class ASTDeclWriter;
   friend TrailingObjects;
 
-  static TemplateTemplateParmDecl *Create(const ASTContext &C, DeclContext *DC,
-                                          SourceLocation L, unsigned D,
-                                          unsigned P, bool ParameterPack,
-                                          IdentifierInfo *Id, bool Typename,
-                                          TemplateParameterList *Params);
   static TemplateTemplateParmDecl *
   Create(const ASTContext &C, DeclContext *DC, SourceLocation L, unsigned D,
-         unsigned P, IdentifierInfo *Id, bool Typename,
-         TemplateParameterList *Params,
+         unsigned P, bool ParameterPack, IdentifierInfo *Id,
+         TemplateNameKind ParameterKind, bool Typename,
+         TemplateParameterList *Params);
+
+  static TemplateTemplateParmDecl *
+  Create(const ASTContext &C, DeclContext *DC, SourceLocation L, unsigned D,
+         unsigned P, IdentifierInfo *Id, TemplateNameKind ParameterKind,
+         bool Typename, TemplateParameterList *Params,
          ArrayRef<TemplateParameterList *> Expansions);
 
   static TemplateTemplateParmDecl *CreateDeserialized(ASTContext &C,
@@ -1746,6 +1754,16 @@ class TemplateTemplateParmDecl final
     return SourceRange(getTemplateParameters()->getTemplateLoc(), End);
   }
 
+  TemplateNameKind kind() const {
+    return static_cast<TemplateNameKind>(ParameterKind);
+  }
+
+  bool isTypeConceptTemplateParam() const {
+    return kind() == TemplateNameKind::TNK_Concept_template &&
+           getTemplateParameters()->size() > 0 &&
+           isa<TemplateTypeParmDecl>(getTemplateParameters()->getParam(0));
+  }
+
   // Implement isa/cast/dyncast/etc.
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
   static bool classofKind(Kind K) { return K == TemplateTemplateParm; }
@@ -3341,7 +3359,12 @@ inline TemplateDecl *getAsTypeTemplateDecl(Decl *D) {
   return TD && (isa<ClassTemplateDecl>(TD) ||
                 isa<ClassTemplatePartialSpecializationDecl>(TD) ||
                 isa<TypeAliasTemplateDecl>(TD) ||
-                isa<TemplateTemplateParmDecl>(TD))
+                [&]() {
+                  if (TemplateTemplateParmDecl *TTP =
+                          dyn_cast<TemplateTemplateParmDecl>(TD))
+                    return TTP->kind() == TNK_Type_template;
+                  return false;
+                }())
              ? TD
              : nullptr;
 }
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index a22c32241ac61..a78ad0999d198 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -38,6 +38,7 @@
 #include "clang/Basic/OperatorKinds.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/Specifiers.h"
+#include "clang/Basic/TemplateKinds.h"
 #include "clang/Basic/TypeTraits.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/PointerUnion.h"
@@ -3257,7 +3258,49 @@ class OverloadExpr : public Expr {
   bool hasTemplateKeyword() const { return getTemplateKeywordLoc().isValid(); }
 
   /// Determines whether this expression had explicit template arguments.
-  bool hasExplicitTemplateArgs() const { return getLAngleLoc().isValid(); }
+  bool hasExplicitTemplateArgs() const {
+    if (!hasTemplateKWAndArgsInfo())
+      return false;
+    // FIXME corentin: deduced function types can have "hidden" args and no <
+    // investigate that further, but ultimately maybe we want to model concepts
+    // reference with another kind of expression.
+    return (isConceptReference() || isVarDeclReference())
+               ? getTrailingASTTemplateKWAndArgsInfo()->NumTemplateArgs
+               : getLAngleLoc().isValid();
+  }
+
+  bool isConceptReference() const {
+    return getNumDecls() == 1 && [&]() {
+      if (auto *TTP = dyn_cast_or_null<TemplateTemplateParmDecl>(
+              getTrailingResults()->getDecl()))
+        return TTP->kind() == TNK_Concept_template;
+      if (isa<ConceptDecl>(getTrailingResults()->getDecl()))
+        return true;
+      return false;
+    }();
+  }
+
+  bool isVarDeclReference() const {
+    return getNumDecls() == 1 && [&]() {
+      if (auto *TTP = dyn_cast_or_null<TemplateTemplateParmDecl>(
+              getTrailingResults()->getDecl()))
+        return TTP->kind() == TNK_Var_template;
+      if (isa<VarTemplateDecl>(getTrailingResults()->getDecl()))
+        return true;
+      return false;
+    }();
+  }
+
+  TemplateDecl *getTemplateDecl() const {
+    assert(getNumDecls() == 1);
+    return dyn_cast_or_null<TemplateDecl>(getTrailingResults()->getDecl());
+  }
+
+  TemplateTemplateParmDecl *getTemplateTemplateDecl() const {
+    assert(getNumDecls() == 1);
+    return dyn_cast_or_null<TemplateTemplateParmDecl>(
+        getTrailingResults()->getDecl());
+  }
 
   TemplateArgumentLoc const *getTemplateArgs() const {
     if (!hasExplicitTemplateArgs())
@@ -4658,7 +4701,7 @@ class SubstNonTypeTemplateParmExpr : public Expr {
   // sugared: it doesn't need to be resugared later.
   bool getFinal() const { return Final; }
 
-  NonTypeTemplateParmDecl *getParameter() const;
+  NamedDecl *getParameter() const;
 
   bool isReferenceParameter() const { return AssociatedDeclAndRef.getInt(); }
 
diff --git a/clang/include/clang/AST/ExprConcepts.h b/clang/include/clang/AST/ExprConcepts.h
index 7ab0c3e0b2769..4f162b6ffc8af 100644
--- a/clang/include/clang/AST/ExprConcepts.h
+++ b/clang/include/clang/AST/ExprConcepts.h
@@ -84,7 +84,9 @@ class ConceptSpecializationExpr final : public Expr {
 
   ConceptReference *getConceptReference() const { return ConceptRef; }
 
-  ConceptDecl *getNamedConcept() const { return ConceptRef->getNamedConcept(); }
+  ConceptDecl *getNamedConcept() const {
+    return cast<ConceptDecl>(ConceptRef->getNamedConcept());
+  }
 
   // FIXME: Several of the following functions can be removed. Instead the
   // caller can directly work with the ConceptReference.
diff --git a/clang/include/clang/AST/TemplateBase.h b/clang/include/clang/AST/TemplateBase.h
index b67036cae4261..eb384eae3faa7 100644
--- a/clang/include/clang/AST/TemplateBase.h
+++ b/clang/include/clang/AST/TemplateBase.h
@@ -316,6 +316,8 @@ class TemplateArgument {
   /// Determine whether this template argument is a pack expansion.
   bool isPackExpansion() const;
 
+  bool isConceptOrConceptTemplateParameter() const;
+
   /// Retrieve the type for a type template argument.
   QualType getAsType() const {
     assert(getKind() == Type && "Unexpected kind");
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index 98810fbea726e..12dce309127e5 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -6762,10 +6762,10 @@ class DeducedType : public Type {
 class AutoType : public DeducedType {
   friend class ASTContext; // ASTContext creates these
 
-  ConceptDecl *TypeConstraintConcept;
+  TemplateDecl *TypeConstraintConcept;
 
   AutoType(QualType DeducedAsType, AutoTypeKeyword Keyword,
-           TypeDependence ExtraDependence, QualType Canon, ConceptDecl *CD,
+           TypeDependence ExtraDependence, QualType Canon, TemplateDecl *CD,
            ArrayRef<TemplateArgument> TypeConstraintArgs);
 
 public:
@@ -6774,7 +6774,7 @@ class AutoType : public DeducedType {
             AutoTypeBits.NumArgs};
   }
 
-  ConceptDecl *getTypeConstraintConcept() const {
+  TemplateDecl *getTypeConstraintConcept() const {
     return TypeConstraintConcept;
   }
 
@@ -6797,7 +6797,7 @@ class AutoType : public DeducedType {
   void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context);
   static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
                       QualType Deduced, AutoTypeKeyword Keyword,
-                      bool IsDependent, ConceptDecl *CD,
+                      bool IsDependent, TemplateDecl *CD,
                       ArrayRef<TemplateArgument> Arguments);
 
   static bool classof(const Type *T) {
diff --git a/clang/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h
index be0bc896de3ea..52ef7ac54145e 100644
--- a/clang/include/clang/AST/TypeLoc.h
+++ b/clang/include/clang/AST/TypeLoc.h
@@ -2284,7 +2284,7 @@ class AutoTypeLoc
     return nullptr;
   }
 
-  ConceptDecl *getNamedConcept() const {
+  TemplateDecl *getNamedConcept() const {
     if (const auto *CR = getConceptReference())
       return CR->getNamedConcept();
     return nullptr;
diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td
index 3114d1180319a..3373e963038f1 100644
--- a/clang/include/clang/AST/TypeProperties.td
+++ b/clang/include/clang/AST/TypeProperties.td
@@ -495,9 +495,9 @@ let Class = AutoType in {
   def : Property<"keyword", AutoTypeKeyword> {
     let Read = [{ node->getKeyword() }];
   }
-  def : Property<"typeConstraintConcept", Optional<ConceptDeclRef>> {
+  def : Property<"typeConstraintConcept", Optional<TemplateDeclRef>> {
     let Read = [{ makeOptionalFromPointer(
-        const_cast<const ConceptDecl*>(node->getTypeConstraintConcept())) }];
+        node->getTypeConstraintConcept()) }];
   }
   def : Property<"typeConstraintArguments", Array<TemplateArgument>> {
     let Read = [{ node->getTypeConstraintArguments() }];
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 81cbd38d5cd24..7cfe145120ea4 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5441,8 +5441,10 @@ def err_template_arg_must_be_expr : Error<
   "template argument for non-type template parameter must be an expression">;
 def err_template_arg_nontype_ambig : Error<
   "template argument for non-type template parameter is treated as function type %0">;
-def err_template_arg_must_be_template : Error<
-  "template argument for template template parameter must be a class template%select{| or type alias template}0">;
+def err_template_arg_must_be_template
+    : Error<"template argument for template template parameter must be a "
+            "%select{class template%select{| or type alias template}1|variable "
+            "template|concept}0">;
 def ext_template_arg_local_type : ExtWarn<
   "template argument uses local type %0">, InGroup<LocalTypeTemplateArgs>;
 def ext_template_arg_unnamed_type : ExtWarn<
@@ -5457,11 +5459,14 @@ def note_template_unnamed_type_here : Note<
   "unnamed type used in template argument was declared here">;
 def err_template_arg_overload_type : Error<
   "template argument is the type of an unresolved overloaded function">;
-def err_template_arg_not_valid_template : Error<
-  "template argument does not refer to a class or alias template, or template "
-  "template parameter">;
-def note_template_arg_refers_here_func : Note<
-  "template argument refers to function template %0, here">;
+def err_template_arg_not_valid_template
+    : Error<"template argument does not refer to a %select{class or alias "
+            "template|variable template|concept}0, or template "
+            "template parameter">;
+
+def note_template_arg_refers_to_template_here
+    : Note<"template argument refers to a %select{function template|class "
+           "template|variable template|concept}0 %1, here">;
 def err_template_arg_template_params_mismatch : Error<
   "template template argument has different template parameters than its "
   "corresponding template template parameter">;
diff --git a/clang/include/clang/Sema/Initialization.h b/clang/include/clang/Sema/Initialization.h
index a1c156eed5394..e5ee4b4e48ccc 100644
--- a/clang/include/clang/Sema/Initialization.h
+++ b/clang/include/clang/Sema/Initialization.h
@@ -160,7 +160,7 @@ class alignas(8) InitializedEntity {
 
   struct VD {
     /// The VarDecl, FieldDecl, or BindingDecl being initialized.
-    ValueDecl *VariableOrMember;
+    NamedDecl *VariableOrMember;
 
     /// When Kind == EK_Member, whether this is an implicit member
     /// initialization in a copy or move constructor. These can perform array
@@ -291,8 +291,8 @@ class alignas(8) InitializedEntity {
   }
 
   /// Create the initialization entity for a template parameter.
-  static InitializedEntity
-  InitializeTemplateParameter(QualType T, NonTypeTemplateParmDecl *Param) {
+  static InitializedEntity InitializeTemplateParameter(QualType T,
+                                                       NamedDecl *Param) {
     InitializedEntity Entity;
     Entity.Kind = EK_TemplateParameter;
     Entity.Type = T;
diff --git a/clang/include/clang/Sema/Ownership.h b/clang/include/clang/Sema/Ownership.h
index 0752f5de7e334..b1520837f2f9d 100644
--- a/clang/include/clang/Sema/Ownership.h
+++ b/clang/include/clang/Sema/Ownership.h
@@ -13,6 +13,7 @@
 #ifndef LLVM_CLANG_SEMA_OWNERSHIP_H
 #define LLVM_CLANG_SEMA_OWNERSHIP_H
 
+#include "clang/AST/DeclTemplate.h"
 #include "clang/AST/Expr.h"
 #include "clang/Basic/LLVM.h"
 #include "llvm/ADT/ArrayRef.h"
diff --git a/clang/include/clang/Sema/ParsedTemplate.h b/clang/include/clang/Sema/ParsedTemplate.h
index 3a8050f9a0a3d..6628bb88a81b9 100644
--- a/clang/include/clang/Sema/ParsedTemplate.h
+++ b/clang/include/clang/Sema/ParsedTemplate.h
@@ -14,6 +14,7 @@
 #ifndef LLVM_CLANG_SEMA_PARSEDTEMPLATE_H
 #define LLVM_CLANG_SEMA_PARSEDTEMPLATE_H
 
+#include "clang/AST/DeclTemplate.h"
 #include "clang/Basic/OperatorKinds.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/TemplateKinds.h"
@@ -26,115 +27,116 @@
 #include <new>
 
 namespace clang {
-  /// Represents the parsed form of a C++ template argument.
-  class ParsedTemplateArgument {
-  public:
-    /// Describes the kind of template argument that was parsed.
-    enum KindType {
-      /// A template type parameter, stored as a type.
-      Type,
-      /// A non-type template parameter, stored as an expression.
-      NonType,
-      /// A template template argument, stored as a template name.
-      Template
-    };
-
-    /// Build an empty template argument.
-    ///
-    /// This template argument is invalid.
-    ParsedTemplateArgument() : Kind(Type), Arg(nullptr) { }
-
-    /// Create a template type argument or non-type template argument.
-    ///
-    /// \param Arg the template type argument or non-type template argument.
-    /// \param Loc the location of the type.
-    ParsedTemplateArgument(KindType Kind, void *Arg, SourceLocation Loc)
-      : Kind(Kind), Arg(Arg), Loc(Loc) { }
-
-    /// Create a template template argument.
-    ///
-    /// \param SS the C++ scope specifier that precedes the template name, if
-    /// any.
-    ///
-    /// \param Template the template to which this template template
-    /// argument refers.
-    ///
-    /// \param TemplateLoc the location of the template name.
-    ParsedTemplateArgument(const CXXScopeSpec &SS,
-                           ParsedTemplateTy Template,
-                           SourceLocation TemplateLoc)
-      : Kind(ParsedTemplateArgument::Template),
-        Arg(Template.getAsOpaquePtr()), SS(SS), Loc(TemplateLoc) {}
-
-    /// Determine whether the given template argument is invalid.
-    bool isInvalid() const { return Arg == nullptr; }
-
-    /// Determine what kind of template argument we have.
-    KindType getKind() const { return Kind; }
-
-    /// Retrieve the template type argument's type.
-    ParsedType getAsType() const {
-      assert(Kind == Type && "Not a template type argument");
-      return ParsedType::getFromOpaquePtr(Arg);
-    }
-
-    /// Retrieve the non-type template argument's expression.
-    Expr *getAsExpr() const {
-      assert(Kind == NonType && "Not a non-type template argument");
-      return static_cast<Expr*>(Arg);
-    }
-
-    /// Retrieve the template template argument's template name.
-    ParsedTemplateTy getAsTemplate() const {
-      assert(Kind == Template && "Not a template template argument");
-      return ParsedTemplateTy::getFromOpaquePtr(Arg);
-    }
-
-    /// Retrieve the location of the template argument.
-    SourceLocation getLocation() const { return Loc; }
-
-    /// Retrieve the nested-name-specifier that precedes the template
-    /// name in a template template argument.
-    const CXXScopeSpec &getScopeSpec() const {
-      assert(Kind == Template &&
-             "Only template template arguments can have a scope specifier");
-      return SS;
-    }
-
-    /// Retrieve the location of the ellipsis that makes a template
-    /// template argument into a pack expansion.
-    SourceLocation getEllipsisLoc() const {
-      assert(Kind == Template &&
-             "Only template template arguments can have an ellipsis");
-      return EllipsisLoc;
-    }
-
-    /// Retrieve a pack expansion of the given template template
-    /// argument.
-    ///
-    /// \param EllipsisLoc The location of the ellipsis.
-    ParsedTemplateArgument getTemplatePackExpansion(
-                                              SourceLocation EllipsisLoc) const;
 
-  private:
-    KindType Kind;
-
-    /// The actual template argument representation, which may be
-    /// an \c Sema::TypeTy* (for a type), an Expr* (for an
-    /// expression), or an Sema::TemplateTy (for a template).
-    void *Arg;
+/// Represents the parsed form of a C++ template argument.
+class ParsedTemplateArgument {
+public:
+  /// Describes the kind of template argument that was parsed.
+  enum KindType {
+    /// A template type parameter, stored as a type.
+    Type,
+    /// A non-type template parameter, stored as an expression.
+    NonType,
+    /// A template template argument, stored as a template name.
+    Template,
+  };
 
-    /// The nested-name-specifier that can accompany a template template
-    /// argument.
-    CXXScopeSpec SS;
+  /// Build an empty template argument.
+  ///
+  /// This template argument is invalid.
+  ParsedTemplateArgument() : Kind(Type), Arg(nullptr) {}
 
-    /// the location of the template argument.
-    SourceLocation Loc;
+  /// Create a template type argument or non-type template argument.
+  ///
+  /// \param Arg the template type argument or non-type template argument.
+  /// \param Loc the location of the type.
+  ParsedTemplateArgument(KindType Kind, void *Arg, SourceLocation Loc)
+      : Kind(Kind), Arg(Arg), Loc(Loc) {}
 
-    /// The ellipsis location that can accompany a template template
-    /// argument (turning it into a template template argument expansion).
-    SourceLocation EllipsisLoc;
-  };
+  /// Create a template template argument.
+  ///
+  /// \param SS the C++ scope specifier that precedes the template name, if
+  /// any.
+  ///
+  /// \param Template the template to which this template template
+  /// argument refers.
+  ///
+  /// \param TemplateLoc the location of the template name.
+  ParsedTemplateArgument(const CXXScopeSpec &SS, ParsedTemplateTy Template,
+                         SourceLocation TemplateLoc)
+      : Kind(ParsedTemplateArgument::Template), Arg(Template.getAsOpaquePtr()),
+        SS(SS), Loc(TemplateLoc) {}
+
+  /// Determine whether the given template argument is invalid.
+  bool isInvalid() const { return Arg == nullptr; }
+
+  /// Determine what kind of template argument we have.
+  KindType getKind() const { return Kind; }
+
+  /// Retrieve the template type argument's type.
+  ParsedType getAsType() const {
+    assert(Kind == Type && "Not a template type argument");
+    return ParsedType::getFromOpaquePtr(Arg);
+  }
+
+  /// Retrieve the non-type template argument's expression.
+  Expr *getAsExpr() const {
+    assert(Kind == NonType && "Not a non-type template argument");
+    return static_cast<Expr *>(Arg);
+  }
+
+  /// Retrieve the template template argument's template name.
+  ParsedTemplateTy getAsTemplate() const {
+    assert(Kind == Template && "Not a template template argument");
+    return ParsedTemplateTy::getFromOpaquePtr(Arg);
+  }
+
+  /// Retrieve the location of the template argument.
+  SourceLocation getLocation() const { return Loc; }
+
+  /// Retrieve the nested-name-specifier that precedes the template
+  /// name in a template template argument.
+  const CXXScopeSpec &getScopeSpec() const {
+    assert((Kind == Template) &&
+           "Only template template arguments can have a scope specifier");
+    return SS;
+  }
+
+  /// Retrieve the location of the ellipsis that makes a template
+  /// template argument into a pack expansion.
+  SourceLocation getEllipsisLoc() const {
+    assert((Kind == Template) &&
+           "Only template arguments can have an ellipsis");
+    return EllipsisLoc;
+  }
+
+  /// Retrieve a pack expansion of the given template template
+  /// argument.
+  ///
+  /// \param EllipsisLoc The location of the ellipsis.
+  ParsedTemplateArgument
+  getTemplatePackExpansion(SourceLocation EllipsisLoc) const;
+
+private:
+  KindType Kind;
+
+  /// The actual template argument representation, which may be
+  /// an \c Sema::TypeTy* (for a type), an Expr* (for an
+  /// expression), or an Sema::TemplateTy (for a template).
+  void *Arg;
+
+  /// The nested-name-specifier that can accompany a template template
+  /// argument.
+  CXXScopeSpec SS;
+
+  /// the location of the template argument.
+  SourceLocation Loc;
+
+  /// The ellipsis location that can accompany a template/universal template
+  /// argument (turning it into a template/universal template argument
+  /// expansion).
+  SourceLocation EllipsisLoc;
+};
 
   /// Information about a template-id annotation
   /// token.
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 73eb730ca555b..7853c9d649f77 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -11444,7 +11444,7 @@ class Sema final : public SemaBase {
   /// of arguments for the named concept).
   bool AttachTypeConstraint(NestedNameSpecifierLoc NS,
                             DeclarationNameInfo NameInfo,
-                            ConceptDecl *NamedConcept, NamedDecl *FoundDecl,
+                            TemplateDecl *NamedConcept, NamedDecl *FoundDecl,
                             const TemplateArgumentListInfo *TemplateArgs,
                             TemplateTypeParmDecl *ConstrainedParameter,
                             SourceLocation EllipsisLoc);
@@ -11477,8 +11477,9 @@ class Sema final : public SemaBase {
   /// parameter (e.g. T in template <template \<typename> class T> class array)
   /// has been parsed. S is the current scope.
   NamedDecl *ActOnTemplateTemplateParameter(
-      Scope *S, SourceLocation TmpLoc, TemplateParameterList *Params,
-      bool Typename, SourceLocation EllipsisLoc, IdentifierInfo *ParamName,
+      Scope *S, SourceLocation TmpLoc, TemplateNameKind Kind,
+      bool TypenameKeyword, TemplateParameterList *Params,
+      SourceLocation EllipsisLoc, IdentifierInfo *ParamName,
       SourceLocation ParamNameLoc, unsigned Depth, unsigned Position,
       SourceLocation EqualLoc, ParsedTemplateArgument DefaultArg);
 
@@ -11646,6 +11647,11 @@ class Sema final : public SemaBase {
                                 SourceLocation TemplateLoc,
                                 const TemplateArgumentListInfo *TemplateArgs);
 
+  ExprResult CheckVarOrConceptTemplateTemplateId(
+      const CXXScopeSpec &SS, const DeclarationNameInfo &NameInfo,
+      TemplateTemplateParmDecl *Template, SourceLocation TemplateLoc,
+      const TemplateArgumentListInfo *TemplateArgs);
+
   ExprResult
   CheckConceptTemplateId(const CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
                          const DeclarationNameInfo &ConceptNameInfo,
@@ -11982,7 +11988,7 @@ class Sema final : public SemaBase {
   /// If an error occurred, it returns ExprError(); otherwise, it
   /// returns the converted template argument. \p ParamType is the
   /// type of the non-type template parameter after it has been instantiated.
-  ExprResult CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
+  ExprResult CheckTemplateArgument(NamedDecl *Param,
                                    QualType InstantiatedParamType, Expr *Arg,
                                    TemplateArgument &SugaredConverted,
                                    TemplateArgument &CanonicalConverted,
@@ -12000,6 +12006,10 @@ class Sema final : public SemaBase {
                                      bool PartialOrdering,
                                      bool *StrictPackMatch);
 
+  bool CheckDeclCompatibleWithTemplateTemplate(TemplateDecl *Template,
+                                               TemplateTemplateParmDecl *Param,
+                                               const TemplateArgumentLoc &Arg);
+
   void NoteTemplateLocation(const NamedDecl &Decl,
                             std::optional<SourceRange> ParamRange = {});
   void NoteTemplateParameterLocation(const NamedDecl &Decl);
@@ -12276,7 +12286,7 @@ class Sema final : public SemaBase {
 
   void CheckConceptRedefinition(ConceptDecl *NewDecl, LookupResult &Previous,
                                 bool &AddToScope);
-  bool CheckConceptUseInDefinition(ConceptDecl *Concept, SourceLocation Loc);
+  bool CheckConceptUseInDefinition(NamedDecl *Concept, SourceLocation Loc);
 
   TypeResult ActOnDependentTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
                                const CXXScopeSpec &SS,
diff --git a/clang/include/clang/Sema/SemaInternal.h b/clang/include/clang/Sema/SemaInternal.h
index 4d0da1102bb59..42c9469e44e53 100644
--- a/clang/include/clang/Sema/SemaInternal.h
+++ b/clang/include/clang/Sema/SemaInternal.h
@@ -15,6 +15,7 @@
 #define LLVM_CLANG_SEMA_SEMAINTERNAL_H
 
 #include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclTemplate.h"
 #include "clang/Sema/Lookup.h"
 #include "clang/Sema/Sema.h"
 #include "clang/Sema/SemaDiagnostic.h"
diff --git a/clang/lib/AST/ASTConcept.cpp b/clang/lib/AST/ASTConcept.cpp
index 91ab66f4639fc..2243ac035115f 100644
--- a/clang/lib/AST/ASTConcept.cpp
+++ b/clang/lib/AST/ASTConcept.cpp
@@ -86,7 +86,7 @@ ConceptReference *
 ConceptReference::Create(const ASTContext &C, NestedNameSpecifierLoc NNS,
                          SourceLocation TemplateKWLoc,
                          DeclarationNameInfo ConceptNameInfo,
-                         NamedDecl *FoundDecl, ConceptDecl *NamedConcept,
+                         NamedDecl *FoundDecl, TemplateDecl *NamedConcept,
                          const ASTTemplateArgumentListInfo *ArgsAsWritten) {
   return new (C) ConceptReference(NNS, TemplateKWLoc, ConceptNameInfo,
                                   FoundDecl, NamedConcept, ArgsAsWritten);
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 16cf114981824..6e6572d5cd31a 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -80,6 +80,7 @@
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Frontend/OpenMP/OMPIRBuilder.h"
 #include "llvm/Support/Capacity.h"
+#include "llvm/Support/Casting.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/MD5.h"
@@ -717,13 +718,13 @@ comments::FullComment *ASTContext::getCommentForDecl(
   return FC;
 }
 
-void
-ASTContext::CanonicalTemplateTemplateParm::Profile(llvm::FoldingSetNodeID &ID,
-                                                   const ASTContext &C,
-                                               TemplateTemplateParmDecl *Parm) {
+void ASTContext::CanonicalTemplateTemplateParm::Profile(
+    llvm::FoldingSetNodeID &ID, const ASTContext &C,
+    TemplateTemplateParmDecl *Parm) {
   ID.AddInteger(Parm->getDepth());
   ID.AddInteger(Parm->getPosition());
   ID.AddBoolean(Parm->isParameterPack());
+  ID.AddInteger(Parm->kind());
 
   TemplateParameterList *Params = Parm->getTemplateParameters();
   ID.AddInteger(Params->size());
@@ -829,7 +830,8 @@ ASTContext::getCanonicalTemplateTemplateParmDecl(
 
   TemplateTemplateParmDecl *CanonTTP = TemplateTemplateParmDecl::Create(
       *this, getTranslationUnitDecl(), SourceLocation(), TTP->getDepth(),
-      TTP->getPosition(), TTP->isParameterPack(), nullptr, /*Typename=*/false,
+      TTP->getPosition(), TTP->isParameterPack(), nullptr, TTP->kind(),
+      /*Typename=*/false,
       TemplateParameterList::Create(*this, SourceLocation(), SourceLocation(),
                                     CanonParams, SourceLocation(),
                                     /*RequiresClause=*/nullptr));
@@ -6643,7 +6645,7 @@ ASTContext::getUnaryTransformType(QualType BaseType, QualType UnderlyingType,
 
 QualType ASTContext::getAutoTypeInternal(
     QualType DeducedType, AutoTypeKeyword Keyword, bool IsDependent,
-    bool IsPack, ConceptDecl *TypeConstraintConcept,
+    bool IsPack, TemplateDecl *TypeConstraintConcept,
     ArrayRef<TemplateArgument> TypeConstraintArgs, bool IsCanon) const {
   if (DeducedType.isNull() && Keyword == AutoTypeKeyword::Auto &&
       !TypeConstraintConcept && !IsDependent)
@@ -6652,7 +6654,8 @@ QualType ASTContext::getAutoTypeInternal(
   // Look in the folding set for an existing type.
   llvm::FoldingSetNodeID ID;
   bool IsDeducedDependent =
-      !DeducedType.isNull() && DeducedType->isDependentType();
+      isa_and_nonnull<TemplateTemplateParmDecl>(TypeConstraintConcept) ||
+      (!DeducedType.isNull() && DeducedType->isDependentType());
   AutoType::Profile(ID, *this, DeducedType, Keyword,
                     IsDependent || IsDeducedDependent, TypeConstraintConcept,
                     TypeConstraintArgs);
@@ -6665,7 +6668,8 @@ QualType ASTContext::getAutoTypeInternal(
       Canon = DeducedType.getCanonicalType();
     } else if (TypeConstraintConcept) {
       bool AnyNonCanonArgs = false;
-      ConceptDecl *CanonicalConcept = TypeConstraintConcept->getCanonicalDecl();
+      TemplateDecl *CanonicalConcept =
+          cast<TemplateDecl>(TypeConstraintConcept->getCanonicalDecl());
       auto CanonicalConceptArgs = ::getCanonicalTemplateArguments(
           *this, TypeConstraintArgs, AnyNonCanonArgs);
       if (CanonicalConcept != TypeConstraintConcept || AnyNonCanonArgs) {
@@ -6701,7 +6705,7 @@ QualType ASTContext::getAutoTypeInternal(
 QualType
 ASTContext::getAutoType(QualType DeducedType, AutoTypeKeyword Keyword,
                         bool IsDependent, bool IsPack,
-                        ConceptDecl *TypeConstraintConcept,
+                        TemplateDecl *TypeConstraintConcept,
                         ArrayRef<TemplateArgument> TypeConstraintArgs) const {
   assert((!IsPack || IsDependent) && "only use IsPack for a dependent pack");
   assert((!IsDependent || DeducedType.isNull()) &&
@@ -14387,8 +14391,8 @@ static QualType getCommonSugarTypeNode(ASTContext &Ctx, const Type *X,
     if (KW != AY->getKeyword())
       return QualType();
 
-    ConceptDecl *CD = ::getCommonDecl(AX->getTypeConstraintConcept(),
-                                      AY->getTypeConstraintConcept());
+    TemplateDecl *CD = ::getCommonDecl(AX->getTypeConstraintConcept(),
+                                       AY->getTypeConstraintConcept());
     SmallVector<TemplateArgument, 8> As;
     if (CD &&
         getCommonTemplateArguments(Ctx, As, AX->getTypeConstraintArguments(),
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index b9bdabe0b8c06..e8cc5b8cda72a 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -6268,8 +6268,8 @@ ASTNodeImporter::VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D) {
           ToD, D, Importer.getToContext(),
           Importer.getToContext().getTranslationUnitDecl(), *LocationOrErr,
           D->getDepth(), D->getPosition(), D->isParameterPack(),
-          (*NameOrErr).getAsIdentifierInfo(), D->wasDeclaredWithTypename(),
-          *TemplateParamsOrErr))
+          (*NameOrErr).getAsIdentifierInfo(), D->kind(),
+          D->wasDeclaredWithTypename(), *TemplateParamsOrErr))
     return ToD;
 
   if (Error Err = importTemplateParameterDefaultArgument(D, ToD))
diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp
index 0f2762d5c0f14..b3b9e47b2787f 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -2249,7 +2249,8 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
   }
 
   // Check template parameter lists.
-  return IsStructurallyEquivalent(Context, D1->getTemplateParameters(),
+  return D1->kind() == D2->kind() &&
+         IsStructurallyEquivalent(Context, D1->getTemplateParameters(),
                                   D2->getTemplateParameters());
 }
 
diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp
index 14ec93eb1d166..87334d98413e6 100644
--- a/clang/lib/AST/ComputeDependence.cpp
+++ b/clang/lib/AST/ComputeDependence.cpp
@@ -806,7 +806,7 @@ clang::computeDependence(OverloadExpr *E, bool KnownDependent,
                              ~NestedNameSpecifierDependence::Dependent);
   for (auto *D : E->decls()) {
     if (D->getDeclContext()->isDependentContext() ||
-        isa<UnresolvedUsingValueDecl>(D))
+        isa<UnresolvedUsingValueDecl>(D) || isa<TemplateTemplateParmDecl>(D))
       Deps |= ExprDependence::TypeValueInstantiation;
   }
   // If we have explicit template arguments, check for dependent
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 83fcd87aec2f8..d8899e9e1f725 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -1233,6 +1233,9 @@ getExplicitVisibilityAux(const NamedDecl *ND,
                          bool IsMostRecent) {
   assert(!IsMostRecent || ND == ND->getMostRecentDecl());
 
+  if (isa<ConceptDecl>(ND))
+    return {};
+
   // Check the declaration itself first.
   if (std::optional<Visibility> V = getVisibilityOf(ND, kind))
     return V;
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index 5035f2d33b0a1..70b12c9d3bb64 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -162,6 +162,7 @@ void TemplateParameterList::Profile(llvm::FoldingSetNodeID &ID,
     }
     const auto *TTP = cast<TemplateTemplateParmDecl>(D);
     ID.AddInteger(2);
+    ID.AddInteger(TTP->kind());
     ID.AddBoolean(TTP->isParameterPack());
     TTP->getTemplateParameters()->Profile(ID, C);
   }
@@ -184,7 +185,8 @@ unsigned TemplateParameterList::getMinRequiredArguments() const {
     } else if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(P)) {
       if (NTTP->hasDefaultArgument())
         break;
-    } else if (cast<TemplateTemplateParmDecl>(P)->hasDefaultArgument())
+    } else if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(P);
+               TTP && TTP->hasDefaultArgument())
       break;
 
     ++NumRequiredArgs;
@@ -235,7 +237,7 @@ void TemplateParameterList::getAssociatedConstraints(
           ACs.emplace_back(E);
       }
     }
-  if (HasRequiresClause)
+  if (HasRequiresClause && getRequiresClause())
     ACs.emplace_back(getRequiresClause());
 }
 
@@ -873,38 +875,40 @@ void TemplateTemplateParmDecl::anchor() {}
 
 TemplateTemplateParmDecl::TemplateTemplateParmDecl(
     DeclContext *DC, SourceLocation L, unsigned D, unsigned P,
-    IdentifierInfo *Id, bool Typename, TemplateParameterList *Params,
-    ArrayRef<TemplateParameterList *> Expansions)
+    IdentifierInfo *Id, TemplateNameKind Kind, bool Typename,
+    TemplateParameterList *Params, ArrayRef<TemplateParameterList *> Expansions)
     : TemplateDecl(TemplateTemplateParm, DC, L, Id, Params),
-      TemplateParmPosition(D, P), Typename(Typename), ParameterPack(true),
-      ExpandedParameterPack(true), NumExpandedParams(Expansions.size()) {
+      TemplateParmPosition(D, P), ParameterKind(Kind), Typename(Typename),
+      ParameterPack(true), ExpandedParameterPack(true),
+      NumExpandedParams(Expansions.size()) {
   llvm::uninitialized_copy(Expansions, getTrailingObjects());
 }
 
-TemplateTemplateParmDecl *
-TemplateTemplateParmDecl::Create(const ASTContext &C, DeclContext *DC,
-                                 SourceLocation L, unsigned D, unsigned P,
-                                 bool ParameterPack, IdentifierInfo *Id,
-                                 bool Typename, TemplateParameterList *Params) {
+TemplateTemplateParmDecl *TemplateTemplateParmDecl::Create(
+    const ASTContext &C, DeclContext *DC, SourceLocation L, unsigned D,
+    unsigned P, bool ParameterPack, IdentifierInfo *Id, TemplateNameKind Kind,
+    bool Typename, TemplateParameterList *Params) {
   return new (C, DC) TemplateTemplateParmDecl(DC, L, D, P, ParameterPack, Id,
-                                              Typename, Params);
+                                              Kind, Typename, Params);
 }
 
 TemplateTemplateParmDecl *
 TemplateTemplateParmDecl::Create(const ASTContext &C, DeclContext *DC,
                                  SourceLocation L, unsigned D, unsigned P,
-                                 IdentifierInfo *Id, bool Typename,
-                                 TemplateParameterList *Params,
+                                 IdentifierInfo *Id, TemplateNameKind Kind,
+                                 bool Typename, TemplateParameterList *Params,
                                  ArrayRef<TemplateParameterList *> Expansions) {
   return new (C, DC,
               additionalSizeToAlloc<TemplateParameterList *>(Expansions.size()))
-      TemplateTemplateParmDecl(DC, L, D, P, Id, Typename, Params, Expansions);
+      TemplateTemplateParmDecl(DC, L, D, P, Id, Kind, Typename, Params,
+                               Expansions);
 }
 
 TemplateTemplateParmDecl *
 TemplateTemplateParmDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) {
-  return new (C, ID) TemplateTemplateParmDecl(nullptr, SourceLocation(), 0, 0,
-                                              false, nullptr, false, nullptr);
+  return new (C, ID) TemplateTemplateParmDecl(
+      nullptr, SourceLocation(), 0, 0, false, nullptr,
+      TemplateNameKind::TNK_Type_template, false, nullptr);
 }
 
 TemplateTemplateParmDecl *
@@ -913,7 +917,8 @@ TemplateTemplateParmDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID,
   auto *TTP =
       new (C, ID, additionalSizeToAlloc<TemplateParameterList *>(NumExpansions))
           TemplateTemplateParmDecl(nullptr, SourceLocation(), 0, 0, nullptr,
-                                   false, nullptr, {});
+                                   TemplateNameKind::TNK_Type_template, false,
+                                   nullptr, {});
   TTP->NumExpandedParams = NumExpansions;
   return TTP;
 }
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index 5833a6405125d..ac55f23bbea07 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -396,6 +396,16 @@ SourceLocation CXXPseudoDestructorExpr::getEndLoc() const {
   return End;
 }
 
+static bool UnresolvedLookupExprIsVariableOrConceptParameterPack(
+    UnresolvedSetIterator Begin, UnresolvedSetIterator End) {
+  if (std::distance(Begin, End) != 1)
+    return false;
+  NamedDecl *ND = *Begin;
+  if (auto *TTP = llvm::dyn_cast<TemplateTemplateParmDecl>(ND))
+    return TTP->isParameterPack();
+  return false;
+}
+
 // UnresolvedLookupExpr
 UnresolvedLookupExpr::UnresolvedLookupExpr(
     const ASTContext &Context, CXXRecordDecl *NamingClass,
@@ -404,9 +414,11 @@ UnresolvedLookupExpr::UnresolvedLookupExpr(
     const TemplateArgumentListInfo *TemplateArgs, UnresolvedSetIterator Begin,
     UnresolvedSetIterator End, bool KnownDependent,
     bool KnownInstantiationDependent)
-    : OverloadExpr(UnresolvedLookupExprClass, Context, QualifierLoc,
-                   TemplateKWLoc, NameInfo, TemplateArgs, Begin, End,
-                   KnownDependent, KnownInstantiationDependent, false),
+    : OverloadExpr(
+          UnresolvedLookupExprClass, Context, QualifierLoc, TemplateKWLoc,
+          NameInfo, TemplateArgs, Begin, End, KnownDependent,
+          KnownInstantiationDependent,
+          UnresolvedLookupExprIsVariableOrConceptParameterPack(Begin, End)),
       NamingClass(NamingClass) {
   UnresolvedLookupExprBits.RequiresADL = RequiresADL;
 }
@@ -1714,8 +1726,8 @@ SizeOfPackExpr *SizeOfPackExpr::CreateDeserialized(ASTContext &Context,
   return new (Storage) SizeOfPackExpr(EmptyShell(), NumPartialArgs);
 }
 
-NonTypeTemplateParmDecl *SubstNonTypeTemplateParmExpr::getParameter() const {
-  return cast<NonTypeTemplateParmDecl>(
+NamedDecl *SubstNonTypeTemplateParmExpr::getParameter() const {
+  return cast<NamedDecl>(
       getReplacedTemplateParameterList(getAssociatedDecl())->asArray()[Index]);
 }
 
@@ -1758,9 +1770,12 @@ QualType SubstNonTypeTemplateParmExpr::getParameterType(
     const ASTContext &Context) const {
   // Note that, for a class type NTTP, we will have an lvalue of type 'const
   // T', so we can't just compute this from the type and value category.
+
+  QualType Type = getType();
+
   if (isReferenceParameter())
-    return Context.getLValueReferenceType(getType());
-  return getType().getUnqualifiedType();
+    return Context.getLValueReferenceType(Type);
+  return Type.getUnqualifiedType();
 }
 
 SubstNonTypeTemplateParmPackExpr::SubstNonTypeTemplateParmPackExpr(
@@ -1997,9 +2012,10 @@ CXXFoldExpr::CXXFoldExpr(QualType T, UnresolvedLookupExpr *Callee,
       NumExpansions(NumExpansions) {
   CXXFoldExprBits.Opcode = Opcode;
   // We rely on asserted invariant to distinguish left and right folds.
-  assert(((LHS && LHS->containsUnexpandedParameterPack()) !=
-          (RHS && RHS->containsUnexpandedParameterPack())) &&
-         "Exactly one of LHS or RHS should contain an unexpanded pack");
+  if (LHS && RHS)
+    assert(LHS->containsUnexpandedParameterPack() !=
+               RHS->containsUnexpandedParameterPack() &&
+           "Exactly one of LHS or RHS should contain an unexpanded pack");
   SubExprs[SubExpr::Callee] = Callee;
   SubExprs[SubExpr::LHS] = LHS;
   SubExprs[SubExpr::RHS] = RHS;
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 2a667934dba42..5233648d8f9c8 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -529,7 +529,7 @@ class CXXNameMangler {
   void mangleUnqualifiedBlock(const BlockDecl *Block);
   void mangleTemplateParamDecl(const NamedDecl *Decl);
   void mangleTemplateParameterList(const TemplateParameterList *Params);
-  void mangleTypeConstraint(const ConceptDecl *Concept,
+  void mangleTypeConstraint(const TemplateDecl *Concept,
                             ArrayRef<TemplateArgument> Arguments);
   void mangleTypeConstraint(const TypeConstraint *Constraint);
   void mangleRequiresClause(const Expr *RequiresClause);
@@ -2080,7 +2080,7 @@ void CXXNameMangler::mangleTemplateParameterList(
 }
 
 void CXXNameMangler::mangleTypeConstraint(
-    const ConceptDecl *Concept, ArrayRef<TemplateArgument> Arguments) {
+    const TemplateDecl *Concept, ArrayRef<TemplateArgument> Arguments) {
   const DeclContext *DC = Context.getEffectiveDeclContext(Concept);
   if (!Arguments.empty())
     mangleTemplateName(Concept, Arguments);
diff --git a/clang/lib/AST/TemplateBase.cpp b/clang/lib/AST/TemplateBase.cpp
index 7c89dea4629cc..4207fea3764ab 100644
--- a/clang/lib/AST/TemplateBase.cpp
+++ b/clang/lib/AST/TemplateBase.cpp
@@ -339,6 +339,18 @@ bool TemplateArgument::isPackExpansion() const {
   llvm_unreachable("Invalid TemplateArgument Kind!");
 }
 
+bool TemplateArgument::isConceptOrConceptTemplateParameter() const {
+  bool isConcept = false;
+  if (getKind() == TemplateArgument::Template) {
+    if (isa<ConceptDecl>(getAsTemplate().getAsTemplateDecl()))
+      isConcept = true;
+    else if (auto *TTP = dyn_cast_if_present<TemplateTemplateParmDecl>(
+                 getAsTemplate().getAsTemplateDecl()))
+      isConcept = TTP->kind() == TNK_Concept_template;
+  }
+  return isConcept;
+}
+
 bool TemplateArgument::containsUnexpandedParameterPack() const {
   return getDependence() & TemplateArgumentDependence::UnexpandedPack;
 }
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 7444a2f90c5dd..141edc881d683 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -5338,7 +5338,7 @@ void clang::FixedPointValueToString(SmallVectorImpl<char> &Str,
 
 AutoType::AutoType(QualType DeducedAsType, AutoTypeKeyword Keyword,
                    TypeDependence ExtraDependence, QualType Canon,
-                   ConceptDecl *TypeConstraintConcept,
+                   TemplateDecl *TypeConstraintConcept,
                    ArrayRef<TemplateArgument> TypeConstraintArgs)
     : DeducedType(Auto, DeducedAsType, ExtraDependence, Canon) {
   AutoTypeBits.Keyword = llvm::to_underlying(Keyword);
@@ -5346,6 +5346,9 @@ AutoType::AutoType(QualType DeducedAsType, AutoTypeKeyword Keyword,
   this->TypeConstraintConcept = TypeConstraintConcept;
   assert(TypeConstraintConcept || AutoTypeBits.NumArgs == 0);
   if (TypeConstraintConcept) {
+    if (isa<TemplateTemplateParmDecl>(TypeConstraintConcept))
+      addDependence(TypeDependence::DependentInstantiation);
+
     auto *ArgBuffer =
         const_cast<TemplateArgument *>(getTypeConstraintArguments().data());
     for (const TemplateArgument &Arg : TypeConstraintArgs) {
@@ -5361,7 +5364,7 @@ AutoType::AutoType(QualType DeducedAsType, AutoTypeKeyword Keyword,
 
 void AutoType::Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
                        QualType Deduced, AutoTypeKeyword Keyword,
-                       bool IsDependent, ConceptDecl *CD,
+                       bool IsDependent, TemplateDecl *CD,
                        ArrayRef<TemplateArgument> Arguments) {
   ID.AddPointer(Deduced.getAsOpaquePtr());
   ID.AddInteger((unsigned)Keyword);
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index e47caeb855d0c..5fb3f92b2e7ce 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -3310,6 +3310,7 @@ Parser::DiagnoseMissingSemiAfterTagDefinition(DeclSpec &DS, AccessSpecifier AS,
       case NameClassificationKind::TypeTemplate:
       case NameClassificationKind::UndeclaredNonType:
       case NameClassificationKind::UndeclaredTemplate:
+      case NameClassificationKind::Concept:
         // Not a previously-declared non-type entity.
         MightBeDeclarator = false;
         break;
@@ -3320,7 +3321,6 @@ Parser::DiagnoseMissingSemiAfterTagDefinition(DeclSpec &DS, AccessSpecifier AS,
       case NameClassificationKind::OverloadSet:
       case NameClassificationKind::VarTemplate:
       case NameClassificationKind::FunctionTemplate:
-      case NameClassificationKind::Concept:
         // Might be a redeclaration of a prior entity.
         break;
       }
diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp
index 9a04bf298c0ac..f08361ced6379 100644
--- a/clang/lib/Parse/ParseTemplate.cpp
+++ b/clang/lib/Parse/ParseTemplate.cpp
@@ -711,41 +711,59 @@ NamedDecl *Parser::ParseTemplateTemplateParameter(unsigned Depth,
     }
   }
 
-  // Provide an ExtWarn if the C++1z feature of using 'typename' here is used.
-  // Generate a meaningful error if the user forgot to put class before the
-  // identifier, comma, or greater. Provide a fixit if the identifier, comma,
-  // or greater appear immediately or after 'struct'. In the latter case,
-  // replace the keyword with 'class'.
+  TemplateNameKind Kind = TemplateNameKind::TNK_Non_template;
+  SourceLocation NameLoc;
+  IdentifierInfo *ParamName = nullptr;
+  SourceLocation EllipsisLoc;
   bool TypenameKeyword = false;
-  if (!TryConsumeToken(tok::kw_class)) {
+
+  if (TryConsumeToken(tok::kw_class)) {
+    Kind = TemplateNameKind::TNK_Type_template;
+  } else {
+
+    // Provide an ExtWarn if the C++1z feature of using 'typename' here is used.
+    // Generate a meaningful error if the user forgot to put class before the
+    // identifier, comma, or greater. Provide a fixit if the identifier, comma,
+    // or greater appear immediately or after 'struct'. In the latter case,
+    // replace the keyword with 'class'.
     bool Replace = Tok.isOneOf(tok::kw_typename, tok::kw_struct);
     const Token &Next = Tok.is(tok::kw_struct) ? NextToken() : Tok;
     if (Tok.is(tok::kw_typename)) {
       TypenameKeyword = true;
+      Kind = TemplateNameKind::TNK_Type_template;
       Diag(Tok.getLocation(),
            getLangOpts().CPlusPlus17
                ? diag::warn_cxx14_compat_template_template_param_typename
                : diag::ext_template_template_param_typename)
-        << (!getLangOpts().CPlusPlus17
-                ? FixItHint::CreateReplacement(Tok.getLocation(), "class")
-                : FixItHint());
+          << (!getLangOpts().CPlusPlus17
+                  ? FixItHint::CreateReplacement(Tok.getLocation(), "class")
+                  : FixItHint());
+      Kind = TemplateNameKind::TNK_Type_template;
+    } else if (TryConsumeToken(tok::kw_concept)) {
+      Kind = TemplateNameKind::TNK_Concept_template;
+    } else if (TryConsumeToken(tok::kw_auto)) {
+      Kind = TemplateNameKind::TNK_Var_template;
     } else if (Next.isOneOf(tok::identifier, tok::comma, tok::greater,
                             tok::greatergreater, tok::ellipsis)) {
+      // Provide a fixit if the identifier, comma,
+      // or greater appear immediately or after 'struct'. In the latter case,
+      // replace the keyword with 'class'.
       Diag(Tok.getLocation(), diag::err_class_on_template_template_param)
           << getLangOpts().CPlusPlus17
           << (Replace
                   ? FixItHint::CreateReplacement(Tok.getLocation(), "class")
                   : FixItHint::CreateInsertion(Tok.getLocation(), "class "));
-    } else
-      Diag(Tok.getLocation(), diag::err_class_on_template_template_param)
-          << getLangOpts().CPlusPlus17;
-
+    }
     if (Replace)
       ConsumeToken();
   }
 
+  if (!getLangOpts().CPlusPlus23 &&
+      Kind != TemplateNameKind::TNK_Type_template) {
+    // Diag older language mods
+  }
+
   // Parse the ellipsis, if given.
-  SourceLocation EllipsisLoc;
   if (TryConsumeToken(tok::ellipsis, EllipsisLoc))
     Diag(EllipsisLoc,
          getLangOpts().CPlusPlus11
@@ -753,8 +771,7 @@ NamedDecl *Parser::ParseTemplateTemplateParameter(unsigned Depth,
            : diag::ext_variadic_templates);
 
   // Get the identifier, if given.
-  SourceLocation NameLoc = Tok.getLocation();
-  IdentifierInfo *ParamName = nullptr;
+  NameLoc = Tok.getLocation();
   if (Tok.is(tok::identifier)) {
     ParamName = Tok.getIdentifierInfo();
     ConsumeToken();
@@ -792,7 +809,7 @@ NamedDecl *Parser::ParseTemplateTemplateParameter(unsigned Depth,
   }
 
   return Actions.ActOnTemplateTemplateParameter(
-      getCurScope(), TemplateLoc, ParamList, TypenameKeyword, EllipsisLoc,
+      getCurScope(), TemplateLoc, Kind, TypenameKeyword, ParamList, EllipsisLoc,
       ParamName, NameLoc, Depth, Position, EqualLoc, DefaultArg);
 }
 
@@ -1201,7 +1218,8 @@ static bool isEndOfTemplateArgument(Token Tok) {
 
 ParsedTemplateArgument Parser::ParseTemplateTemplateArgument() {
   if (!Tok.is(tok::identifier) && !Tok.is(tok::coloncolon) &&
-      !Tok.is(tok::annot_cxxscope))
+      !Tok.is(tok::annot_cxxscope) && !Tok.is(tok::annot_template_id) &&
+      !Tok.is(tok::annot_non_type))
     return ParsedTemplateArgument();
 
   // C++0x [temp.arg.template]p1:
@@ -1245,12 +1263,27 @@ ParsedTemplateArgument Parser::ParseTemplateTemplateArgument() {
                                     /*EnteringContext=*/false, Template))
         Result = ParsedTemplateArgument(SS, Template, Name.StartLocation);
     }
-  } else if (Tok.is(tok::identifier)) {
+  } else if (Tok.is(tok::identifier) || Tok.is(tok::annot_template_id) ||
+             Tok.is(tok::annot_non_type)) {
     // We may have a (non-dependent) template name.
     TemplateTy Template;
     UnqualifiedId Name;
-    Name.setIdentifier(Tok.getIdentifierInfo(), Tok.getLocation());
-    ConsumeToken(); // the identifier
+    if (Tok.is(tok::annot_non_type)) {
+      NamedDecl *ND = getNonTypeAnnotation(Tok);
+      if (!isa<VarTemplateDecl>(ND))
+        return Result;
+      Name.setIdentifier(ND->getIdentifier(), Tok.getLocation());
+      ConsumeAnnotationToken();
+    } else if (Tok.is(tok::annot_template_id)) {
+      TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
+      if (TemplateId->LAngleLoc.isValid())
+        return Result;
+      Name.setIdentifier(TemplateId->Name, Tok.getLocation());
+      ConsumeAnnotationToken();
+    } else {
+      Name.setIdentifier(Tok.getIdentifierInfo(), Tok.getLocation());
+      ConsumeToken(); // the identifier
+    }
 
     TryConsumeToken(tok::ellipsis, EllipsisLoc);
 
@@ -1261,7 +1294,8 @@ ParsedTemplateArgument Parser::ParseTemplateTemplateArgument() {
           /*hasTemplateKeyword=*/false, Name,
           /*ObjectType=*/nullptr,
           /*EnteringContext=*/false, Template, MemberOfUnknownSpecialization);
-      if (TNK == TNK_Dependent_template_name || TNK == TNK_Type_template) {
+      if (TNK == TNK_Dependent_template_name || TNK == TNK_Type_template ||
+          TNK == TNK_Var_template || TNK == TNK_Concept_template) {
         // We have an id-expression that refers to a class template or
         // (C++0x) alias template.
         Result = ParsedTemplateArgument(SS, Template, Name.StartLocation);
@@ -1301,13 +1335,12 @@ ParsedTemplateArgument Parser::ParseTemplateArgument() {
   {
     TentativeParsingAction TPA(*this);
 
-    ParsedTemplateArgument TemplateTemplateArgument
-      = ParseTemplateTemplateArgument();
+    ParsedTemplateArgument TemplateTemplateArgument =
+        ParseTemplateTemplateArgument();
     if (!TemplateTemplateArgument.isInvalid()) {
       TPA.Commit();
       return TemplateTemplateArgument;
     }
-
     // Revert this tentative parse to parse a non-type template argument.
     TPA.Revert();
   }
diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp
index b58100c635677..2a731a18ea82a 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -1376,7 +1376,7 @@ Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename,
               // If we annotated then the current token should not still be ::
               // FIXME we may want to also check for tok::annot_typename but
               // currently don't have a test case.
-              if (Tok.isNot(tok::annot_cxxscope))
+              if (Tok.isNot(tok::annot_cxxscope) && Tok.isNot(tok::identifier))
                 break;
             }
 
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index ff50b3f83908c..a8cfe202c5214 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -1838,7 +1838,8 @@ Parser::TryAnnotateName(CorrectionCandidateCallback *CCC,
 
   case NameClassificationKind::TypeTemplate:
     if (Next.isNot(tok::less)) {
-      // This may be a type template being used as a template template argument.
+      // This may be a type or variable template being used as a template
+      // template argument.
       if (SS.isNotEmpty())
         AnnotateScopeToken(SS, !WasScopeAnnotation);
       return AnnotatedNameKind::TemplateName;
@@ -1852,10 +1853,10 @@ Parser::TryAnnotateName(CorrectionCandidateCallback *CCC,
         Classification.getKind() == NameClassificationKind::Concept;
     // We have a template name followed by '<'. Consume the identifier token so
     // we reach the '<' and annotate it.
-    if (Next.is(tok::less))
-      ConsumeToken();
     UnqualifiedId Id;
     Id.setIdentifier(Name, NameLoc);
+    if (Next.is(tok::less))
+      ConsumeToken();
     if (AnnotateTemplateIdToken(
             TemplateTy::make(Classification.getTemplateName()),
             Classification.getTemplateNameKind(), SS, SourceLocation(), Id,
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index c74b67106ad74..6e97cd08584b4 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -35,6 +35,7 @@
 #include "clang/AST/RecordLayout.h"
 #include "clang/AST/Stmt.h"
 #include "clang/AST/TemplateBase.h"
+#include "clang/AST/TemplateName.h"
 #include "clang/AST/Type.h"
 #include "clang/AST/TypeLoc.h"
 #include "clang/AST/UnresolvedSet.h"
@@ -3716,7 +3717,7 @@ void Sema::checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto,
 }
 
 void Sema::CheckConstrainedAuto(const AutoType *AutoT, SourceLocation Loc) {
-  if (ConceptDecl *Decl = AutoT->getTypeConstraintConcept()) {
+  if (TemplateDecl *Decl = AutoT->getTypeConstraintConcept()) {
     DiagnoseUseOfDecl(Decl, Loc);
   }
 }
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 45c7178c6965d..af8f5ca185c59 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -2903,9 +2903,11 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS,
     // in BuildTemplateIdExpr().
     // The single lookup result must be a variable template declaration.
     if (Id.getKind() == UnqualifiedIdKind::IK_TemplateId && Id.TemplateId &&
-        Id.TemplateId->Kind == TNK_Var_template) {
-      assert(R.getAsSingle<VarTemplateDecl>() &&
-             "There should only be one declaration found.");
+        (Id.TemplateId->Kind == TNK_Var_template ||
+         Id.TemplateId->Kind == TNK_Concept_template)) {
+      // TODO CORENTIN
+      // assert(R.getAsSingle<VarTemplateDecl>() &&
+      //       "There should only be one declaration found.");
     }
 
     return BuildTemplateIdExpr(SS, TemplateKWLoc, R, ADL, TemplateArgs);
diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp
index 1c6f292454ed6..95aa60ed64a82 100644
--- a/clang/lib/Sema/SemaInit.cpp
+++ b/clang/lib/Sema/SemaInit.cpp
@@ -3713,7 +3713,7 @@ ValueDecl *InitializedEntity::getDecl() const {
   case EK_ParenAggInitMember:
   case EK_Binding:
   case EK_TemplateParameter:
-    return Variable.VariableOrMember;
+    return cast<ValueDecl>(Variable.VariableOrMember);
 
   case EK_Parameter:
   case EK_Parameter_CF_Audited:
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 698d1270be634..4a985bf37a49c 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -18,6 +18,7 @@
 #include "clang/AST/Expr.h"
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/TemplateName.h"
+#include "clang/AST/Type.h"
 #include "clang/AST/TypeVisitor.h"
 #include "clang/Basic/Builtins.h"
 #include "clang/Basic/DiagnosticSema.h"
@@ -38,6 +39,7 @@
 #include "clang/Sema/TemplateDeduction.h"
 #include "llvm/ADT/SmallBitVector.h"
 #include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Casting.h"
 #include "llvm/Support/SaveAndRestore.h"
 
 #include <optional>
@@ -305,10 +307,11 @@ TemplateNameKind Sema::isTemplateName(Scope *S,
       assert(isa<ClassTemplateDecl>(TD) || isa<TemplateTemplateParmDecl>(TD) ||
              isa<TypeAliasTemplateDecl>(TD) || isa<VarTemplateDecl>(TD) ||
              isa<BuiltinTemplateDecl>(TD) || isa<ConceptDecl>(TD));
-      TemplateKind =
-          isa<VarTemplateDecl>(TD) ? TNK_Var_template :
-          isa<ConceptDecl>(TD) ? TNK_Concept_template :
-          TNK_Type_template;
+      TemplateKind = isa<TemplateTemplateParmDecl>(TD)
+                         ? dyn_cast<TemplateTemplateParmDecl>(TD)->kind()
+                     : isa<VarTemplateDecl>(TD) ? TNK_Var_template
+                     : isa<ConceptDecl>(TD)     ? TNK_Concept_template
+                                                : TNK_Type_template;
     }
   }
 
@@ -1071,12 +1074,27 @@ makeTemplateArgumentListInfo(Sema &S, TemplateIdAnnotation &TemplateId) {
 bool Sema::CheckTypeConstraint(TemplateIdAnnotation *TypeConstr) {
 
   TemplateName TN = TypeConstr->Template.get();
-  ConceptDecl *CD = cast<ConceptDecl>(TN.getAsTemplateDecl());
+  NamedDecl *CD = nullptr;
+  bool IsTypeConcept = false;
+  bool RequiresArguments = false;
+  if (TemplateTemplateParmDecl *TTP =
+          llvm::dyn_cast<TemplateTemplateParmDecl>(TN.getAsTemplateDecl())) {
+    IsTypeConcept = TTP->isTypeConceptTemplateParam();
+    RequiresArguments =
+        TTP->getTemplateParameters()->getMinRequiredArguments() > 1;
+    CD = TTP;
+  } else {
+    CD = TN.getAsTemplateDecl();
+    IsTypeConcept = cast<ConceptDecl>(CD)->isTypeConcept();
+    RequiresArguments = cast<ConceptDecl>(CD)
+                            ->getTemplateParameters()
+                            ->getMinRequiredArguments() > 1;
+  }
 
   // C++2a [temp.param]p4:
   //     [...] The concept designated by a type-constraint shall be a type
   //     concept ([temp.concept]).
-  if (!CD->isTypeConcept()) {
+  if (!IsTypeConcept) {
     Diag(TypeConstr->TemplateNameLoc,
          diag::err_type_constraint_non_type_concept);
     return true;
@@ -1087,8 +1105,7 @@ bool Sema::CheckTypeConstraint(TemplateIdAnnotation *TypeConstr) {
 
   bool WereArgsSpecified = TypeConstr->LAngleLoc.isValid();
 
-  if (!WereArgsSpecified &&
-      CD->getTemplateParameters()->getMinRequiredArguments() > 1) {
+  if (!WereArgsSpecified && RequiresArguments) {
     Diag(TypeConstr->TemplateNameLoc,
          diag::err_type_constraint_missing_arguments)
         << CD;
@@ -1115,7 +1132,7 @@ bool Sema::BuildTypeConstraint(const CXXScopeSpec &SS,
     return true;
 
   TemplateName TN = TypeConstr->Template.get();
-  ConceptDecl *CD = cast<ConceptDecl>(TN.getAsTemplateDecl());
+  TemplateDecl *CD = cast<TemplateDecl>(TN.getAsTemplateDecl());
   UsingShadowDecl *USD = TN.getAsUsingShadowDecl();
 
   DeclarationNameInfo ConceptName(DeclarationName(TypeConstr->Name),
@@ -1143,7 +1160,7 @@ bool Sema::BuildTypeConstraint(const CXXScopeSpec &SS,
 template <typename ArgumentLocAppender>
 static ExprResult formImmediatelyDeclaredConstraint(
     Sema &S, NestedNameSpecifierLoc NS, DeclarationNameInfo NameInfo,
-    ConceptDecl *NamedConcept, NamedDecl *FoundDecl, SourceLocation LAngleLoc,
+    NamedDecl *NamedConcept, NamedDecl *FoundDecl, SourceLocation LAngleLoc,
     SourceLocation RAngleLoc, QualType ConstrainedType,
     SourceLocation ParamNameLoc, ArgumentLocAppender Appender,
     SourceLocation EllipsisLoc) {
@@ -1162,12 +1179,32 @@ static ExprResult formImmediatelyDeclaredConstraint(
   //     constraint of T. [...]
   CXXScopeSpec SS;
   SS.Adopt(NS);
-  ExprResult ImmediatelyDeclaredConstraint = S.CheckConceptTemplateId(
-      SS, /*TemplateKWLoc=*/SourceLocation(), NameInfo,
-      /*FoundDecl=*/FoundDecl ? FoundDecl : NamedConcept, NamedConcept,
-      &ConstraintArgs);
-  if (ImmediatelyDeclaredConstraint.isInvalid() || !EllipsisLoc.isValid())
-    return ImmediatelyDeclaredConstraint;
+  ExprResult ImmediatelyDeclaredConstraint;
+  if (auto *CD = dyn_cast<ConceptDecl>(NamedConcept)) {
+    ImmediatelyDeclaredConstraint = S.CheckConceptTemplateId(
+        SS, /*TemplateKWLoc=*/SourceLocation(), NameInfo,
+        /*FoundDecl=*/FoundDecl ? FoundDecl : NamedConcept, CD,
+        &ConstraintArgs);
+    if (ImmediatelyDeclaredConstraint.isInvalid() || !EllipsisLoc.isValid())
+      return ImmediatelyDeclaredConstraint;
+  }
+  // We have a template template parameter
+  else {
+    auto *CDT = dyn_cast<TemplateTemplateParmDecl>(NamedConcept);
+    ImmediatelyDeclaredConstraint = S.CheckVarOrConceptTemplateTemplateId(
+        SS, NameInfo, CDT, SourceLocation(), &ConstraintArgs);
+    if (ImmediatelyDeclaredConstraint.isInvalid())
+      return ImmediatelyDeclaredConstraint;
+    UnresolvedSet<1> R;
+    R.addDecl(CDT);
+    ImmediatelyDeclaredConstraint = UnresolvedLookupExpr::Create(
+        S.getASTContext(), nullptr, SS.getWithLocInContext(S.getASTContext()),
+        SourceLocation(), NameInfo, false, &ConstraintArgs, R.begin(), R.end(),
+        /*KnownDependent=*/false,
+        /*KnownInstantiationDependent=*/false);
+    if (ImmediatelyDeclaredConstraint.isInvalid() || !EllipsisLoc.isValid())
+      return ImmediatelyDeclaredConstraint;
+  }
 
   // C++2a [temp.param]p4:
   //     [...] If T is not a pack, then E is E', otherwise E is (E' && ...).
@@ -1191,7 +1228,8 @@ static ExprResult formImmediatelyDeclaredConstraint(
 
 bool Sema::AttachTypeConstraint(NestedNameSpecifierLoc NS,
                                 DeclarationNameInfo NameInfo,
-                                ConceptDecl *NamedConcept, NamedDecl *FoundDecl,
+                                TemplateDecl *NamedConcept,
+                                NamedDecl *FoundDecl,
                                 const TemplateArgumentListInfo *TemplateArgs,
                                 TemplateTypeParmDecl *ConstrainedParameter,
                                 SourceLocation EllipsisLoc) {
@@ -1588,11 +1626,15 @@ NamedDecl *Sema::ActOnNonTypeTemplateParameter(Scope *S, Declarator &D,
   return Param;
 }
 
+/// ActOnTemplateTemplateParameter - Called when a C++ template template
+/// parameter (e.g. T in template <template \<typename> class T> class array)
+/// has been parsed. S is the current scope.
 NamedDecl *Sema::ActOnTemplateTemplateParameter(
-    Scope *S, SourceLocation TmpLoc, TemplateParameterList *Params,
-    bool Typename, SourceLocation EllipsisLoc, IdentifierInfo *Name,
-    SourceLocation NameLoc, unsigned Depth, unsigned Position,
-    SourceLocation EqualLoc, ParsedTemplateArgument Default) {
+    Scope *S, SourceLocation TmpLoc, TemplateNameKind Kind, bool Typename,
+    TemplateParameterList *Params, SourceLocation EllipsisLoc,
+    IdentifierInfo *Name, SourceLocation NameLoc, unsigned Depth,
+    unsigned Position, SourceLocation EqualLoc,
+    ParsedTemplateArgument Default) {
   assert(S->isTemplateParamScope() &&
          "Template template parameter not in template parameter scope!");
 
@@ -1609,7 +1651,7 @@ NamedDecl *Sema::ActOnTemplateTemplateParameter(
   TemplateTemplateParmDecl *Param = TemplateTemplateParmDecl::Create(
       Context, Context.getTranslationUnitDecl(),
       NameLoc.isInvalid() ? TmpLoc : NameLoc, Depth, Position, IsParameterPack,
-      Name, Typename, Params);
+      Name, Kind, Typename, Params);
   Param->setAccess(AS_public);
 
   if (Param->isParameterPack())
@@ -1658,6 +1700,14 @@ NamedDecl *Sema::ActOnTemplateTemplateParameter(
       return Param;
     }
 
+    TemplateName Name =
+        DefaultArg.getArgument().getAsTemplateOrTemplatePattern();
+    TemplateDecl *Template = Name.getAsTemplateDecl();
+    if (Template &&
+        !CheckDeclCompatibleWithTemplateTemplate(Template, Param, DefaultArg)) {
+      return Param;
+    }
+
     // Check for unexpanded parameter packs.
     if (DiagnoseUnexpandedParameterPack(DefaultArg.getLocation(),
                                         DefaultArg.getArgument().getAsTemplate(),
@@ -2667,6 +2717,18 @@ struct DependencyChecker : DynamicRecursiveASTVisitor {
     return DynamicRecursiveASTVisitor::VisitDeclRefExpr(E);
   }
 
+  bool VisitUnresolvedLookupExpr(UnresolvedLookupExpr *ULE) override {
+    if (ULE->isConceptReference() || ULE->isVarDeclReference()) {
+      if (auto *TTP = ULE->getTemplateTemplateDecl()) {
+        if (Matches(TTP->getDepth(), ULE->getExprLoc()))
+          return false;
+      }
+      for (auto &TLoc : ULE->template_arguments())
+        DynamicRecursiveASTVisitor::TraverseTemplateArgumentLoc(TLoc);
+    }
+    return DynamicRecursiveASTVisitor::VisitUnresolvedLookupExpr(ULE);
+  }
+
   bool VisitSubstTemplateTypeParmType(SubstTemplateTypeParmType *T) override {
     return TraverseType(T->getReplacementType());
   }
@@ -4047,8 +4109,10 @@ static bool CheckTemplateSpecializationScope(Sema &S, NamedDecl *Specialized,
 
 static TemplateSpecializationKind getTemplateSpecializationKind(Decl *D);
 
-static bool isTemplateArgumentTemplateParameter(
-    const TemplateArgument &Arg, unsigned Depth, unsigned Index) {
+static bool isTemplateArgumentTemplateParameter(const TemplateArgument &Arg,
+                                                NamedDecl *Param,
+                                                unsigned Depth,
+                                                unsigned Index) {
   switch (Arg.getKind()) {
   case TemplateArgument::Null:
   case TemplateArgument::NullPtr:
@@ -4104,7 +4168,8 @@ static bool isSameAsPrimaryTemplate(TemplateParameterList *Params,
       Arg = Arg.pack_begin()->getPackExpansionPattern();
     }
 
-    if (!isTemplateArgumentTemplateParameter(Arg, Depth, I))
+    if (!isTemplateArgumentTemplateParameter(Arg, Params->getParam(I), Depth,
+                                             I))
       return false;
   }
 
@@ -4694,6 +4759,28 @@ ExprResult Sema::CheckVarTemplateId(
   return BuildDeclarationNameExpr(SS, NameInfo, Var, FoundD, TemplateArgs);
 }
 
+ExprResult Sema::CheckVarOrConceptTemplateTemplateId(
+    const CXXScopeSpec &, const DeclarationNameInfo &,
+    TemplateTemplateParmDecl *Template, SourceLocation TemplateLoc,
+    const TemplateArgumentListInfo *TemplateArgs) {
+  assert(Template && "A variable template id without template?");
+
+  if (Template->kind() != TemplateNameKind::TNK_Var_template &&
+      Template->kind() != TemplateNameKind::TNK_Concept_template)
+    return ExprResult();
+
+  // Check that the template argument list is well-formed for this template.
+  CheckTemplateArgumentInfo CTAI;
+  if (CheckTemplateArgumentList(
+          Template, TemplateLoc,
+          const_cast<TemplateArgumentListInfo &>(*TemplateArgs),
+          /*DefaultArgs=*/{}, /*PartialTemplateArgs=*/false, CTAI,
+          /*UpdateArgsWithConversions=*/true))
+    return true;
+
+  return ExprResult();
+}
+
 void Sema::diagnoseMissingTemplateArguments(TemplateName Name,
                                             SourceLocation Loc) {
   Diag(Loc, diag::err_template_missing_args)
@@ -4812,6 +4899,16 @@ ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS,
                                   R.getAsSingle<ConceptDecl>(), TemplateArgs);
   }
 
+  // In C++1y, check variable template ids.
+  if (R.getAsSingle<TemplateTemplateParmDecl>()) {
+    ExprResult Res = CheckVarOrConceptTemplateTemplateId(
+        SS, R.getLookupNameInfo(), R.getAsSingle<TemplateTemplateParmDecl>(),
+        TemplateKWLoc, TemplateArgs);
+    if (Res.isInvalid() || Res.isUsable())
+      return Res;
+    // Result is dependent. Carry on to build an UnresolvedLookupEpxr.
+  }
+
   // We don't want lookup warnings at this point.
   R.suppressDiagnostics();
 
@@ -5611,12 +5708,25 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param, TemplateArgumentLoc &ArgLoc,
     break;
 
   case TemplateArgument::Expression:
-  case TemplateArgument::Type:
+  case TemplateArgument::Type: {
+    auto Kind = 0;
+    switch (TempParm->kind()) {
+    case TemplateNameKind::TNK_Var_template:
+      Kind = 1;
+      break;
+    case TemplateNameKind::TNK_Concept_template:
+      Kind = 2;
+      break;
+    default:
+      break;
+    }
+
     // We have a template template parameter but the template
     // argument does not refer to a template.
     Diag(ArgLoc.getLocation(), diag::err_template_arg_must_be_template)
-        << getLangOpts().CPlusPlus11;
+        << Kind << getLangOpts().CPlusPlus11;
     return true;
+  }
 
   case TemplateArgument::Declaration:
   case TemplateArgument::Integral:
@@ -6361,7 +6471,7 @@ enum NullPointerValueKind {
 /// Determine whether the given template argument is a null pointer
 /// value of the appropriate type.
 static NullPointerValueKind
-isNullPointerValueTemplateArgument(Sema &S, NonTypeTemplateParmDecl *Param,
+isNullPointerValueTemplateArgument(Sema &S, NamedDecl *Param,
                                    QualType ParamType, Expr *Arg,
                                    Decl *Entity = nullptr) {
   if (Arg->isValueDependent() || Arg->isTypeDependent())
@@ -6466,9 +6576,10 @@ isNullPointerValueTemplateArgument(Sema &S, NonTypeTemplateParmDecl *Param,
 
 /// Checks whether the given template argument is compatible with its
 /// template parameter.
-static bool CheckTemplateArgumentIsCompatibleWithParameter(
-    Sema &S, NonTypeTemplateParmDecl *Param, QualType ParamType, Expr *ArgIn,
-    Expr *Arg, QualType ArgType) {
+static bool
+CheckTemplateArgumentIsCompatibleWithParameter(Sema &S, NamedDecl *Param,
+                                               QualType ParamType, Expr *ArgIn,
+                                               Expr *Arg, QualType ArgType) {
   bool ObjCLifetimeConversion;
   if (ParamType->isPointerType() &&
       !ParamType->castAs<PointerType>()->getPointeeType()->isFunctionType() &&
@@ -6524,7 +6635,7 @@ static bool CheckTemplateArgumentIsCompatibleWithParameter(
 /// Checks whether the given template argument is the address
 /// of an object or function according to C++ [temp.arg.nontype]p1.
 static bool CheckTemplateArgumentAddressOfObjectOrFunction(
-    Sema &S, NonTypeTemplateParmDecl *Param, QualType ParamType, Expr *ArgIn,
+    Sema &S, NamedDecl *Param, QualType ParamType, Expr *ArgIn,
     TemplateArgument &SugaredConverted, TemplateArgument &CanonicalConverted) {
   bool Invalid = false;
   Expr *Arg = ArgIn;
@@ -6786,11 +6897,9 @@ static bool CheckTemplateArgumentAddressOfObjectOrFunction(
 
 /// Checks whether the given template argument is a pointer to
 /// member constant according to C++ [temp.arg.nontype]p1.
-static bool
-CheckTemplateArgumentPointerToMember(Sema &S, NonTypeTemplateParmDecl *Param,
-                                     QualType ParamType, Expr *&ResultArg,
-                                     TemplateArgument &SugaredConverted,
-                                     TemplateArgument &CanonicalConverted) {
+static bool CheckTemplateArgumentPointerToMember(
+    Sema &S, NamedDecl *Param, QualType ParamType, Expr *&ResultArg,
+    TemplateArgument &SugaredConverted, TemplateArgument &CanonicalConverted) {
   bool Invalid = false;
 
   Expr *Arg = ResultArg;
@@ -6921,8 +7030,15 @@ CheckTemplateArgumentPointerToMember(Sema &S, NonTypeTemplateParmDecl *Param,
   return true;
 }
 
-ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
-                                       QualType ParamType, Expr *Arg,
+/// Check a template argument against its corresponding
+/// non-type template parameter.
+///
+/// This routine implements the semantics of C++ [temp.arg.nontype].
+/// If an error occurred, it returns ExprError(); otherwise, it
+/// returns the converted template argument. \p ParamType is the
+/// type of the non-type template parameter after it has been instantiated.
+ExprResult Sema::CheckTemplateArgument(NamedDecl *Param, QualType ParamType,
+                                       Expr *Arg,
                                        TemplateArgument &SugaredConverted,
                                        TemplateArgument &CanonicalConverted,
                                        bool StrictCheck,
@@ -6976,7 +7092,7 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
         return ExprError();
     } else {
       TemplateDeductionInfo Info(DeductionArg->getExprLoc(),
-                                 Param->getDepth() + 1);
+                                 Param->getTemplateDepth() + 1);
       ParamType = QualType();
       TemplateDeductionResult Result =
           DeduceAutoType(TSI->getTypeLoc(), DeductionArg, ParamType, Info,
@@ -6988,13 +7104,14 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
                          // checking the template argument list.
                          /*IgnoreConstraints=*/true);
       if (Result == TemplateDeductionResult::AlreadyDiagnosed) {
-        if (ParamType.isNull())
-          return ExprError();
+        return ExprError();
       } else if (Result != TemplateDeductionResult::Success) {
-        Diag(Arg->getExprLoc(),
-             diag::err_non_type_template_parm_type_deduction_failure)
-            << Param->getDeclName() << Param->getType() << Arg->getType()
-            << Arg->getSourceRange();
+        if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Param)) {
+          Diag(Arg->getExprLoc(),
+               diag::err_non_type_template_parm_type_deduction_failure)
+              << Param->getDeclName() << NTTP->getType() << Arg->getType()
+              << Arg->getSourceRange();
+        }
         NoteTemplateParameterLocation(*Param);
         return ExprError();
       }
@@ -7389,7 +7506,7 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
       if (IntegerType->isUnsignedIntegerOrEnumerationType() &&
           (OldValue.isSigned() && OldValue.isNegative())) {
         Diag(Arg->getBeginLoc(), diag::warn_template_arg_negative)
-            << toString(OldValue, 10) << toString(Value, 10) << Param->getType()
+            << toString(OldValue, 10) << toString(Value, 10) << ParamType
             << Arg->getSourceRange();
         NoteTemplateParameterLocation(*Param);
       }
@@ -7404,7 +7521,7 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param,
         RequiredBits = OldValue.getSignificantBits();
       if (RequiredBits > AllowedBits) {
         Diag(Arg->getBeginLoc(), diag::warn_template_arg_too_large)
-            << toString(OldValue, 10) << toString(Value, 10) << Param->getType()
+            << toString(OldValue, 10) << toString(Value, 10) << ParamType
             << Arg->getSourceRange();
         NoteTemplateParameterLocation(*Param);
       }
@@ -7583,6 +7700,81 @@ static void DiagnoseTemplateParameterListArityMismatch(
     Sema &S, TemplateParameterList *New, TemplateParameterList *Old,
     Sema::TemplateParameterListEqualKind Kind, SourceLocation TemplateArgLoc);
 
+bool Sema::CheckDeclCompatibleWithTemplateTemplate(
+    TemplateDecl *Template, TemplateTemplateParmDecl *Param,
+    const TemplateArgumentLoc &Arg) {
+  // C++0x [temp.arg.template]p1:
+  //   A template-argument for a template template-parameter shall be
+  //   the name of a class template or an alias template, expressed as an
+  //   id-expression. When the template-argument names a class template, only
+  //   primary class templates are considered when matching the
+  //   template template argument with the corresponding parameter;
+  //   partial specializations are not considered even if their
+  //   parameter lists match that of the template template parameter.
+  //
+
+  TemplateNameKind Kind = TNK_Non_template;
+  unsigned DiagFoundKind = 0;
+
+  if (auto *TTP = llvm::dyn_cast<TemplateTemplateParmDecl>(Template)) {
+    switch (TTP->kind()) {
+    case TemplateNameKind::TNK_Concept_template:
+      DiagFoundKind = 3;
+      break;
+    case TemplateNameKind::TNK_Var_template:
+      DiagFoundKind = 2;
+      break;
+    default:
+      DiagFoundKind = 1;
+      break;
+    }
+    Kind = TTP->kind();
+  } else if (isa<ConceptDecl>(Template)) {
+    Kind = TemplateNameKind::TNK_Concept_template;
+    DiagFoundKind = 3;
+  } else if (isa<FunctionTemplateDecl>(Template)) {
+    Kind = TemplateNameKind::TNK_Function_template;
+    DiagFoundKind = 0;
+  } else if (isa<VarTemplateDecl>(Template)) {
+    Kind = TemplateNameKind::TNK_Var_template;
+    DiagFoundKind = 2;
+  } else if (isa<ClassTemplateDecl>(Template) ||
+             isa<TypeAliasTemplateDecl>(Template) ||
+             isa<BuiltinTemplateDecl>(Template)) {
+    Kind = TemplateNameKind::TNK_Type_template;
+    DiagFoundKind = 1;
+  } else {
+    assert(false && "Unexpected Decl");
+  }
+
+  if (Kind == Param->kind()) {
+    return true;
+  }
+
+  unsigned DiagKind = 0;
+  switch (Param->kind()) {
+  case TemplateNameKind::TNK_Concept_template:
+    DiagKind = 2;
+    break;
+  case TemplateNameKind::TNK_Var_template:
+    DiagKind = 1;
+    break;
+  default:
+    DiagKind = 0;
+    break;
+  }
+  Diag(Arg.getLocation(), diag::err_template_arg_not_valid_template)
+      << DiagKind;
+  Diag(Template->getLocation(), diag::note_template_arg_refers_to_template_here)
+      << DiagFoundKind << Template;
+  return false;
+}
+
+/// Check a template argument against its corresponding
+/// template template parameter.
+///
+/// This routine implements the semantics of C++ [temp.arg.template].
+/// It returns true if an error occurred, and false otherwise.
 bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param,
                                          TemplateParameterList *Params,
                                          TemplateArgumentLoc &Arg,
@@ -7599,27 +7791,8 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param,
   if (Template->isInvalidDecl())
     return true;
 
-  // C++0x [temp.arg.template]p1:
-  //   A template-argument for a template template-parameter shall be
-  //   the name of a class template or an alias template, expressed as an
-  //   id-expression. When the template-argument names a class template, only
-  //   primary class templates are considered when matching the
-  //   template template argument with the corresponding parameter;
-  //   partial specializations are not considered even if their
-  //   parameter lists match that of the template template parameter.
-  //
-  // Note that we also allow template template parameters here, which
-  // will happen when we are dealing with, e.g., class template
-  // partial specializations.
-  if (!isa<ClassTemplateDecl>(Template) &&
-      !isa<TemplateTemplateParmDecl>(Template) &&
-      !isa<TypeAliasTemplateDecl>(Template) &&
-      !isa<BuiltinTemplateDecl>(Template)) {
-    assert(isa<FunctionTemplateDecl>(Template) &&
-           "Only function templates are possible here");
-    Diag(Arg.getLocation(), diag::err_template_arg_not_valid_template);
-    Diag(Template->getLocation(), diag::note_template_arg_refers_here_func)
-      << Template;
+  if (!CheckDeclCompatibleWithTemplateTemplate(Template, Param, Arg)) {
+    return true;
   }
 
   // C++1z [temp.arg.template]p3: (DR 150)
@@ -7691,6 +7864,10 @@ void Sema::NoteTemplateParameterLocation(const NamedDecl &Decl) {
                diag::note_template_param_external);
 }
 
+/// Given a non-type template argument that refers to a
+/// declaration and the type of its corresponding non-type template
+/// parameter, produce an expression that properly refers to that
+/// declaration.
 ExprResult Sema::BuildExpressionFromDeclTemplateArgument(
     const TemplateArgument &Arg, QualType ParamType, SourceLocation Loc,
     NamedDecl *TemplateParam) {
@@ -8011,34 +8188,40 @@ static bool MatchTemplateParameterKind(
 
     return false;
   }
-
   // For non-type template parameters, check the type of the parameter.
-  if (NonTypeTemplateParmDecl *OldNTTP
-                                    = dyn_cast<NonTypeTemplateParmDecl>(Old)) {
+  if (NonTypeTemplateParmDecl *OldNTTP =
+          dyn_cast<NonTypeTemplateParmDecl>(Old)) {
     NonTypeTemplateParmDecl *NewNTTP = cast<NonTypeTemplateParmDecl>(New);
 
-    // C++20 [temp.over.link]p6:
-    //   Two [non-type] template-parameters are equivalent [if] they have
-    //   equivalent types ignoring the use of type-constraints for
-    //   placeholder types
-    QualType OldType = S.Context.getUnconstrainedType(OldNTTP->getType());
-    QualType NewType = S.Context.getUnconstrainedType(NewNTTP->getType());
-    if (!S.Context.hasSameType(OldType, NewType)) {
-      if (Complain) {
-        unsigned NextDiag = diag::err_template_nontype_parm_different_type;
-        if (TemplateArgLoc.isValid()) {
-          S.Diag(TemplateArgLoc,
-                 diag::err_template_arg_template_params_mismatch);
-          NextDiag = diag::note_template_nontype_parm_different_type;
+    // If we are matching a template template argument to a template
+    // template parameter and one of the non-type template parameter types
+    // is dependent, then we must wait until template instantiation time
+    // to actually compare the arguments.
+    if (Kind != Sema::TPL_TemplateTemplateParmMatch ||
+        (!OldNTTP->getType()->isDependentType() &&
+         !NewNTTP->getType()->isDependentType())) {
+      // C++20 [temp.over.link]p6:
+      //   Two [non-type] template-parameters are equivalent [if] they have
+      //   equivalent types ignoring the use of type-constraints for
+      //   placeholder types
+      QualType OldType = S.Context.getUnconstrainedType(OldNTTP->getType());
+      QualType NewType = S.Context.getUnconstrainedType(NewNTTP->getType());
+      if (!S.Context.hasSameType(OldType, NewType)) {
+        if (Complain) {
+          unsigned NextDiag = diag::err_template_nontype_parm_different_type;
+          if (TemplateArgLoc.isValid()) {
+            S.Diag(TemplateArgLoc,
+                   diag::err_template_arg_template_params_mismatch);
+            NextDiag = diag::note_template_nontype_parm_different_type;
+          }
+          S.Diag(NewNTTP->getLocation(), NextDiag)
+              << NewNTTP->getType() << (Kind != Sema::TPL_TemplateMatch);
+          S.Diag(OldNTTP->getLocation(),
+                 diag::note_template_nontype_parm_prev_declaration)
+              << OldNTTP->getType();
         }
-        S.Diag(NewNTTP->getLocation(), NextDiag)
-            << NewNTTP->getType() << (Kind != Sema::TPL_TemplateMatch);
-        S.Diag(OldNTTP->getLocation(),
-               diag::note_template_nontype_parm_prev_declaration)
-            << OldNTTP->getType();
+        return false;
       }
-
-      return false;
     }
   }
   // For template template parameters, check the template parameter types.
@@ -8047,6 +8230,8 @@ static bool MatchTemplateParameterKind(
   else if (TemplateTemplateParmDecl *OldTTP =
                dyn_cast<TemplateTemplateParmDecl>(Old)) {
     TemplateTemplateParmDecl *NewTTP = cast<TemplateTemplateParmDecl>(New);
+    if (OldTTP->kind() != NewTTP->kind())
+      return false;
     if (!S.TemplateParameterListsAreEqual(
             NewInstFrom, NewTTP->getTemplateParameters(), OldInstFrom,
             OldTTP->getTemplateParameters(), Complain,
@@ -8058,6 +8243,7 @@ static bool MatchTemplateParameterKind(
   }
 
   if (Kind != Sema::TPL_TemplateParamsEquivalent &&
+      Kind != Sema::TPL_TemplateTemplateParmMatch &&
       !isa<TemplateTemplateParmDecl>(Old)) {
     const Expr *NewC = nullptr, *OldC = nullptr;
 
@@ -8423,6 +8609,11 @@ static bool CheckNonTypeTemplatePartialSpecializationArgs(
       if (isa<NonTypeTemplateParmDecl>(DRE->getDecl()))
         continue;
 
+    if (auto *ULE = dyn_cast<UnresolvedLookupExpr>(ArgExpr);
+        ULE && (ULE->isConceptReference() || ULE->isVarDeclReference())) {
+      continue;
+    }
+
     // C++ [temp.class.spec]p9:
     //   Within the argument list of a class template partial
     //   specialization, the following restrictions apply:
@@ -9034,11 +9225,11 @@ void Sema::CheckConceptRedefinition(ConceptDecl *NewDecl,
   Context.setPrimaryMergedDecl(NewDecl, OldConcept->getCanonicalDecl());
 }
 
-bool Sema::CheckConceptUseInDefinition(ConceptDecl *Concept,
-                                       SourceLocation Loc) {
-  if (!Concept->isInvalidDecl() && !Concept->hasDefinition()) {
-    Diag(Loc, diag::err_recursive_concept) << Concept;
-    Diag(Concept->getLocation(), diag::note_declared_at);
+bool Sema::CheckConceptUseInDefinition(NamedDecl *Concept, SourceLocation Loc) {
+  if (auto *CE = llvm::dyn_cast<ConceptDecl>(Concept);
+      CE && !CE->isInvalidDecl() && !CE->hasDefinition()) {
+    Diag(Loc, diag::err_recursive_concept) << CE;
+    Diag(CE->getLocation(), diag::note_declared_at);
     return true;
   }
   return false;
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 9e56e6978ef7f..c36aac906af3a 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -37,6 +37,7 @@
 #include "clang/Basic/PartialDiagnostic.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/Specifiers.h"
+#include "clang/Basic/TemplateKinds.h"
 #include "clang/Sema/EnterExpressionEvaluationContext.h"
 #include "clang/Sema/Ownership.h"
 #include "clang/Sema/Sema.h"
@@ -146,11 +147,7 @@ static void MarkUsedTemplateParameters(ASTContext &Ctx, QualType T,
                                        bool OnlyDeduced, unsigned Level,
                                        llvm::SmallBitVector &Deduced);
 
-/// If the given expression is of a form that permits the deduction
-/// of a non-type template parameter, return the declaration of that
-/// non-type template parameter.
-static const NonTypeTemplateParmDecl *
-getDeducedParameterFromExpr(const Expr *E, unsigned Depth) {
+static const Expr *unwrapExpressionForDeduction(const Expr *E) {
   // If we are within an alias template, the expression may have undergone
   // any number of parameter substitutions already.
   while (true) {
@@ -170,18 +167,107 @@ getDeducedParameterFromExpr(const Expr *E, unsigned Depth) {
     } else
       break;
   }
+  return E;
+}
+
+class NonTypeOrVarTemplateParmDecl {
+public:
+  NonTypeOrVarTemplateParmDecl(const NamedDecl *Template) : Template(Template) {
+    assert(!Template || isa<NonTypeTemplateParmDecl>(Template) ||
+           (isa<TemplateTemplateParmDecl>(Template) &&
+            (cast<TemplateTemplateParmDecl>(Template)->kind() ==
+                 TNK_Var_template ||
+             cast<TemplateTemplateParmDecl>(Template)->kind() ==
+                 TNK_Concept_template)));
+  }
+
+  /*NonTypeOrVarTemplateParmDecl(const NonTypeTemplateParmDecl* NTTP)
+      : Template(NTTP) {}
+  NonTypeOrVarTemplateParmDecl(const TemplateTemplateParmDecl* TTP)
+      : Template(TTP) {
+    assert(TTP->kind() == TNK_Var_template || TTP->kind() ==
+  clang::TNK_Concept_template);
+  }*/
+
+  QualType getType() const {
+    if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Template))
+      return NTTP->getType();
+    return getTemplate()->kind() == TNK_Concept_template
+               ? getTemplate()->getASTContext().BoolTy
+               : getTemplate()->getASTContext().DependentTy;
+  }
+
+  unsigned getDepth() const {
+    if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Template))
+      return NTTP->getDepth();
+    return getTemplate()->getDepth();
+  }
+
+  unsigned getIndex() const {
+    if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Template))
+      return NTTP->getIndex();
+    return getTemplate()->getIndex();
+  }
+
+  const TemplateTemplateParmDecl *getTemplate() const {
+    return cast<TemplateTemplateParmDecl>(Template);
+  }
+
+  const NonTypeTemplateParmDecl *getNTTP() const {
+    return cast<NonTypeTemplateParmDecl>(Template);
+  }
+
+  TemplateParameter asTemplateParam() const {
+    if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Template))
+      return const_cast<NonTypeTemplateParmDecl *>(NTTP);
+    return const_cast<TemplateTemplateParmDecl *>(getTemplate());
+  }
+
+  bool isExpandedParameterPack() const {
+    if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Template))
+      return NTTP->isExpandedParameterPack();
+    return getTemplate()->isExpandedParameterPack();
+  }
+
+  SourceLocation getLocation() const {
+    if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Template))
+      return NTTP->getLocation();
+    return getTemplate()->getLocation();
+  }
 
+  operator bool() const { return Template; }
+
+private:
+  const NamedDecl *Template;
+};
+
+/// If the given expression is of a form that permits the deduction
+/// of a non-type template parameter, return the declaration of that
+/// non-type template parameter.
+static NonTypeOrVarTemplateParmDecl
+getDeducedNTTParameterFromExpr(const Expr *E, unsigned Depth) {
+  // If we are within an alias template, the expression may have undergone
+  // any number of parameter substitutions already.
+  E = unwrapExpressionForDeduction(E);
   if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
     if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(DRE->getDecl()))
       if (NTTP->getDepth() == Depth)
         return NTTP;
 
+  if (const auto *ULE = dyn_cast<UnresolvedLookupExpr>(E);
+      ULE && (ULE->isConceptReference() || ULE->isVarDeclReference())) {
+    if (auto *TTP = ULE->getTemplateTemplateDecl()) {
+
+      if (TTP->getDepth() == Depth)
+        return TTP;
+    }
+  }
   return nullptr;
 }
 
-static const NonTypeTemplateParmDecl *
-getDeducedParameterFromExpr(TemplateDeductionInfo &Info, Expr *E) {
-  return getDeducedParameterFromExpr(E, Info.getDeducedDepth());
+static const NonTypeOrVarTemplateParmDecl
+getDeducedNTTParameterFromExpr(TemplateDeductionInfo &Info, Expr *E) {
+  return getDeducedNTTParameterFromExpr(E, Info.getDeducedDepth());
 }
 
 /// Determine whether two declaration pointers refer to the same
@@ -386,29 +472,28 @@ checkDeducedTemplateArguments(ASTContext &Context,
 /// deduction is funneled through here.
 static TemplateDeductionResult
 DeduceNonTypeTemplateArgument(Sema &S, TemplateParameterList *TemplateParams,
-                              const NonTypeTemplateParmDecl *NTTP,
+                              const NonTypeOrVarTemplateParmDecl NTTP,
                               const DeducedTemplateArgument &NewDeduced,
                               QualType ValueType, TemplateDeductionInfo &Info,
                               bool PartialOrdering,
                               SmallVectorImpl<DeducedTemplateArgument> &Deduced,
                               bool *HasDeducedAnyParam) {
-  assert(NTTP->getDepth() == Info.getDeducedDepth() &&
+  assert(NTTP.getDepth() == Info.getDeducedDepth() &&
          "deducing non-type template argument with wrong depth");
 
   DeducedTemplateArgument Result = checkDeducedTemplateArguments(
-      S.Context, Deduced[NTTP->getIndex()], NewDeduced);
+      S.Context, Deduced[NTTP.getIndex()], NewDeduced);
   if (Result.isNull()) {
-    Info.Param = const_cast<NonTypeTemplateParmDecl*>(NTTP);
-    Info.FirstArg = Deduced[NTTP->getIndex()];
+    Info.Param = NTTP.asTemplateParam();
+    Info.FirstArg = Deduced[NTTP.getIndex()];
     Info.SecondArg = NewDeduced;
     return TemplateDeductionResult::Inconsistent;
   }
-
-  Deduced[NTTP->getIndex()] = Result;
+  Deduced[NTTP.getIndex()] = Result;
   if (!S.getLangOpts().CPlusPlus17)
     return TemplateDeductionResult::Success;
 
-  if (NTTP->isExpandedParameterPack())
+  if (NTTP.isExpandedParameterPack())
     // FIXME: We may still need to deduce parts of the type here! But we
     // don't have any way to find which slice of the type to use, and the
     // type stored on the NTTP itself is nonsense. Perhaps the type of an
@@ -417,7 +502,7 @@ DeduceNonTypeTemplateArgument(Sema &S, TemplateParameterList *TemplateParams,
 
   // Get the type of the parameter for deduction. If it's a (dependent) array
   // or function type, we will not have decayed it yet, so do that now.
-  QualType ParamType = S.Context.getAdjustedParameterType(NTTP->getType());
+  QualType ParamType = S.Context.getAdjustedParameterType(NTTP.getType());
   if (auto *Expansion = dyn_cast<PackExpansionType>(ParamType))
     ParamType = Expansion->getPattern();
 
@@ -444,7 +529,7 @@ DeduceNonTypeTemplateArgument(Sema &S, TemplateParameterList *TemplateParams,
 /// from the given integral constant.
 static TemplateDeductionResult DeduceNonTypeTemplateArgument(
     Sema &S, TemplateParameterList *TemplateParams,
-    const NonTypeTemplateParmDecl *NTTP, const llvm::APSInt &Value,
+    NonTypeOrVarTemplateParmDecl NTTP, const llvm::APSInt &Value,
     QualType ValueType, bool DeducedFromArrayBound, TemplateDeductionInfo &Info,
     bool PartialOrdering, SmallVectorImpl<DeducedTemplateArgument> &Deduced,
     bool *HasDeducedAnyParam) {
@@ -459,14 +544,14 @@ static TemplateDeductionResult DeduceNonTypeTemplateArgument(
 /// from the given null pointer template argument type.
 static TemplateDeductionResult
 DeduceNullPtrTemplateArgument(Sema &S, TemplateParameterList *TemplateParams,
-                              const NonTypeTemplateParmDecl *NTTP,
+                              NonTypeOrVarTemplateParmDecl NTTP,
                               QualType NullPtrType, TemplateDeductionInfo &Info,
                               bool PartialOrdering,
                               SmallVectorImpl<DeducedTemplateArgument> &Deduced,
                               bool *HasDeducedAnyParam) {
   Expr *Value = S.ImpCastExprToType(
                      new (S.Context) CXXNullPtrLiteralExpr(S.Context.NullPtrTy,
-                                                           NTTP->getLocation()),
+                                                           NTTP.getLocation()),
                      NullPtrType,
                      NullPtrType->isMemberPointerType() ? CK_NullToMemberPointer
                                                         : CK_NullToPointer)
@@ -482,7 +567,7 @@ DeduceNullPtrTemplateArgument(Sema &S, TemplateParameterList *TemplateParams,
 /// \returns true if deduction succeeded, false otherwise.
 static TemplateDeductionResult
 DeduceNonTypeTemplateArgument(Sema &S, TemplateParameterList *TemplateParams,
-                              const NonTypeTemplateParmDecl *NTTP, Expr *Value,
+                              NonTypeOrVarTemplateParmDecl NTTP, Expr *Value,
                               TemplateDeductionInfo &Info, bool PartialOrdering,
                               SmallVectorImpl<DeducedTemplateArgument> &Deduced,
                               bool *HasDeducedAnyParam) {
@@ -497,7 +582,7 @@ DeduceNonTypeTemplateArgument(Sema &S, TemplateParameterList *TemplateParams,
 /// \returns true if deduction succeeded, false otherwise.
 static TemplateDeductionResult
 DeduceNonTypeTemplateArgument(Sema &S, TemplateParameterList *TemplateParams,
-                              const NonTypeTemplateParmDecl *NTTP, ValueDecl *D,
+                              NonTypeOrVarTemplateParmDecl NTTP, ValueDecl *D,
                               QualType T, TemplateDeductionInfo &Info,
                               bool PartialOrdering,
                               SmallVectorImpl<DeducedTemplateArgument> &Deduced,
@@ -1930,14 +2015,14 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
         return Result;
 
       // Determine the array bound is something we can deduce.
-      const NonTypeTemplateParmDecl *NTTP =
-          getDeducedParameterFromExpr(Info, DAP->getSizeExpr());
+      NonTypeOrVarTemplateParmDecl NTTP =
+          getDeducedNTTParameterFromExpr(Info, DAP->getSizeExpr());
       if (!NTTP)
         return TemplateDeductionResult::Success;
 
       // We can perform template argument deduction for the given non-type
       // template parameter.
-      assert(NTTP->getDepth() == Info.getDeducedDepth() &&
+      assert(NTTP.getDepth() == Info.getDeducedDepth() &&
              "saw non-type template parameter with wrong depth");
       if (const auto *CAA = dyn_cast<ConstantArrayType>(AA)) {
         llvm::APSInt Size(CAA->getSize());
@@ -1994,10 +2079,10 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
       // deducing through the noexcept-specifier if it's part of the canonical
       // type. libstdc++ relies on this.
       Expr *NoexceptExpr = FPP->getNoexceptExpr();
-      if (const NonTypeTemplateParmDecl *NTTP =
-              NoexceptExpr ? getDeducedParameterFromExpr(Info, NoexceptExpr)
+      if (NonTypeOrVarTemplateParmDecl NTTP =
+              NoexceptExpr ? getDeducedNTTParameterFromExpr(Info, NoexceptExpr)
                            : nullptr) {
-        assert(NTTP->getDepth() == Info.getDeducedDepth() &&
+        assert(NTTP.getDepth() == Info.getDeducedDepth() &&
                "saw non-type template parameter with wrong depth");
 
         llvm::APSInt Noexcept(1);
@@ -2191,8 +2276,8 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
           return Result;
 
         // Perform deduction on the vector size, if we can.
-        const NonTypeTemplateParmDecl *NTTP =
-            getDeducedParameterFromExpr(Info, VP->getSizeExpr());
+        NonTypeOrVarTemplateParmDecl NTTP =
+            getDeducedNTTParameterFromExpr(Info, VP->getSizeExpr());
         if (!NTTP)
           return TemplateDeductionResult::Success;
 
@@ -2217,8 +2302,8 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
           return Result;
 
         // Perform deduction on the vector size, if we can.
-        const NonTypeTemplateParmDecl *NTTP =
-            getDeducedParameterFromExpr(Info, VP->getSizeExpr());
+        NonTypeOrVarTemplateParmDecl NTTP =
+            getDeducedNTTParameterFromExpr(Info, VP->getSizeExpr());
         if (!NTTP)
           return TemplateDeductionResult::Success;
 
@@ -2246,8 +2331,8 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
           return Result;
 
         // Perform deduction on the vector size, if we can.
-        const NonTypeTemplateParmDecl *NTTP =
-            getDeducedParameterFromExpr(Info, VP->getSizeExpr());
+        NonTypeOrVarTemplateParmDecl NTTP =
+            getDeducedNTTParameterFromExpr(Info, VP->getSizeExpr());
         if (!NTTP)
           return TemplateDeductionResult::Success;
 
@@ -2271,8 +2356,8 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
           return Result;
 
         // Perform deduction on the vector size, if we can.
-        const NonTypeTemplateParmDecl *NTTP =
-            getDeducedParameterFromExpr(Info, VP->getSizeExpr());
+        NonTypeOrVarTemplateParmDecl NTTP =
+            getDeducedNTTParameterFromExpr(Info, VP->getSizeExpr());
         if (!NTTP)
           return TemplateDeductionResult::Success;
 
@@ -2348,8 +2433,8 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
               return TemplateDeductionResult::NonDeducedMismatch;
             }
 
-            const NonTypeTemplateParmDecl *NTTP =
-                getDeducedParameterFromExpr(Info, ParamExpr);
+            NonTypeOrVarTemplateParmDecl NTTP =
+                getDeducedNTTParameterFromExpr(Info, ParamExpr);
             if (!NTTP)
               return TemplateDeductionResult::Success;
 
@@ -2395,8 +2480,8 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
           return Result;
 
         // Perform deduction on the address space, if we can.
-        const NonTypeTemplateParmDecl *NTTP =
-            getDeducedParameterFromExpr(Info, ASP->getAddrSpaceExpr());
+        NonTypeOrVarTemplateParmDecl NTTP =
+            getDeducedNTTParameterFromExpr(Info, ASP->getAddrSpaceExpr());
         if (!NTTP)
           return TemplateDeductionResult::Success;
 
@@ -2420,8 +2505,8 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
           return Result;
 
         // Perform deduction on the address space, if we can.
-        const NonTypeTemplateParmDecl *NTTP =
-            getDeducedParameterFromExpr(Info, ASP->getAddrSpaceExpr());
+        NonTypeOrVarTemplateParmDecl NTTP =
+            getDeducedNTTParameterFromExpr(Info, ASP->getAddrSpaceExpr());
         if (!NTTP)
           return TemplateDeductionResult::Success;
 
@@ -2440,8 +2525,8 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
         if (IP->isUnsigned() != IA->isUnsigned())
           return TemplateDeductionResult::NonDeducedMismatch;
 
-        const NonTypeTemplateParmDecl *NTTP =
-            getDeducedParameterFromExpr(Info, IP->getNumBitsExpr());
+        NonTypeOrVarTemplateParmDecl NTTP =
+            getDeducedNTTParameterFromExpr(Info, IP->getNumBitsExpr());
         if (!NTTP)
           return TemplateDeductionResult::Success;
 
@@ -2573,8 +2658,8 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
     return TemplateDeductionResult::NonDeducedMismatch;
 
   case TemplateArgument::Expression:
-    if (const NonTypeTemplateParmDecl *NTTP =
-            getDeducedParameterFromExpr(Info, P.getAsExpr())) {
+    if (NonTypeOrVarTemplateParmDecl NTTP =
+            getDeducedNTTParameterFromExpr(Info, P.getAsExpr())) {
       switch (A.getKind()) {
       case TemplateArgument::Expression: {
         const Expr *E = A.getAsExpr();
@@ -2628,7 +2713,6 @@ DeduceTemplateArguments(Sema &S, TemplateParameterList *TemplateParams,
       }
       llvm_unreachable("Unknown template argument kind");
     }
-
     // Can't deduce anything, but that's okay.
     return TemplateDeductionResult::Success;
   case TemplateArgument::Pack:
@@ -4392,8 +4476,8 @@ static TemplateDeductionResult DeduceFromInitializerList(
   //   from the length of the initializer list.
   if (auto *DependentArrTy = dyn_cast_or_null<DependentSizedArrayType>(ArrTy)) {
     // Determine the array bound is something we can deduce.
-    if (const NonTypeTemplateParmDecl *NTTP =
-            getDeducedParameterFromExpr(Info, DependentArrTy->getSizeExpr())) {
+    if (NonTypeOrVarTemplateParmDecl NTTP = getDeducedNTTParameterFromExpr(
+            Info, DependentArrTy->getSizeExpr())) {
       // We can perform template argument deduction for the given non-type
       // template parameter.
       // C++ [temp.deduct.type]p13:
@@ -5098,7 +5182,7 @@ static bool CheckDeducedPlaceholderConstraints(Sema &S, const AutoType &Type,
                                                AutoTypeLoc TypeLoc,
                                                QualType Deduced) {
   ConstraintSatisfaction Satisfaction;
-  ConceptDecl *Concept = Type.getTypeConstraintConcept();
+  ConceptDecl *Concept = cast<ConceptDecl>(Type.getTypeConstraintConcept());
   TemplateArgumentListInfo TemplateArgs(TypeLoc.getLAngleLoc(),
                                         TypeLoc.getRAngleLoc());
   TemplateArgs.addArgument(
@@ -6654,6 +6738,19 @@ struct MarkUsedTemplateParameterVisitor : DynamicRecursiveASTVisitor {
         Used[NTTP->getIndex()] = true;
     return true;
   }
+
+  /*bool VisitUnresolvedLookupExpr(UnresolvedLookupExpr *ULE) {
+    if (ULE->isConceptReference() || ULE->isVarDeclReference()) {
+      if (auto *TTP = ULE->getTemplateTemplateDecl()) {
+        if (TTP->getDepth() == Depth)
+          Used[TTP->getIndex()] = true;
+      }
+      for (auto &TLoc : ULE->template_arguments())
+        RecursiveASTVisitor<MarkUsedTemplateParameterVisitor>::
+            TraverseTemplateArgumentLoc(TLoc);
+    }
+    return true;
+  }*/
 };
 }
 
@@ -6675,17 +6772,28 @@ MarkUsedTemplateParameters(ASTContext &Ctx,
   if (const PackExpansionExpr *Expansion = dyn_cast<PackExpansionExpr>(E))
     E = Expansion->getPattern();
 
-  const NonTypeTemplateParmDecl *NTTP = getDeducedParameterFromExpr(E, Depth);
-  if (!NTTP)
+  E = unwrapExpressionForDeduction(E);
+  /*if (const auto *ULE = dyn_cast<UnresolvedLookupExpr>(E);
+      ULE && (ULE->isConceptReference() || ULE->isVarDeclReference())) {
+    if (const auto *TTP = ULE->getTemplateTemplateDecl())
+      Used[TTP->getIndex()] = true;
+    for (auto &TLoc : ULE->template_arguments())
+      MarkUsedTemplateParameters(Ctx, TLoc.getArgument(), OnlyDeduced, Depth,
+                                 Used);
     return;
+  }*/
 
-  if (NTTP->getDepth() == Depth)
-    Used[NTTP->getIndex()] = true;
+  const NonTypeOrVarTemplateParmDecl NTTP =
+      getDeducedNTTParameterFromExpr(E, Depth);
+  if (!NTTP)
+    return;
+  if (NTTP.getDepth() == Depth)
+    Used[NTTP.getIndex()] = true;
 
   // In C++17 mode, additional arguments may be deduced from the type of a
   // non-type argument.
   if (Ctx.getLangOpts().CPlusPlus17)
-    MarkUsedTemplateParameters(Ctx, NTTP->getType(), OnlyDeduced, Depth, Used);
+    MarkUsedTemplateParameters(Ctx, NTTP.getType(), OnlyDeduced, Depth, Used);
 }
 
 /// Mark the template parameters that are used by the given
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 20bac0e56b195..7a4ff53573ebf 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1608,6 +1608,9 @@ namespace {
                           QualType ObjectType = QualType(),
                           NamedDecl *FirstQualifierInScope = nullptr,
                           bool AllowInjectedClassName = false);
+    TemplateArgument
+    TransformNamedTemplateTemplateArgument(CXXScopeSpec &SS, TemplateName Name,
+                                           SourceLocation NameLoc);
 
     const AnnotateAttr *TransformAnnotateAttr(const AnnotateAttr *AA);
     const CXXAssumeAttr *TransformCXXAssumeAttr(const CXXAssumeAttr *AA);
@@ -1899,8 +1902,7 @@ namespace {
 
   private:
     ExprResult
-    transformNonTypeTemplateParmRef(Decl *AssociatedDecl,
-                                    const NonTypeTemplateParmDecl *parm,
+    transformNonTypeTemplateParmRef(Decl *AssociatedDecl, const NamedDecl *parm,
                                     SourceLocation loc, TemplateArgument arg,
                                     UnsignedOrNone PackIndex, bool Final);
   };
@@ -2187,6 +2189,27 @@ TemplateName TemplateInstantiator::TransformTemplateName(
                                           AllowInjectedClassName);
 }
 
+TemplateArgument TemplateInstantiator::TransformNamedTemplateTemplateArgument(
+    CXXScopeSpec &SS, TemplateName Name, SourceLocation NameLoc) {
+  if (TemplateTemplateParmDecl *TTP =
+          dyn_cast_or_null<TemplateTemplateParmDecl>(
+              Name.getAsTemplateDecl())) {
+    if (TTP->getDepth() < TemplateArgs.getNumLevels()) {
+      // If the corresponding template argument is NULL or non-existent, it's
+      // because we are performing instantiation from explicitly-specified
+      // template arguments in a function template, but there were some
+      // arguments left unspecified.
+      if (!TemplateArgs.hasTemplateArgument(TTP->getDepth(),
+                                            TTP->getPosition()))
+        return TemplateArgument(Name);
+    }
+  }
+  TemplateName TN = getDerived().TransformTemplateName(SS, Name, NameLoc);
+  if (!TN.isNull())
+    return TN;
+  return TemplateArgument();
+}
+
 ExprResult
 TemplateInstantiator::TransformPredefinedExpr(PredefinedExpr *E) {
   if (!E->isTypeDependent())
@@ -2343,22 +2366,25 @@ TemplateInstantiator::TransformOpenACCRoutineDeclAttr(
 }
 
 ExprResult TemplateInstantiator::transformNonTypeTemplateParmRef(
-    Decl *AssociatedDecl, const NonTypeTemplateParmDecl *parm,
-    SourceLocation loc, TemplateArgument arg, UnsignedOrNone PackIndex,
-    bool Final) {
+    Decl *AssociatedDecl, const NamedDecl *parm, SourceLocation loc,
+    TemplateArgument arg, UnsignedOrNone PackIndex, bool Final) {
   ExprResult result;
 
   // Determine the substituted parameter type. We can usually infer this from
   // the template argument, but not always.
   auto SubstParamType = [&] {
-    QualType T;
-    if (parm->isExpandedParameterPack())
-      T = parm->getExpansionType(*SemaRef.ArgPackSubstIndex);
-    else
-      T = parm->getType();
-    if (parm->isParameterPack() && isa<PackExpansionType>(T))
-      T = cast<PackExpansionType>(T)->getPattern();
-    return SemaRef.SubstType(T, TemplateArgs, loc, parm->getDeclName());
+    if (auto NTTP = dyn_cast<NonTypeTemplateParmDecl>(parm)) {
+      QualType T;
+      if (NTTP->isExpandedParameterPack())
+        T = NTTP->getExpansionType(*SemaRef.ArgPackSubstIndex);
+      else
+        T = NTTP->getType();
+      if (parm->isParameterPack() && isa<PackExpansionType>(T))
+        T = cast<PackExpansionType>(T)->getPattern();
+      return SemaRef.SubstType(T, TemplateArgs, loc, parm->getDeclName());
+    }
+    return SemaRef.SubstType(arg.getAsExpr()->getType(), TemplateArgs, loc,
+                             parm->getDeclName());
   };
 
   bool refParam = false;
@@ -2413,7 +2439,9 @@ ExprResult TemplateInstantiator::transformNonTypeTemplateParmRef(
   Expr *resultExpr = result.get();
   return new (SemaRef.Context) SubstNonTypeTemplateParmExpr(
       resultExpr->getType(), resultExpr->getValueKind(), loc, resultExpr,
-      AssociatedDecl, parm->getIndex(), PackIndex, refParam, Final);
+      AssociatedDecl,
+      clang::getDepthAndIndex(const_cast<NamedDecl *>(parm)).second, PackIndex,
+      refParam, Final);
 }
 
 ExprResult
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index e2c3cdcd536bc..509222e135e21 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -3784,13 +3784,13 @@ TemplateDeclInstantiator::VisitTemplateTemplateParmDecl(
     Param = TemplateTemplateParmDecl::Create(
         SemaRef.Context, Owner, D->getLocation(),
         D->getDepth() - TemplateArgs.getNumSubstitutedLevels(),
-        D->getPosition(), D->getIdentifier(), D->wasDeclaredWithTypename(),
-        InstParams, ExpandedParams);
+        D->getPosition(), D->getIdentifier(), D->kind(),
+        D->wasDeclaredWithTypename(), InstParams, ExpandedParams);
   else
     Param = TemplateTemplateParmDecl::Create(
         SemaRef.Context, Owner, D->getLocation(),
         D->getDepth() - TemplateArgs.getNumSubstitutedLevels(),
-        D->getPosition(), D->isParameterPack(), D->getIdentifier(),
+        D->getPosition(), D->isParameterPack(), D->getIdentifier(), D->kind(),
         D->wasDeclaredWithTypename(), InstParams);
   if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) {
     NestedNameSpecifierLoc QualifierLoc =
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index 572dbf2e7393f..8e44accc0e3d5 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -310,6 +310,16 @@ class CollectUnexpandedParameterPacksVisitor
       return DynamicRecursiveASTVisitor::TraverseLambdaCapture(Lambda, C, Init);
     }
 
+    bool TraverseUnresolvedLookupExpr(UnresolvedLookupExpr *E) override {
+      if (E->getNumDecls() == 1) {
+        NamedDecl *ND = *E->decls_begin();
+        if (auto *TTP = llvm::dyn_cast<TemplateTemplateParmDecl>(ND);
+            TTP && TTP->isParameterPack())
+          addUnexpanded(ND, E->getBeginLoc());
+      }
+      return DynamicRecursiveASTVisitor::TraverseUnresolvedLookupExpr(E);
+    }
+
 #ifndef NDEBUG
     bool TraverseFunctionParmPackExpr(FunctionParmPackExpr *) override {
       ContainsIntermediatePacks = true;
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 7dbd4bb0ed125..1289bede3f192 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -1306,12 +1306,12 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) {
                       ? AutoTypeKeyword::DecltypeAuto
                       : AutoTypeKeyword::Auto;
 
-    ConceptDecl *TypeConstraintConcept = nullptr;
+    TemplateDecl *TypeConstraintConcept = nullptr;
     llvm::SmallVector<TemplateArgument, 8> TemplateArgs;
     if (DS.isConstrainedAuto()) {
       if (TemplateIdAnnotation *TemplateId = DS.getRepAsTemplateId()) {
         TypeConstraintConcept =
-            cast<ConceptDecl>(TemplateId->Template.get().getAsTemplateDecl());
+            cast<TemplateDecl>(TemplateId->Template.get().getAsTemplateDecl());
         TemplateArgumentListInfo TemplateArgsInfo;
         TemplateArgsInfo.setLAngleLoc(TemplateId->LAngleLoc);
         TemplateArgsInfo.setRAngleLoc(TemplateId->RAngleLoc);
@@ -3090,15 +3090,13 @@ InventTemplateParameter(TypeProcessingState &state, QualType T,
       if (!Invalid) {
         UsingShadowDecl *USD =
             TemplateId->Template.get().getAsUsingShadowDecl();
-        auto *CD =
-            cast<ConceptDecl>(TemplateId->Template.get().getAsTemplateDecl());
+        TemplateDecl *CD = TemplateId->Template.get().getAsTemplateDecl();
         S.AttachTypeConstraint(
             D.getDeclSpec().getTypeSpecScope().getWithLocInContext(S.Context),
             DeclarationNameInfo(DeclarationName(TemplateId->Name),
                                 TemplateId->TemplateNameLoc),
             CD,
-            /*FoundDecl=*/
-            USD ? cast<NamedDecl>(USD) : CD,
+            /*FoundDecl=*/USD ? cast<NamedDecl>(USD) : CD,
             TemplateId->LAngleLoc.isValid() ? &TemplateArgsInfo : nullptr,
             InventedTemplateParam, D.getEllipsisLoc());
       }
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index c7428d1a02345..d3c90b0508234 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -610,6 +610,10 @@ class TreeTransform {
                                  TemplateArgumentLoc &Output,
                                  bool Uneval = false);
 
+  TemplateArgument
+  TransformNamedTemplateTemplateArgument(CXXScopeSpec &SS, TemplateName Name,
+                                         SourceLocation NameLoc);
+
   /// Transform the given set of template arguments.
   ///
   /// By default, this operation transforms all of the template arguments
@@ -656,6 +660,12 @@ class TreeTransform {
                                   TemplateArgumentListInfo &Outputs,
                                   bool Uneval = false);
 
+  template <typename InputIterator>
+  bool TransformConceptTemplateArguments(InputIterator First,
+                                         InputIterator Last,
+                                         TemplateArgumentListInfo &Outputs,
+                                         bool Uneval = false);
+
   /// Fakes up a TemplateArgumentLoc for a given TemplateArgument.
   void InventTemplateArgumentLoc(const TemplateArgument &Arg,
                                  TemplateArgumentLoc &ArgLoc);
@@ -4843,6 +4853,15 @@ TreeTransform<Derived>::TransformTemplateName(CXXScopeSpec &SS,
   llvm_unreachable("overloaded function decl survived to here");
 }
 
+template <typename Derived>
+TemplateArgument TreeTransform<Derived>::TransformNamedTemplateTemplateArgument(
+    CXXScopeSpec &SS, TemplateName Name, SourceLocation NameLoc) {
+  TemplateName TN = getDerived().TransformTemplateName(SS, Name, NameLoc);
+  if (TN.isNull())
+    return TemplateArgument();
+  return TemplateArgument(TN);
+}
+
 template<typename Derived>
 void TreeTransform<Derived>::InventTemplateArgumentLoc(
                                          const TemplateArgument &Arg,
@@ -4927,13 +4946,13 @@ bool TreeTransform<Derived>::TransformTemplateArgument(
 
     CXXScopeSpec SS;
     SS.Adopt(QualifierLoc);
-    TemplateName Template = getDerived().TransformTemplateName(
+
+    TemplateArgument Out = getDerived().TransformNamedTemplateTemplateArgument(
         SS, Arg.getAsTemplate(), Input.getTemplateNameLoc());
-    if (Template.isNull())
+    if (Out.isNull())
       return true;
-
-    Output = TemplateArgumentLoc(SemaRef.Context, TemplateArgument(Template),
-                                 QualifierLoc, Input.getTemplateNameLoc());
+    Output = TemplateArgumentLoc(SemaRef.Context, Out, QualifierLoc,
+                                 Input.getTemplateNameLoc());
     return false;
   }
 
@@ -5144,6 +5163,56 @@ bool TreeTransform<Derived>::TransformTemplateArguments(
 
 }
 
+template <typename Derived>
+template <typename InputIterator>
+bool TreeTransform<Derived>::TransformConceptTemplateArguments(
+    InputIterator First, InputIterator Last, TemplateArgumentListInfo &Outputs,
+    bool Uneval) {
+
+  auto isConcept = [](const TemplateArgument &Arg) {
+    bool isConcept = false;
+    if (Arg.getKind() == TemplateArgument::Template)
+      if (auto *TTP = dyn_cast_if_present<TemplateTemplateParmDecl>(
+              Arg.getAsTemplate().getAsTemplateDecl()))
+        isConcept = TTP->kind() == TNK_Concept_template;
+    return isConcept;
+  };
+
+  for (; First != Last; ++First) {
+    TemplateArgumentLoc Out;
+    TemplateArgumentLoc In = *First;
+
+    if (In.getArgument().getKind() == TemplateArgument::Pack) {
+      // if(In.getArgument().pack_size() == 00  ||
+      // !isConcept(In.getArgument().pack_elements()[0])) {
+      //   Outputs.addArgument(In);
+      //   continue;
+      // }
+      typedef TemplateArgumentLocInventIterator<Derived,
+                                                TemplateArgument::pack_iterator>
+          PackLocIterator;
+      if (TransformConceptTemplateArguments(
+              PackLocIterator(*this, In.getArgument().pack_begin()),
+              PackLocIterator(*this, In.getArgument().pack_end()), Outputs,
+              Uneval))
+        return true;
+      continue;
+    }
+
+    if (!isConcept(In.getArgument())) {
+      Outputs.addArgument(In);
+      continue;
+    }
+
+    if (getDerived().TransformTemplateArgument(In, Out, Uneval))
+      return true;
+
+    Outputs.addArgument(Out);
+  }
+
+  return false;
+}
+
 //===----------------------------------------------------------------------===//
 // Type transformation
 //===----------------------------------------------------------------------===//
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index bd84a9741d01b..ef526ae92a43c 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -2741,6 +2741,7 @@ void ASTDeclReader::VisitNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D) {
 
 void ASTDeclReader::VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D) {
   VisitTemplateDecl(D);
+  D->ParameterKind = static_cast<TemplateNameKind>(Record.readInt());
   D->setDeclaredWithTypename(Record.readBool());
   // TemplateParmPosition.
   D->setDepth(Record.readInt());
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 0166e493bf03f..3f37dfbc3dea9 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -2086,13 +2086,12 @@ void ASTStmtReader::VisitOverloadExpr(OverloadExpr *E) {
   assert((E->hasTemplateKWAndArgsInfo() == HasTemplateKWAndArgsInfo) &&
          "Wrong HasTemplateKWAndArgsInfo!");
 
+  unsigned NumTemplateArgs = 0;
   if (HasTemplateKWAndArgsInfo) {
-    unsigned NumTemplateArgs = Record.readInt();
+    NumTemplateArgs = Record.readInt();
     ReadTemplateKWAndArgsInfo(*E->getTrailingASTTemplateKWAndArgsInfo(),
                               E->getTrailingTemplateArgumentLoc(),
                               NumTemplateArgs);
-    assert((E->getNumTemplateArgs() == NumTemplateArgs) &&
-           "Wrong NumTemplateArgs!");
   }
 
   UnresolvedSet<8> Decls;
@@ -2108,6 +2107,9 @@ void ASTStmtReader::VisitOverloadExpr(OverloadExpr *E) {
     Results[I] = (Iter + I).getPair();
   }
 
+  assert((E->getNumTemplateArgs() == NumTemplateArgs) &&
+         "Wrong NumTemplateArgs!");
+
   E->NameInfo = Record.readDeclarationNameInfo();
   E->QualifierLoc = Record.readNestedNameSpecifierLoc();
 }
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index e414910469a64..2881ff8cf5ecf 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -2149,6 +2149,7 @@ void ASTDeclWriter::VisitTemplateTemplateParmDecl(TemplateTemplateParmDecl *D) {
     Record.push_back(D->getNumExpansionTemplateParameters());
 
   VisitTemplateDecl(D);
+  Record.push_back(D->kind());
   Record.push_back(D->wasDeclaredWithTypename());
   // TemplateParmPosition.
   Record.push_back(D->getDepth());
diff --git a/clang/test/CodeGenCXX/mangle-concept.cpp b/clang/test/CodeGenCXX/mangle-concept.cpp
index aa0940c35b237..63e819bb4f8f0 100644
--- a/clang/test/CodeGenCXX/mangle-concept.cpp
+++ b/clang/test/CodeGenCXX/mangle-concept.cpp
@@ -6,7 +6,8 @@
 namespace test1 {
 template <bool> struct S {};
 template <typename> concept C = true;
-template <typename T = int> S<C<T>> f0() { return S<C<T>>{}; }
+template <typename T = int>
+S<C<T>> f0() { return S<C<T>>{}; }
 template S<C<int>> f0<>();
 // CHECK: @_ZN5test12f0IiEENS_1SIX1CIT_EEEEv(
 // CLANG17: @_ZN5test12f0IiEENS_1SIL_ZNS_1CIT_EEEEEv(
diff --git a/clang/test/Parser/cxx-template-template-recovery.cpp b/clang/test/Parser/cxx-template-template-recovery.cpp
index 5700b160cd364..61ef82dd06ec4 100644
--- a/clang/test/Parser/cxx-template-template-recovery.cpp
+++ b/clang/test/Parser/cxx-template-template-recovery.cpp
@@ -22,18 +22,18 @@ auto V3 = true; // #V3
 template <template <typename T> typename C>
 constexpr bool test = true;
 
-static_assert(test<a::C1>); // expected-error {{too few template arguments for concept 'C1'}} \
+static_assert(test<a::C1>); // expected-error {{template argument does not refer to a class or alias template, or template template parameter}} \
                             // expected-note@#C1 {{here}}
-static_assert(test<a::b::C2>); // expected-error {{too few template arguments for concept 'C2'}} \
+static_assert(test<a::b::C2>); // expected-error {{template argument does not refer to a class or alias template, or template template parameter}} \
                                // expected-note@#C2 {{here}}
-static_assert(test<C3>); // expected-error {{too few template arguments for concept 'C3'}} \
+static_assert(test<C3>); // expected-error {{template argument does not refer to a class or alias template, or template template parameter}} \
                          // expected-note@#C3 {{here}}
 
-static_assert(test<a::V1>); // expected-error {{use of variable template 'a::V1' requires template arguments}} \
+static_assert(test<a::V1>); // expected-error {{template argument does not refer to a class or alias template, or template template parameter}} \
                             // expected-note@#V1 {{here}}
-static_assert(test<a::b::V2>); // expected-error {{use of variable template 'a::b::V2' requires template arguments}} \
+static_assert(test<a::b::V2>); // expected-error {{template argument does not refer to a class or alias template, or template template parameter}} \
                             // expected-note@#V2 {{here}}
-static_assert(test<V3>); // expected-error {{use of variable template 'V3' requires template arguments}} \
+static_assert(test<V3>); // expected-error {{template argument does not refer to a class or alias template, or template template parameter}} \
                          // expected-note@#V3 {{here}}
 
 
diff --git a/clang/test/Parser/cxx2a-concept-declaration.cpp b/clang/test/Parser/cxx2a-concept-declaration.cpp
index 0a7af847112de..fea6084d58f3a 100644
--- a/clang/test/Parser/cxx2a-concept-declaration.cpp
+++ b/clang/test/Parser/cxx2a-concept-declaration.cpp
@@ -9,11 +9,6 @@ template<concept T> concept D1 = true;
 // expected-error at -1{{expected template parameter}}
 // expected-error at -2{{concept template parameter list must have at least one parameter; explicit specialization of concepts is not allowed}}
 
-template<template<typename> concept T> concept D2 = true;
-// expected-error at -1{{expected identifier}}
-// expected-error at -2{{template template parameter requires 'class' or 'typename' after the parameter list}}
-// expected-error at -3{{concept template parameter list must have at least one parameter; explicit specialization of concepts is not allowed}}
-
 struct S1 {
   template<typename T> concept C1 = true; // expected-error {{concept declarations may only appear in global or namespace scope}}
 };
diff --git a/clang/test/Parser/cxx2c-template-template-param.cpp b/clang/test/Parser/cxx2c-template-template-param.cpp
new file mode 100644
index 0000000000000..aa9e2393cf106
--- /dev/null
+++ b/clang/test/Parser/cxx2c-template-template-param.cpp
@@ -0,0 +1,79 @@
+// RUN:  %clang_cc1 -std=c++2b -verify %s
+
+template<template<typename> auto Var>
+struct A{};
+template<template<auto> auto Var>
+struct B{};
+template<template<typename> auto Var>
+struct C{};
+template<template<typename> concept C>
+struct D{};
+template<template<auto> concept C>
+struct E{};
+
+template<template<typename> auto Var>
+int V1;
+template<template<auto> auto Var>
+int V2;
+template<template<typename> auto Var>
+int V3;
+template<template<typename> concept C>
+int V4;
+template<template<auto> concept C>
+int V5;
+
+namespace packs {
+
+template<template<typename> auto... Var>
+struct A{};
+template<template<auto> auto... Var>
+struct B{};
+template<template<typename> auto... Var>
+struct C{};
+template<template<typename> concept... C>
+struct D{};
+template<template<auto> concept... C>
+struct E{};
+
+template<template<typename> auto... Var>
+int V1;
+template<template<auto> auto...  Var>
+int V2;
+template<template<typename> auto...  Var>
+int V3;
+template<template<typename> concept... C>
+int V4;
+template<template<auto> concept... C>
+int V5;
+
+}
+
+namespace concepts {
+template<template<auto> concept...>
+struct A{};
+template<template<auto> concept... C>
+struct B{};
+template<template<auto> concept& C> // expected-error{{expected identifier}} \
+                                    // expected-error {{in declaration of struct 'C'}}
+struct C{};
+}
+
+namespace vars {
+template<template<auto> auto...>
+struct A{};
+template<template<auto> auto & C> // expected-error {{expected identifier}} \
+                                  // expected-error {{extraneous 'template<>'}}
+struct B{};
+template<template<auto> const auto> // expected-error {{expected identifier}} \
+                                    // expected-error {{extraneous 'template<>'}}
+struct C{};
+}
+
+namespace errors {
+template<concept> // expected-error {{expected template parameter}} \
+                  // expected-error {{extraneous 'template<>' in declaration of struct 'A'}}
+struct A{};
+template<template<concept> auto> // expected-error {{expected template parameter}} \
+                                 // expected-error {{template template parameter must have its own template parameters}}
+struct B{};
+}
diff --git a/clang/test/SemaCXX/cxx2c-template-template-param.cpp b/clang/test/SemaCXX/cxx2c-template-template-param.cpp
new file mode 100644
index 0000000000000..b96ce733eabe0
--- /dev/null
+++ b/clang/test/SemaCXX/cxx2c-template-template-param.cpp
@@ -0,0 +1,372 @@
+// RUN:  %clang_cc1 -std=c++2b -verify %s
+
+namespace Errors {
+
+template <template<typename T> auto>
+struct S1;
+template <template<auto T> auto>
+struct S2;
+template <template<typename T> concept>
+struct S3;
+template <template<auto T> concept>
+struct S4;
+int a;
+
+template <typename T>
+concept C = true; // expected-note 2{{template argument refers to a concept 'C', here}}
+template <typename T>
+auto Var = 0; // expected-note 2{{template argument refers to a variable template 'Var', here}}
+
+S1<1> t1; // expected-error {{template argument for template template parameter must be a variable template}}
+S1<a> t2; // expected-error {{template argument for template template parameter must be a variable template}}
+S1<int> t3; // expected-error {{template argument for template template parameter must be a variable template}}
+S1<C> t4; // expected-error {{template argument does not refer to a variable template, or template template parameter}}
+S2<1> t5; // expected-error {{template argument for template template parameter must be a variable template}}
+S2<a> t6; // expected-error {{template argument for template template parameter must be a variable template}}
+S2<int> t7; // expected-error {{template argument for template template parameter must be a variable template}}
+S2<C> t8; // expected-error {{template argument does not refer to a variable template, or template template parameter}}
+S3<1> t9; // expected-error {{template argument for template template parameter must be a concept}}
+S3<a> t10; // expected-error {{template argument for template template parameter must be a concept}}
+S3<int> t11; // expected-error {{template argument for template template parameter must be a concept}}
+S3<Var> t12; // expected-error {{template argument does not refer to a concept, or template template parameter}}
+S4<1> t13; // expected-error {{template argument for template template parameter must be a concept}}
+S4<a> t14; // expected-error {{template argument for template template parameter must be a concept}}
+S4<int> t15; // expected-error {{template argument for template template parameter must be a concept}}
+S4<Var> t16; // expected-error {{template argument does not refer to a concept, or template template parameter}}
+
+}
+
+template <template<typename T> auto V> // expected-note {{previous template template parameter is here}}
+struct S1 {
+    static_assert(V<int> == 42);
+    static_assert(V<const int> == 84);
+    static_assert(V<double> == 0);
+};
+template <template<auto T> auto V>  // expected-note {{previous template template parameter is here}}
+struct S2 {
+    static_assert(V<0> == 1);
+    static_assert(V<1> == 0);
+};
+template <template<typename T> concept C > // expected-note {{previous template template parameter is here}}
+struct S3 {
+    static_assert(C<int>);
+};
+template <template<auto> concept C> // expected-note {{previous template template parameter is here}}
+struct S4 {
+    static_assert(C<0>);
+};
+
+template <typename T> // expected-note {{template parameter has a different kind in template argument}}
+concept C = true;
+
+template <auto I> // expected-note {{template parameter has a different kind in template argument}}
+concept CI = true;
+
+template <typename T> // expected-note {{template parameter has a different kind in template argument}}
+constexpr auto Var = 42;
+template <typename T>
+constexpr auto Var<const T> = 84;
+template <>
+constexpr auto Var<double> = 0;
+
+template <auto N> // expected-note {{template parameter has a different kind in template argument}}
+constexpr auto Var2 = 0;
+template <auto N>
+requires (N%2 == 0)
+constexpr auto Var2<N> = 1;
+
+void test () {
+    S1<Var2> sE; // expected-error {{template template argument has different template parameters than its corresponding template template parameter}}
+    S2<Var>  sE; // expected-error {{template template argument has different template parameters than its corresponding template template parameter}}
+    S1<Var> s1;
+    S2<Var2> s2;
+    S3<C> s3;
+    S4<C> sE; // expected-error {{template template argument has different template parameters than its corresponding template template parameter}}
+    S4<CI> s4;
+    S3<CI> sE; // expected-error {{template template argument has different template parameters than its corresponding template template parameter}}
+}
+
+namespace subsumption {
+
+template <typename T>
+concept A = true;
+
+template <typename T>
+concept B = A<T> && true;
+
+template <typename T>
+concept C = true;
+
+template <typename T>
+concept D = C<T> && true;
+
+template <typename ABC, template <typename> concept... C>
+concept Apply = (C<ABC> && ...);
+
+constexpr int f(Apply<A, C> auto) {return 1;}
+constexpr int f(Apply<B, D> auto) {return 2;}
+
+int test() {
+   static_assert(f(1) == 2);
+}
+
+}
+
+namespace template_type_constraints {
+
+
+template <typename T>
+concept Unary = true;
+template <typename T, typename = int>
+concept BinaryDefaulted = true;
+
+template <typename T>
+concept UnaryFalse = false; // expected-note 3{{because 'false' evaluated to false}}
+template <typename T, typename = int>
+concept BinaryDefaultedFalse = false;
+
+template <template <typename...> concept C, typename T>
+struct S {
+    template <C TT> // expected-note {{because 'int' does not satisfy 'UnaryFalse'}}
+    void f(TT); // expected-note {{ignored}}
+    void g(C auto); // expected-note {{ignored}} \
+                    // expected-note {{because 'int' does not satisfy 'UnaryFalse'}}
+
+    auto h() -> C auto { // expected-error {{deduced type 'int' does not satisfy 'UnaryFalse'}}
+        return 0;
+    };
+
+    void test() {
+        C auto a = 0;
+    }
+};
+
+template <template <typename...> concept C, typename T>
+struct SArg {
+    template <C<int> TT>
+    void f(TT);
+    void g(C<int> auto);
+
+    auto h() -> C<int> auto {
+        return 0;
+    };
+    void test() {
+        C<int> auto a = 0;
+    }
+};
+
+void test() {
+    S<Unary, int> s;
+    s.f(0);
+    s.g(0);
+    s.h();
+    S<BinaryDefaulted, int> s2;
+    s2.f(0);
+    s2.g(0);
+    s2.h();
+
+    SArg<BinaryDefaulted, int> s3;
+    s3.f(0);
+    s3.g(0);
+    s3.h();
+}
+
+void test_errors() {
+    S<UnaryFalse, int> s;
+    s.f(0); // expected-error {{no matching member function for call to 'f'}}
+    s.g(0); // expected-error {{no matching member function for call to 'g'}}
+    s.h(); // expected-note {{in instantiation of member function 'template_type_constraints::S<template_type_constraints::UnaryFalse, int>::h'}}
+}
+
+}
+
+template <typename T>
+concept Unary = true;
+template <typename T, typename = int>
+concept BinaryDefaulted = true;
+
+template <typename T>
+concept UnaryFalse = false; // expected-note 3{{because 'false' evaluated to false}}
+template <typename T, typename = int>
+concept BinaryDefaultedFalse = false;
+
+template <template <typename...> concept C, typename T>
+struct S {
+    template <C TT> // expected-note {{because 'int' does not satisfy 'UnaryFalse'}}
+    void f(TT); // expected-note {{ignored}}
+    void g(C auto); // expected-note {{ignored}} \
+                    // expected-note {{because 'int' does not satisfy 'UnaryFalse'}}
+
+    auto h() -> C auto { // expected-error {{deduced type 'int' does not satisfy 'UnaryFalse'}}
+        return 0;
+    };
+
+    void test() {
+        C auto a = 0;
+    }
+};
+
+template <template <typename...> concept C, typename T>
+struct SArg {
+    template <C<int> TT>
+    void f(TT);
+    void g(C<int> auto);
+
+    auto h() -> C<int> auto {
+        return 0;
+    };
+    void test() {
+        C<int> auto a = 0;
+    }
+};
+
+void test_args() {
+    S<Unary, int> s;
+    s.f(0);
+    s.g(0);
+    s.h();
+    S<BinaryDefaulted, int> s2;
+    s2.f(0);
+    s2.g(0);
+    s2.h();
+
+    SArg<BinaryDefaulted, int> s3;
+    s3.f(0);
+    s3.g(0);
+    s3.h();
+}
+
+void test_errors() {
+    S<UnaryFalse, int> s;
+    s.f(0); // expected-error {{no matching member function for call to 'f'}}
+    s.g(0); // expected-error {{no matching member function for call to 'g'}}
+    s.h(); // expected-note {{in instantiation of member function 'S<UnaryFalse, int>::h'}}
+}
+
+namespace non_type {
+
+template <auto>
+concept Unary = true;
+
+template <template <auto> concept C>
+struct S {
+    template <C Foo> // expected-error {{concept named in type constraint is not a type concept}}
+    void f();
+    // FIXME, bad diagnostic
+    void g(C auto);  // expected-error{{concept named in type constraint is not a type concept}}
+    auto h() -> C auto {  // expected-error{{concept named in type constraint is not a type concept}}
+    }
+    void i() {
+        C auto a = 0;  // expected-error{{concept named in type constraint is not a type concept}}
+    }
+};
+
+}
+
+namespace default_args {
+
+template <typename T>
+concept Concept = false; // expected-note 2{{template argument refers to a concept 'Concept', here}} \
+                         // expected-note 2{{because 'false' evaluated to false}}
+
+template <typename T>
+constexpr auto Var = false; // expected-note 2{{template argument refers to a variable template 'Var', here}}
+
+template <typename T>
+struct Type; // expected-note 2{{template argument refers to a class template 'Type', here}}
+
+
+template <template <typename> auto = Concept> // expected-error {{template argument does not refer to a variable template, or template template parameter}}
+struct E1;
+
+template <template <typename> auto  = Type> // expected-error {{template argument does not refer to a variable template, or template template parameter}}
+struct E2;
+
+template <template <typename> typename = Concept> // expected-error {{template argument does not refer to a class or alias template, or template template parameter}}
+struct E3;
+
+template <template <typename> typename  = Var> // expected-error {{template argument does not refer to a class or alias template, or template template parameter}}
+struct E4;
+
+template <template <typename> concept  = Var> // expected-error {{template argument does not refer to a concept, or template template parameter}}
+struct E4;
+
+template <template <typename> concept  = Type> // expected-error {{template argument does not refer to a concept, or template template parameter}}
+struct E4;
+
+template <
+    template <typename> concept TConcept, // expected-note 2{{template argument refers to a concept 'TConcept', here}}
+    template <typename> auto TVar, // expected-note 2{{template argument refers to a variable template 'TVar', here}}
+    template <typename> typename TType // expected-note 2{{template argument refers to a class template 'TType', here}}
+>
+struct Nested {
+    template <template <typename> auto = TConcept> // expected-error {{template argument does not refer to a variable template, or template template parameter}}
+    struct E1;
+
+    template <template <typename> auto  = TType> // expected-error {{template argument does not refer to a variable template, or template template parameter}}
+    struct E2;
+
+    template <template <typename> typename = TConcept> // expected-error {{template argument does not refer to a class or alias template, or template template parameter}}
+    struct E3;
+
+    template <template <typename> typename  = TVar> // expected-error {{template argument does not refer to a class or alias template, or template template parameter}}
+    struct E4;
+
+    template <template <typename> concept  = TVar> // expected-error {{template argument does not refer to a concept, or template template parameter}}
+    struct E4;
+
+    template <template <typename> concept  = TType> // expected-error {{template argument does not refer to a concept, or template template parameter}}
+    struct E4;
+};
+
+
+template <template <typename> concept C = Concept>
+struct TestDefaultConcept {
+    template <template <typename> concept CC = C>
+    void f() {
+        static_assert(C<int>); // expected-error {{static assertion failed}} \
+                               // expected-note {{because 'int' does not satisfy 'Concept'}}
+        static_assert(CC<int>);  // expected-error {{static assertion failed}} \
+                                 // expected-note {{because 'int' does not satisfy 'Concept'}}
+    }
+};
+void do_test_concept() {
+    TestDefaultConcept<>{}.f(); // expected-note {{in instantiation}}
+}
+
+template <template <typename> auto V = Var>
+struct TestDefaultVar {
+    template <template <typename> auto VV = V>
+    void f() {
+        static_assert(V<int>); // expected-error {{static assertion failed}}
+        static_assert(VV<int>); // expected-error {{static assertion failed}}
+    }
+};
+void do_test_var() {
+    TestDefaultVar<>{}.f(); // expected-note {{in instantiation}}
+}
+
+}
+
+namespace TTPDependence {
+template <template <typename... > concept C>
+concept A = C<>;
+template <template <typename... > concept C>
+concept B = C<int>;
+
+template <template <typename... > auto Var>
+concept C = Var<>;
+template <template <typename... > auto Var>
+concept D = Var<int>;
+
+}
+
+namespace InvalidName {
+// FIXME corentin: improve diagnostics
+template <typename T, template <typename> concept C>
+concept A = C<T>; // expected-note {{here}}
+
+template <A<concept missing<int>> T> // expected-error {{expected expression}} \
+                                     // expected-error {{too few template arguments for concept 'A'}}
+auto f();
+}
diff --git a/clang/utils/TableGen/ClangBuiltinTemplatesEmitter.cpp b/clang/utils/TableGen/ClangBuiltinTemplatesEmitter.cpp
index 310ae89835e00..7c0cda044ee78 100644
--- a/clang/utils/TableGen/ClangBuiltinTemplatesEmitter.cpp
+++ b/clang/utils/TableGen/ClangBuiltinTemplatesEmitter.cpp
@@ -52,7 +52,8 @@ ParseTemplateParameterList(ParserState &PS,
       Code << TemplateCode << " auto *" << ParmName
            << " = TemplateTemplateParmDecl::Create(C, DC, SourceLocation(), "
            << PS.CurrentDepth << ", " << Position++
-           << ", /*ParameterPack=*/false, /*Id=*/nullptr, /*Typename=*/false, "
+           << ", /*ParameterPack=*/false, /*Id=*/nullptr, "
+              "/*Kind=*/TNK_Type_template, /*Typename=*/false, "
            << TPLName << ");\n";
     } else if (Arg->isSubClassOf("Class")) {
       Code << " auto *" << ParmName
diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index 256952dcf9085..8f0decbf3dcd7 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -1631,11 +1631,11 @@ TypeSystemClang::CreateTemplateTemplateParmDecl(const char *template_name) {
   // type that includes a template template argument. Only the name matters for
   // this purpose, so we use dummy values for the other characteristics of the
   // type.
-  return TemplateTemplateParmDecl::Create(ast, decl_ctx, SourceLocation(),
-                                          /*Depth=*/0, /*Position=*/0,
-                                          /*IsParameterPack=*/false,
-                                          &identifier_info, /*Typename=*/false,
-                                          template_param_list);
+  return TemplateTemplateParmDecl::Create(
+      ast, decl_ctx, SourceLocation(),
+      /*Depth*/ 0, /*Position*/ 0,
+      /*IsParameterPack*/ false, &identifier_info,
+      TemplateNameKind::TNK_Type_template, template_param_list);
 }
 
 ClassTemplateSpecializationDecl *

>From 7948dc0eb1a69e50d02c514bd5e9dd162febd9d4 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Mon, 28 Jul 2025 14:55:10 +0200
Subject: [PATCH 02/10] fix tests

---
 .../SemaCXX/cxx2c-template-template-param.cpp | 58 ++++++-------------
 1 file changed, 19 insertions(+), 39 deletions(-)

diff --git a/clang/test/SemaCXX/cxx2c-template-template-param.cpp b/clang/test/SemaCXX/cxx2c-template-template-param.cpp
index b96ce733eabe0..303e5ef9c6276 100644
--- a/clang/test/SemaCXX/cxx2c-template-template-param.cpp
+++ b/clang/test/SemaCXX/cxx2c-template-template-param.cpp
@@ -36,81 +36,60 @@ S4<Var> t16; // expected-error {{template argument does not refer to a concept,
 
 }
 
-template <template<typename T> auto V> // expected-note {{previous template template parameter is here}}
+template <template<typename T> auto V> // expected-note {{previous template template parameter is here}} \
+                                       // expected-error{{template argument for non-type template parameter must be an expression}}
 struct S1 {
     static_assert(V<int> == 42);
     static_assert(V<const int> == 84);
     static_assert(V<double> == 0);
 };
-template <template<auto T> auto V>  // expected-note {{previous template template parameter is here}}
+template <template<auto T> auto V>  // expected-error {{template argument for template type parameter must be a type}} \
+                                    // expected-note {{previous template template parameter is here}}
 struct S2 {
     static_assert(V<0> == 1);
     static_assert(V<1> == 0);
 };
-template <template<typename T> concept C > // expected-note {{previous template template parameter is here}}
+template <template<typename T> concept C > // expected-error {{template argument for non-type template parameter must be an expression}} \
+                                           // expected-note {{previous template template parameter is here}}
 struct S3 {
     static_assert(C<int>);
 };
-template <template<auto> concept C> // expected-note {{previous template template parameter is here}}
+template <template<auto> concept C> // expected-error {{template argument for template type parameter must be a type}} \
+                                    // expected-note {{previous template template parameter is here}}
 struct S4 {
     static_assert(C<0>);
 };
 
-template <typename T> // expected-note {{template parameter has a different kind in template argument}}
+template <typename T> // expected-note {{template parameter is declared here}}
 concept C = true;
 
-template <auto I> // expected-note {{template parameter has a different kind in template argument}}
+template <auto I> // expected-note {{template parameter is declared here}}
 concept CI = true;
 
-template <typename T> // expected-note {{template parameter has a different kind in template argument}}
+template <typename T> // expected-note {{template parameter is declared here}}
 constexpr auto Var = 42;
 template <typename T>
 constexpr auto Var<const T> = 84;
 template <>
 constexpr auto Var<double> = 0;
 
-template <auto N> // expected-note {{template parameter has a different kind in template argument}}
+template <auto N> // expected-note {{template parameter is declared here}}
 constexpr auto Var2 = 0;
 template <auto N>
 requires (N%2 == 0)
 constexpr auto Var2<N> = 1;
 
 void test () {
-    S1<Var2> sE; // expected-error {{template template argument has different template parameters than its corresponding template template parameter}}
-    S2<Var>  sE; // expected-error {{template template argument has different template parameters than its corresponding template template parameter}}
+    S1<Var2> sE; // expected-note {{template template argument has different template parameters than its corresponding template template parameter}}
+    S2<Var>  sE; // expected-note {{template template argument has different template parameters than its corresponding template template parameter}}
     S1<Var> s1;
     S2<Var2> s2;
     S3<C> s3;
-    S4<C> sE; // expected-error {{template template argument has different template parameters than its corresponding template template parameter}}
+    S4<C> sE; // expected-note {{template template argument has different template parameters than its corresponding template template parameter}}
     S4<CI> s4;
-    S3<CI> sE; // expected-error {{template template argument has different template parameters than its corresponding template template parameter}}
+    S3<CI> sE; // expected-note {{template template argument has different template parameters than its corresponding template template parameter}}
 }
 
-namespace subsumption {
-
-template <typename T>
-concept A = true;
-
-template <typename T>
-concept B = A<T> && true;
-
-template <typename T>
-concept C = true;
-
-template <typename T>
-concept D = C<T> && true;
-
-template <typename ABC, template <typename> concept... C>
-concept Apply = (C<ABC> && ...);
-
-constexpr int f(Apply<A, C> auto) {return 1;}
-constexpr int f(Apply<B, D> auto) {return 2;}
-
-int test() {
-   static_assert(f(1) == 2);
-}
-
-}
 
 namespace template_type_constraints {
 
@@ -362,11 +341,12 @@ concept D = Var<int>;
 }
 
 namespace InvalidName {
-// FIXME corentin: improve diagnostics
 template <typename T, template <typename> concept C>
 concept A = C<T>; // expected-note {{here}}
 
 template <A<concept missing<int>> T> // expected-error {{expected expression}} \
-                                     // expected-error {{too few template arguments for concept 'A'}}
+                                     // expected-error {{too few template arguments for concept 'A'}} \
+                                     // expected-error {{unknown type name 'T'}}  \
+                                     // expected-error {{expected unqualified-id}}
 auto f();
 }

>From 8fedefe2b768121fe17e96b50c865514f611bc17 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Mon, 28 Jul 2025 15:12:11 +0200
Subject: [PATCH 03/10] Make it an error in older language modes

---
 clang/include/clang/Basic/DiagnosticParseKinds.td    | 4 ++++
 clang/lib/Parse/ParseTemplate.cpp                    | 8 +++++---
 clang/test/Parser/cxx2c-template-template-param.cpp  | 2 +-
 clang/test/SemaCXX/cxx2c-template-template-param.cpp | 2 +-
 4 files changed, 11 insertions(+), 5 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 165f01514e2b1..0042afccba2c8 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -931,6 +931,10 @@ def err_missing_dependent_template_keyword : Error<
 def warn_missing_dependent_template_keyword : ExtWarn<
   "use 'template' keyword to treat '%0' as a dependent template name">;
 
+def err_cxx26_template_template_params
+    : Error<"%select{variable template|concept}0 template parameter is a C++2c "
+            "extension">;
+
 def ext_extern_template : Extension<
   "extern templates are a C++11 extension">, InGroup<CXX11>;
 def warn_cxx98_compat_extern_template : Warning<
diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp
index f08361ced6379..0277dfb5aac13 100644
--- a/clang/lib/Parse/ParseTemplate.cpp
+++ b/clang/lib/Parse/ParseTemplate.cpp
@@ -758,9 +758,11 @@ NamedDecl *Parser::ParseTemplateTemplateParameter(unsigned Depth,
       ConsumeToken();
   }
 
-  if (!getLangOpts().CPlusPlus23 &&
-      Kind != TemplateNameKind::TNK_Type_template) {
-    // Diag older language mods
+  if (!getLangOpts().CPlusPlus26 &&
+      (Kind == TemplateNameKind::TNK_Concept_template ||
+       Kind == TemplateNameKind::TNK_Var_template)) {
+    Diag(PrevTokLocation, diag::err_cxx26_template_template_params)
+        << (Kind == TemplateNameKind::TNK_Concept_template);
   }
 
   // Parse the ellipsis, if given.
diff --git a/clang/test/Parser/cxx2c-template-template-param.cpp b/clang/test/Parser/cxx2c-template-template-param.cpp
index aa9e2393cf106..e8c7621404766 100644
--- a/clang/test/Parser/cxx2c-template-template-param.cpp
+++ b/clang/test/Parser/cxx2c-template-template-param.cpp
@@ -1,4 +1,4 @@
-// RUN:  %clang_cc1 -std=c++2b -verify %s
+// RUN:  %clang_cc1 -std=c++2c -verify %s
 
 template<template<typename> auto Var>
 struct A{};
diff --git a/clang/test/SemaCXX/cxx2c-template-template-param.cpp b/clang/test/SemaCXX/cxx2c-template-template-param.cpp
index 303e5ef9c6276..ed55a059bb53c 100644
--- a/clang/test/SemaCXX/cxx2c-template-template-param.cpp
+++ b/clang/test/SemaCXX/cxx2c-template-template-param.cpp
@@ -1,4 +1,4 @@
-// RUN:  %clang_cc1 -std=c++2b -verify %s
+// RUN:  %clang_cc1 -std=c++2c -verify %s
 
 namespace Errors {
 

>From 352b44a7f3ded99beb47ebd4c5178edb733cd084 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Mon, 28 Jul 2025 16:07:15 +0200
Subject: [PATCH 04/10] fixc lldb

---
 lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index 8f0decbf3dcd7..0988343709442 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -1634,8 +1634,9 @@ TypeSystemClang::CreateTemplateTemplateParmDecl(const char *template_name) {
   return TemplateTemplateParmDecl::Create(
       ast, decl_ctx, SourceLocation(),
       /*Depth*/ 0, /*Position*/ 0,
-      /*IsParameterPack*/ false, &identifier_info,
-      TemplateNameKind::TNK_Type_template, template_param_list);
+      /*IsParameterPack=*/false, &identifier_info,
+      TemplateNameKind::TNK_Type_template, /*DeclaredWithTypename=*/true,
+      template_param_list);
 }
 
 ClassTemplateSpecializationDecl *

>From db90ccc9c46cbd8b9414a5fb4a5e94bae8d81e8e Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Mon, 28 Jul 2025 16:34:46 +0200
Subject: [PATCH 05/10] cleanups

---
 clang/include/clang/AST/ExprCXX.h        |  2 +-
 clang/lib/Sema/SemaExpr.cpp              |  5 ++---
 clang/lib/Sema/SemaTemplateDeduction.cpp | 19 +++++--------------
 clang/lib/Sema/TreeTransform.h           | 10 +++++-----
 4 files changed, 13 insertions(+), 23 deletions(-)

diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index a78ad0999d198..1979aa8a97b26 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -3261,7 +3261,7 @@ class OverloadExpr : public Expr {
   bool hasExplicitTemplateArgs() const {
     if (!hasTemplateKWAndArgsInfo())
       return false;
-    // FIXME corentin: deduced function types can have "hidden" args and no <
+    // FIXME: deduced function types can have "hidden" args and no <
     // investigate that further, but ultimately maybe we want to model concepts
     // reference with another kind of expression.
     return (isConceptReference() || isVarDeclReference())
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index af8f5ca185c59..0b524cd0b3493 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -2905,9 +2905,8 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS,
     if (Id.getKind() == UnqualifiedIdKind::IK_TemplateId && Id.TemplateId &&
         (Id.TemplateId->Kind == TNK_Var_template ||
          Id.TemplateId->Kind == TNK_Concept_template)) {
-      // TODO CORENTIN
-      // assert(R.getAsSingle<VarTemplateDecl>() &&
-      //       "There should only be one declaration found.");
+      assert(R.getAsSingle<TemplateDecl>() &&
+             "There should only be one declaration found.");
     }
 
     return BuildTemplateIdExpr(SS, TemplateKWLoc, R, ADL, TemplateArgs);
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index c36aac906af3a..df96ff6ebca05 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -181,14 +181,6 @@ class NonTypeOrVarTemplateParmDecl {
                  TNK_Concept_template)));
   }
 
-  /*NonTypeOrVarTemplateParmDecl(const NonTypeTemplateParmDecl* NTTP)
-      : Template(NTTP) {}
-  NonTypeOrVarTemplateParmDecl(const TemplateTemplateParmDecl* TTP)
-      : Template(TTP) {
-    assert(TTP->kind() == TNK_Var_template || TTP->kind() ==
-  clang::TNK_Concept_template);
-  }*/
-
   QualType getType() const {
     if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Template))
       return NTTP->getType();
@@ -6739,18 +6731,17 @@ struct MarkUsedTemplateParameterVisitor : DynamicRecursiveASTVisitor {
     return true;
   }
 
-  /*bool VisitUnresolvedLookupExpr(UnresolvedLookupExpr *ULE) {
+  bool VisitUnresolvedLookupExpr(UnresolvedLookupExpr *ULE) override {
     if (ULE->isConceptReference() || ULE->isVarDeclReference()) {
       if (auto *TTP = ULE->getTemplateTemplateDecl()) {
         if (TTP->getDepth() == Depth)
           Used[TTP->getIndex()] = true;
       }
       for (auto &TLoc : ULE->template_arguments())
-        RecursiveASTVisitor<MarkUsedTemplateParameterVisitor>::
-            TraverseTemplateArgumentLoc(TLoc);
+        DynamicRecursiveASTVisitor::TraverseTemplateArgumentLoc(TLoc);
     }
     return true;
-  }*/
+  }
 };
 }
 
@@ -6773,7 +6764,7 @@ MarkUsedTemplateParameters(ASTContext &Ctx,
     E = Expansion->getPattern();
 
   E = unwrapExpressionForDeduction(E);
-  /*if (const auto *ULE = dyn_cast<UnresolvedLookupExpr>(E);
+  if (const auto *ULE = dyn_cast<UnresolvedLookupExpr>(E);
       ULE && (ULE->isConceptReference() || ULE->isVarDeclReference())) {
     if (const auto *TTP = ULE->getTemplateTemplateDecl())
       Used[TTP->getIndex()] = true;
@@ -6781,7 +6772,7 @@ MarkUsedTemplateParameters(ASTContext &Ctx,
       MarkUsedTemplateParameters(Ctx, TLoc.getArgument(), OnlyDeduced, Depth,
                                  Used);
     return;
-  }*/
+  }
 
   const NonTypeOrVarTemplateParmDecl NTTP =
       getDeducedNTTParameterFromExpr(E, Depth);
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index d3c90b0508234..a955ee7f7987c 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -5183,11 +5183,11 @@ bool TreeTransform<Derived>::TransformConceptTemplateArguments(
     TemplateArgumentLoc In = *First;
 
     if (In.getArgument().getKind() == TemplateArgument::Pack) {
-      // if(In.getArgument().pack_size() == 00  ||
-      // !isConcept(In.getArgument().pack_elements()[0])) {
-      //   Outputs.addArgument(In);
-      //   continue;
-      // }
+      if (In.getArgument().pack_size() == 0 ||
+          !isConcept(In.getArgument().pack_elements()[0])) {
+        Outputs.addArgument(In);
+        continue;
+      }
       typedef TemplateArgumentLocInventIterator<Derived,
                                                 TemplateArgument::pack_iterator>
           PackLocIterator;

>From 031ca840bca44642e690ee8ef7df104fde0b35e9 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Tue, 29 Jul 2025 09:08:13 +0200
Subject: [PATCH 06/10] Address Erich's feedback

---
 clang/lib/AST/DeclTemplate.cpp |  2 +-
 clang/lib/AST/TemplateBase.cpp |  7 ++--
 clang/lib/Sema/TreeTransform.h | 69 +++-------------------------------
 3 files changed, 9 insertions(+), 69 deletions(-)

diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index 70b12c9d3bb64..f084af7f4832e 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -237,7 +237,7 @@ void TemplateParameterList::getAssociatedConstraints(
           ACs.emplace_back(E);
       }
     }
-  if (HasRequiresClause && getRequiresClause())
+  if (HasRequiresClause)
     ACs.emplace_back(getRequiresClause());
 }
 
diff --git a/clang/lib/AST/TemplateBase.cpp b/clang/lib/AST/TemplateBase.cpp
index 4207fea3764ab..93d5611ff34bd 100644
--- a/clang/lib/AST/TemplateBase.cpp
+++ b/clang/lib/AST/TemplateBase.cpp
@@ -340,15 +340,14 @@ bool TemplateArgument::isPackExpansion() const {
 }
 
 bool TemplateArgument::isConceptOrConceptTemplateParameter() const {
-  bool isConcept = false;
   if (getKind() == TemplateArgument::Template) {
     if (isa<ConceptDecl>(getAsTemplate().getAsTemplateDecl()))
-      isConcept = true;
+      return true;
     else if (auto *TTP = dyn_cast_if_present<TemplateTemplateParmDecl>(
                  getAsTemplate().getAsTemplateDecl()))
-      isConcept = TTP->kind() == TNK_Concept_template;
+      return TTP->kind() == TNK_Concept_template;
   }
-  return isConcept;
+  return false;
 }
 
 bool TemplateArgument::containsUnexpandedParameterPack() const {
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index a955ee7f7987c..f2e800ad17dd3 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -660,12 +660,6 @@ class TreeTransform {
                                   TemplateArgumentListInfo &Outputs,
                                   bool Uneval = false);
 
-  template <typename InputIterator>
-  bool TransformConceptTemplateArguments(InputIterator First,
-                                         InputIterator Last,
-                                         TemplateArgumentListInfo &Outputs,
-                                         bool Uneval = false);
-
   /// Fakes up a TemplateArgumentLoc for a given TemplateArgument.
   void InventTemplateArgumentLoc(const TemplateArgument &Arg,
                                  TemplateArgumentLoc &ArgLoc);
@@ -5048,10 +5042,8 @@ template<typename InputIterator>
 bool TreeTransform<Derived>::TransformTemplateArguments(
     InputIterator First, InputIterator Last, TemplateArgumentListInfo &Outputs,
     bool Uneval) {
-  for (; First != Last; ++First) {
+  for (TemplateArgumentLoc In : llvm::make_range(First, Last)) {
     TemplateArgumentLoc Out;
-    TemplateArgumentLoc In = *First;
-
     if (In.getArgument().getKind() == TemplateArgument::Pack) {
       // Unpack argument packs, which we translate them into separate
       // arguments.
@@ -5061,11 +5053,10 @@ 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))
+      if (TransformTemplateArguments(
+              PackLocIterator(*this, In.getArgument().pack_begin()),
+              PackLocIterator(*this, In.getArgument().pack_end()), Outputs,
+              Uneval))
         return true;
 
       continue;
@@ -5163,56 +5154,6 @@ bool TreeTransform<Derived>::TransformTemplateArguments(
 
 }
 
-template <typename Derived>
-template <typename InputIterator>
-bool TreeTransform<Derived>::TransformConceptTemplateArguments(
-    InputIterator First, InputIterator Last, TemplateArgumentListInfo &Outputs,
-    bool Uneval) {
-
-  auto isConcept = [](const TemplateArgument &Arg) {
-    bool isConcept = false;
-    if (Arg.getKind() == TemplateArgument::Template)
-      if (auto *TTP = dyn_cast_if_present<TemplateTemplateParmDecl>(
-              Arg.getAsTemplate().getAsTemplateDecl()))
-        isConcept = TTP->kind() == TNK_Concept_template;
-    return isConcept;
-  };
-
-  for (; First != Last; ++First) {
-    TemplateArgumentLoc Out;
-    TemplateArgumentLoc In = *First;
-
-    if (In.getArgument().getKind() == TemplateArgument::Pack) {
-      if (In.getArgument().pack_size() == 0 ||
-          !isConcept(In.getArgument().pack_elements()[0])) {
-        Outputs.addArgument(In);
-        continue;
-      }
-      typedef TemplateArgumentLocInventIterator<Derived,
-                                                TemplateArgument::pack_iterator>
-          PackLocIterator;
-      if (TransformConceptTemplateArguments(
-              PackLocIterator(*this, In.getArgument().pack_begin()),
-              PackLocIterator(*this, In.getArgument().pack_end()), Outputs,
-              Uneval))
-        return true;
-      continue;
-    }
-
-    if (!isConcept(In.getArgument())) {
-      Outputs.addArgument(In);
-      continue;
-    }
-
-    if (getDerived().TransformTemplateArgument(In, Out, Uneval))
-      return true;
-
-    Outputs.addArgument(Out);
-  }
-
-  return false;
-}
-
 //===----------------------------------------------------------------------===//
 // Type transformation
 //===----------------------------------------------------------------------===//

>From 01f651f330ce0820691ad515f872b09a2185a5f9 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Tue, 29 Jul 2025 09:48:10 +0200
Subject: [PATCH 07/10] Address some of Younan's feedback

---
 clang/lib/Sema/SemaTemplate.cpp | 52 +++++++++++++++------------------
 1 file changed, 24 insertions(+), 28 deletions(-)

diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 4a985bf37a49c..d15253662aea6 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -1185,26 +1185,15 @@ static ExprResult formImmediatelyDeclaredConstraint(
         SS, /*TemplateKWLoc=*/SourceLocation(), NameInfo,
         /*FoundDecl=*/FoundDecl ? FoundDecl : NamedConcept, CD,
         &ConstraintArgs);
-    if (ImmediatelyDeclaredConstraint.isInvalid() || !EllipsisLoc.isValid())
-      return ImmediatelyDeclaredConstraint;
   }
   // We have a template template parameter
   else {
     auto *CDT = dyn_cast<TemplateTemplateParmDecl>(NamedConcept);
     ImmediatelyDeclaredConstraint = S.CheckVarOrConceptTemplateTemplateId(
         SS, NameInfo, CDT, SourceLocation(), &ConstraintArgs);
-    if (ImmediatelyDeclaredConstraint.isInvalid())
-      return ImmediatelyDeclaredConstraint;
-    UnresolvedSet<1> R;
-    R.addDecl(CDT);
-    ImmediatelyDeclaredConstraint = UnresolvedLookupExpr::Create(
-        S.getASTContext(), nullptr, SS.getWithLocInContext(S.getASTContext()),
-        SourceLocation(), NameInfo, false, &ConstraintArgs, R.begin(), R.end(),
-        /*KnownDependent=*/false,
-        /*KnownInstantiationDependent=*/false);
-    if (ImmediatelyDeclaredConstraint.isInvalid() || !EllipsisLoc.isValid())
-      return ImmediatelyDeclaredConstraint;
   }
+  if (ImmediatelyDeclaredConstraint.isInvalid() || !EllipsisLoc.isValid())
+    return ImmediatelyDeclaredConstraint;
 
   // C++2a [temp.param]p4:
   //     [...] If T is not a pack, then E is E', otherwise E is (E' && ...).
@@ -4760,7 +4749,7 @@ ExprResult Sema::CheckVarTemplateId(
 }
 
 ExprResult Sema::CheckVarOrConceptTemplateTemplateId(
-    const CXXScopeSpec &, const DeclarationNameInfo &,
+    const CXXScopeSpec &SS, const DeclarationNameInfo &NameInfo,
     TemplateTemplateParmDecl *Template, SourceLocation TemplateLoc,
     const TemplateArgumentListInfo *TemplateArgs) {
   assert(Template && "A variable template id without template?");
@@ -4773,12 +4762,21 @@ ExprResult Sema::CheckVarOrConceptTemplateTemplateId(
   CheckTemplateArgumentInfo CTAI;
   if (CheckTemplateArgumentList(
           Template, TemplateLoc,
+          // FIXME: TemplateArgs will not be modified because
+          // UpdateArgsWithConversions is false, however, we should
+          // CheckTemplateArgumentList to be const-correct.
           const_cast<TemplateArgumentListInfo &>(*TemplateArgs),
           /*DefaultArgs=*/{}, /*PartialTemplateArgs=*/false, CTAI,
-          /*UpdateArgsWithConversions=*/true))
+          /*UpdateArgsWithConversions=*/false))
     return true;
 
-  return ExprResult();
+  UnresolvedSet<1> R;
+  R.addDecl(Template);
+  return UnresolvedLookupExpr::Create(
+      getASTContext(), nullptr, SS.getWithLocInContext(getASTContext()),
+      SourceLocation(), NameInfo, false, TemplateArgs, R.begin(), R.end(),
+      /*KnownDependent=*/false,
+      /*KnownInstantiationDependent=*/false);
 }
 
 void Sema::diagnoseMissingTemplateArguments(TemplateName Name,
@@ -4893,31 +4891,29 @@ ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS,
     KnownDependent = true;
   }
 
+  // We don't want lookup warnings at this point.
+  R.suppressDiagnostics();
+
   if (R.getAsSingle<ConceptDecl>()) {
     return CheckConceptTemplateId(SS, TemplateKWLoc, R.getLookupNameInfo(),
                                   R.getRepresentativeDecl(),
                                   R.getAsSingle<ConceptDecl>(), TemplateArgs);
   }
 
-  // In C++1y, check variable template ids.
-  if (R.getAsSingle<TemplateTemplateParmDecl>()) {
-    ExprResult Res = CheckVarOrConceptTemplateTemplateId(
+  // Check variable template ids (C++17) and concept template parameters
+  // (C++26).
+  UnresolvedLookupExpr *ULE;
+  if (R.getAsSingle<TemplateTemplateParmDecl>())
+    return CheckVarOrConceptTemplateTemplateId(
         SS, R.getLookupNameInfo(), R.getAsSingle<TemplateTemplateParmDecl>(),
         TemplateKWLoc, TemplateArgs);
-    if (Res.isInvalid() || Res.isUsable())
-      return Res;
-    // Result is dependent. Carry on to build an UnresolvedLookupEpxr.
-  }
 
-  // We don't want lookup warnings at this point.
-  R.suppressDiagnostics();
-
-  UnresolvedLookupExpr *ULE = UnresolvedLookupExpr::Create(
+  // Function templates
+  ULE = UnresolvedLookupExpr::Create(
       Context, R.getNamingClass(), SS.getWithLocInContext(Context),
       TemplateKWLoc, R.getLookupNameInfo(), RequiresADL, TemplateArgs,
       R.begin(), R.end(), KnownDependent,
       /*KnownInstantiationDependent=*/false);
-
   // Model the templates with UnresolvedTemplateTy. The expression should then
   // either be transformed in an instantiation or be diagnosed in
   // CheckPlaceholderExpr.

>From c8464a0cb521707ed9324aa134500e8f9a803db2 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Tue, 29 Jul 2025 10:18:39 +0200
Subject: [PATCH 08/10] add comment

---
 clang/include/clang/Sema/Initialization.h | 3 ++-
 clang/lib/Sema/SemaTemplate.cpp           | 2 ++
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/clang/include/clang/Sema/Initialization.h b/clang/include/clang/Sema/Initialization.h
index e5ee4b4e48ccc..d7675ea153afb 100644
--- a/clang/include/clang/Sema/Initialization.h
+++ b/clang/include/clang/Sema/Initialization.h
@@ -159,7 +159,8 @@ class alignas(8) InitializedEntity {
   };
 
   struct VD {
-    /// The VarDecl, FieldDecl, or BindingDecl being initialized.
+    /// The VarDecl, FieldDecl, TemplateParmDecl, or BindingDecl being
+    /// initialized.
     NamedDecl *VariableOrMember;
 
     /// When Kind == EK_Member, whether this is an implicit member
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index d15253662aea6..631bfc7558c16 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -9228,6 +9228,8 @@ bool Sema::CheckConceptUseInDefinition(NamedDecl *Concept, SourceLocation Loc) {
     Diag(CE->getLocation(), diag::note_declared_at);
     return true;
   }
+  // Concept template parameters don't have a definition and can't
+  // be defined recursively.
   return false;
 }
 

>From 83d6873908742672811c77e3704cef49bb1df083 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Tue, 29 Jul 2025 10:25:23 +0200
Subject: [PATCH 09/10] revert ParsedTemplate.h changes

---
 clang/include/clang/Sema/ParsedTemplate.h | 212 +++++++++++-----------
 1 file changed, 105 insertions(+), 107 deletions(-)

diff --git a/clang/include/clang/Sema/ParsedTemplate.h b/clang/include/clang/Sema/ParsedTemplate.h
index 6628bb88a81b9..3a8050f9a0a3d 100644
--- a/clang/include/clang/Sema/ParsedTemplate.h
+++ b/clang/include/clang/Sema/ParsedTemplate.h
@@ -14,7 +14,6 @@
 #ifndef LLVM_CLANG_SEMA_PARSEDTEMPLATE_H
 #define LLVM_CLANG_SEMA_PARSEDTEMPLATE_H
 
-#include "clang/AST/DeclTemplate.h"
 #include "clang/Basic/OperatorKinds.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/TemplateKinds.h"
@@ -27,116 +26,115 @@
 #include <new>
 
 namespace clang {
+  /// Represents the parsed form of a C++ template argument.
+  class ParsedTemplateArgument {
+  public:
+    /// Describes the kind of template argument that was parsed.
+    enum KindType {
+      /// A template type parameter, stored as a type.
+      Type,
+      /// A non-type template parameter, stored as an expression.
+      NonType,
+      /// A template template argument, stored as a template name.
+      Template
+    };
+
+    /// Build an empty template argument.
+    ///
+    /// This template argument is invalid.
+    ParsedTemplateArgument() : Kind(Type), Arg(nullptr) { }
+
+    /// Create a template type argument or non-type template argument.
+    ///
+    /// \param Arg the template type argument or non-type template argument.
+    /// \param Loc the location of the type.
+    ParsedTemplateArgument(KindType Kind, void *Arg, SourceLocation Loc)
+      : Kind(Kind), Arg(Arg), Loc(Loc) { }
+
+    /// Create a template template argument.
+    ///
+    /// \param SS the C++ scope specifier that precedes the template name, if
+    /// any.
+    ///
+    /// \param Template the template to which this template template
+    /// argument refers.
+    ///
+    /// \param TemplateLoc the location of the template name.
+    ParsedTemplateArgument(const CXXScopeSpec &SS,
+                           ParsedTemplateTy Template,
+                           SourceLocation TemplateLoc)
+      : Kind(ParsedTemplateArgument::Template),
+        Arg(Template.getAsOpaquePtr()), SS(SS), Loc(TemplateLoc) {}
+
+    /// Determine whether the given template argument is invalid.
+    bool isInvalid() const { return Arg == nullptr; }
+
+    /// Determine what kind of template argument we have.
+    KindType getKind() const { return Kind; }
+
+    /// Retrieve the template type argument's type.
+    ParsedType getAsType() const {
+      assert(Kind == Type && "Not a template type argument");
+      return ParsedType::getFromOpaquePtr(Arg);
+    }
 
-/// Represents the parsed form of a C++ template argument.
-class ParsedTemplateArgument {
-public:
-  /// Describes the kind of template argument that was parsed.
-  enum KindType {
-    /// A template type parameter, stored as a type.
-    Type,
-    /// A non-type template parameter, stored as an expression.
-    NonType,
-    /// A template template argument, stored as a template name.
-    Template,
-  };
+    /// Retrieve the non-type template argument's expression.
+    Expr *getAsExpr() const {
+      assert(Kind == NonType && "Not a non-type template argument");
+      return static_cast<Expr*>(Arg);
+    }
 
-  /// Build an empty template argument.
-  ///
-  /// This template argument is invalid.
-  ParsedTemplateArgument() : Kind(Type), Arg(nullptr) {}
+    /// Retrieve the template template argument's template name.
+    ParsedTemplateTy getAsTemplate() const {
+      assert(Kind == Template && "Not a template template argument");
+      return ParsedTemplateTy::getFromOpaquePtr(Arg);
+    }
 
-  /// Create a template type argument or non-type template argument.
-  ///
-  /// \param Arg the template type argument or non-type template argument.
-  /// \param Loc the location of the type.
-  ParsedTemplateArgument(KindType Kind, void *Arg, SourceLocation Loc)
-      : Kind(Kind), Arg(Arg), Loc(Loc) {}
+    /// Retrieve the location of the template argument.
+    SourceLocation getLocation() const { return Loc; }
 
-  /// Create a template template argument.
-  ///
-  /// \param SS the C++ scope specifier that precedes the template name, if
-  /// any.
-  ///
-  /// \param Template the template to which this template template
-  /// argument refers.
-  ///
-  /// \param TemplateLoc the location of the template name.
-  ParsedTemplateArgument(const CXXScopeSpec &SS, ParsedTemplateTy Template,
-                         SourceLocation TemplateLoc)
-      : Kind(ParsedTemplateArgument::Template), Arg(Template.getAsOpaquePtr()),
-        SS(SS), Loc(TemplateLoc) {}
-
-  /// Determine whether the given template argument is invalid.
-  bool isInvalid() const { return Arg == nullptr; }
-
-  /// Determine what kind of template argument we have.
-  KindType getKind() const { return Kind; }
-
-  /// Retrieve the template type argument's type.
-  ParsedType getAsType() const {
-    assert(Kind == Type && "Not a template type argument");
-    return ParsedType::getFromOpaquePtr(Arg);
-  }
-
-  /// Retrieve the non-type template argument's expression.
-  Expr *getAsExpr() const {
-    assert(Kind == NonType && "Not a non-type template argument");
-    return static_cast<Expr *>(Arg);
-  }
-
-  /// Retrieve the template template argument's template name.
-  ParsedTemplateTy getAsTemplate() const {
-    assert(Kind == Template && "Not a template template argument");
-    return ParsedTemplateTy::getFromOpaquePtr(Arg);
-  }
-
-  /// Retrieve the location of the template argument.
-  SourceLocation getLocation() const { return Loc; }
-
-  /// Retrieve the nested-name-specifier that precedes the template
-  /// name in a template template argument.
-  const CXXScopeSpec &getScopeSpec() const {
-    assert((Kind == Template) &&
-           "Only template template arguments can have a scope specifier");
-    return SS;
-  }
-
-  /// Retrieve the location of the ellipsis that makes a template
-  /// template argument into a pack expansion.
-  SourceLocation getEllipsisLoc() const {
-    assert((Kind == Template) &&
-           "Only template arguments can have an ellipsis");
-    return EllipsisLoc;
-  }
-
-  /// Retrieve a pack expansion of the given template template
-  /// argument.
-  ///
-  /// \param EllipsisLoc The location of the ellipsis.
-  ParsedTemplateArgument
-  getTemplatePackExpansion(SourceLocation EllipsisLoc) const;
-
-private:
-  KindType Kind;
-
-  /// The actual template argument representation, which may be
-  /// an \c Sema::TypeTy* (for a type), an Expr* (for an
-  /// expression), or an Sema::TemplateTy (for a template).
-  void *Arg;
-
-  /// The nested-name-specifier that can accompany a template template
-  /// argument.
-  CXXScopeSpec SS;
-
-  /// the location of the template argument.
-  SourceLocation Loc;
-
-  /// The ellipsis location that can accompany a template/universal template
-  /// argument (turning it into a template/universal template argument
-  /// expansion).
-  SourceLocation EllipsisLoc;
-};
+    /// Retrieve the nested-name-specifier that precedes the template
+    /// name in a template template argument.
+    const CXXScopeSpec &getScopeSpec() const {
+      assert(Kind == Template &&
+             "Only template template arguments can have a scope specifier");
+      return SS;
+    }
+
+    /// Retrieve the location of the ellipsis that makes a template
+    /// template argument into a pack expansion.
+    SourceLocation getEllipsisLoc() const {
+      assert(Kind == Template &&
+             "Only template template arguments can have an ellipsis");
+      return EllipsisLoc;
+    }
+
+    /// Retrieve a pack expansion of the given template template
+    /// argument.
+    ///
+    /// \param EllipsisLoc The location of the ellipsis.
+    ParsedTemplateArgument getTemplatePackExpansion(
+                                              SourceLocation EllipsisLoc) const;
+
+  private:
+    KindType Kind;
+
+    /// The actual template argument representation, which may be
+    /// an \c Sema::TypeTy* (for a type), an Expr* (for an
+    /// expression), or an Sema::TemplateTy (for a template).
+    void *Arg;
+
+    /// The nested-name-specifier that can accompany a template template
+    /// argument.
+    CXXScopeSpec SS;
+
+    /// the location of the template argument.
+    SourceLocation Loc;
+
+    /// The ellipsis location that can accompany a template template
+    /// argument (turning it into a template template argument expansion).
+    SourceLocation EllipsisLoc;
+  };
 
   /// Information about a template-id annotation
   /// token.

>From 0cb6d19c703e584d2d531034aaf959b9a75f7725 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Tue, 29 Jul 2025 10:36:44 +0200
Subject: [PATCH 10/10] remove more uneeded/redundant code

---
 clang/lib/Sema/SemaTemplateInstantiate.cpp | 24 ----------------------
 1 file changed, 24 deletions(-)

diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 7a4ff53573ebf..6ed46fdd46531 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1608,9 +1608,6 @@ namespace {
                           QualType ObjectType = QualType(),
                           NamedDecl *FirstQualifierInScope = nullptr,
                           bool AllowInjectedClassName = false);
-    TemplateArgument
-    TransformNamedTemplateTemplateArgument(CXXScopeSpec &SS, TemplateName Name,
-                                           SourceLocation NameLoc);
 
     const AnnotateAttr *TransformAnnotateAttr(const AnnotateAttr *AA);
     const CXXAssumeAttr *TransformCXXAssumeAttr(const CXXAssumeAttr *AA);
@@ -2189,27 +2186,6 @@ TemplateName TemplateInstantiator::TransformTemplateName(
                                           AllowInjectedClassName);
 }
 
-TemplateArgument TemplateInstantiator::TransformNamedTemplateTemplateArgument(
-    CXXScopeSpec &SS, TemplateName Name, SourceLocation NameLoc) {
-  if (TemplateTemplateParmDecl *TTP =
-          dyn_cast_or_null<TemplateTemplateParmDecl>(
-              Name.getAsTemplateDecl())) {
-    if (TTP->getDepth() < TemplateArgs.getNumLevels()) {
-      // If the corresponding template argument is NULL or non-existent, it's
-      // because we are performing instantiation from explicitly-specified
-      // template arguments in a function template, but there were some
-      // arguments left unspecified.
-      if (!TemplateArgs.hasTemplateArgument(TTP->getDepth(),
-                                            TTP->getPosition()))
-        return TemplateArgument(Name);
-    }
-  }
-  TemplateName TN = getDerived().TransformTemplateName(SS, Name, NameLoc);
-  if (!TN.isNull())
-    return TN;
-  return TemplateArgument();
-}
-
 ExprResult
 TemplateInstantiator::TransformPredefinedExpr(PredefinedExpr *E) {
   if (!E->isTypeDependent())



More information about the lldb-commits mailing list