Author: Corentin Jabot
Date: 2026-08-19T10:18:50+02:00
New Revision: 40fe85daa063f8201174f9a5a777d9db13f77f91

URL: 
https://github.com/llvm/llvm-project/commit/40fe85daa063f8201174f9a5a777d9db13f77f91
DIFF: 
https://github.com/llvm/llvm-project/commit/40fe85daa063f8201174f9a5a777d9db13f77f91.diff

LOG: [Clang][NFC] Introduce a node to model concept and variable template 
template parameters (#216729)

The initial implementation of concept template parameter piggy-backed on
UnresolvedLookupExpr, because it did _mostly_ what we wanted and I was
lazy (it led to some akwardness in a few places)

However, to implement template pack indexing we need to store a
TemplateName rather than a template decl.

So this PR adds this new node, as preparatory work for P3670.

---

Opus 5 was used to make the initial version of this PR, with quite a bit
of cleaning after.

Added: 
    

Modified: 
    clang/include/clang/AST/ComputeDependence.h
    clang/include/clang/AST/ExprCXX.h
    clang/include/clang/AST/RecursiveASTVisitor.h
    clang/include/clang/AST/TemplateBase.h
    clang/include/clang/AST/TextNodeDumper.h
    clang/include/clang/Basic/StmtNodes.td
    clang/include/clang/Sema/Sema.h
    clang/include/clang/Serialization/ASTBitCodes.h
    clang/lib/AST/ASTImporter.cpp
    clang/lib/AST/ComputeDependence.cpp
    clang/lib/AST/Expr.cpp
    clang/lib/AST/ExprCXX.cpp
    clang/lib/AST/ExprClassification.cpp
    clang/lib/AST/ExprConstant.cpp
    clang/lib/AST/ItaniumMangle.cpp
    clang/lib/AST/StmtPrinter.cpp
    clang/lib/AST/StmtProfile.cpp
    clang/lib/AST/TextNodeDumper.cpp
    clang/lib/Sema/SemaExceptionSpec.cpp
    clang/lib/Sema/SemaTemplate.cpp
    clang/lib/Sema/SemaTemplateDeduction.cpp
    clang/lib/Sema/SemaTemplateInstantiate.cpp
    clang/lib/Sema/TreeTransform.h
    clang/lib/Serialization/ASTReaderStmt.cpp
    clang/lib/Serialization/ASTWriter.cpp
    clang/lib/Serialization/ASTWriterStmt.cpp
    clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
    clang/tools/libclang/CXCursor.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/AST/ComputeDependence.h 
b/clang/include/clang/AST/ComputeDependence.h
index 3a3c86842501a..6430081a40350 100644
--- a/clang/include/clang/AST/ComputeDependence.h
+++ b/clang/include/clang/AST/ComputeDependence.h
@@ -80,6 +80,7 @@ class CXXDefaultArgExpr;
 class LambdaExpr;
 class CXXUnresolvedConstructExpr;
 class CXXDependentScopeMemberExpr;
+class DependentTemplateIdExpr;
 class MaterializeTemporaryExpr;
 class CXXFoldExpr;
 class CXXParenListInitExpr;
@@ -175,6 +176,7 @@ ExprDependence computeDependence(LambdaExpr *E,
                                  bool ContainsUnexpandedParameterPack);
 ExprDependence computeDependence(CXXUnresolvedConstructExpr *E);
 ExprDependence computeDependence(CXXDependentScopeMemberExpr *E);
+ExprDependence computeDependence(DependentTemplateIdExpr *E);
 ExprDependence computeDependence(MaterializeTemporaryExpr *E);
 ExprDependence computeDependence(CXXFoldExpr *E);
 ExprDependence computeDependence(CXXParenListInitExpr *E);

diff  --git a/clang/include/clang/AST/ExprCXX.h 
b/clang/include/clang/AST/ExprCXX.h
index d3d3b9c6d6326..59a70942af4e1 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -3287,39 +3287,11 @@ class OverloadExpr : public Expr {
            getTrailingASTTemplateKWAndArgsInfo()->NumTemplateArgs;
   }
 
-  bool isConceptReference() const {
-    return getNumDecls() == 1 && [&]() {
-      if (auto *TTP = dyn_cast_or_null<TemplateTemplateParmDecl>(
-              getTrailingResults()->getDecl()))
-        return TTP->templateParameterKind() == 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->templateParameterKind() == 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())
       return nullptr;
@@ -3488,6 +3460,75 @@ class UnresolvedLookupExpr final
   }
 };
 
+/// A template-id naming a variable template or a concept through a template
+/// template parameter.
+class DependentTemplateIdExpr final
+    : public Expr,
+      private llvm::TrailingObjects<DependentTemplateIdExpr,
+                                    TemplateArgumentLoc> {
+  friend class ASTStmtReader;
+  friend class ASTStmtWriter;
+  friend TrailingObjects;
+
+  DeclarationNameInfo NameInfo;
+  TemplateName Name;
+  ASTTemplateKWAndArgsInfo KWAndArgs;
+
+  DependentTemplateIdExpr(const ASTContext &Context,
+                          const DeclarationNameInfo &NameInfo,
+                          TemplateName Name,
+                          const TemplateArgumentListInfo &TemplateArgs);
+
+  DependentTemplateIdExpr(EmptyShell Empty, unsigned NumTemplateArgs);
+
+public:
+  static DependentTemplateIdExpr *
+  Create(const ASTContext &Context, const DeclarationNameInfo &NameInfo,
+         TemplateName Name, const TemplateArgumentListInfo &TemplateArgs);
+
+  static DependentTemplateIdExpr *CreateEmpty(const ASTContext &Context,
+                                              unsigned NumTemplateArgs);
+
+  const DeclarationNameInfo &getNameInfo() const { return NameInfo; }
+  DeclarationName getName() const { return NameInfo.getName(); }
+  SourceLocation getNameLoc() const { return NameInfo.getLoc(); }
+
+  TemplateName getTemplateName() const { return Name; }
+
+  TemplateTemplateParmDecl *getParameter() const {
+    return cast<TemplateTemplateParmDecl>(Name.getAsTemplateDecl());
+  }
+
+  bool isConceptReference() const {
+    return getParameter()->templateParameterKind() == TNK_Concept_template;
+  }
+
+  SourceLocation getLAngleLoc() const { return KWAndArgs.LAngleLoc; }
+  SourceLocation getRAngleLoc() const { return KWAndArgs.RAngleLoc; }
+
+  unsigned getNumTemplateArgs() const { return KWAndArgs.NumTemplateArgs; }
+
+  ArrayRef<TemplateArgumentLoc> template_arguments() const {
+    return getTrailingObjects(getNumTemplateArgs());
+  }
+
+  SourceLocation getBeginLoc() const { return getNameLoc(); }
+
+  SourceLocation getEndLoc() const { return getRAngleLoc(); }
+
+  child_range children() {
+    return child_range(child_iterator(), child_iterator());
+  }
+
+  const_child_range children() const {
+    return const_child_range(const_child_iterator(), const_child_iterator());
+  }
+
+  static bool classof(const Stmt *T) {
+    return T->getStmtClass() == DependentTemplateIdExprClass;
+  }
+};
+
 /// A qualified reference to a name whose declaration cannot
 /// yet be resolved.
 ///

diff  --git a/clang/include/clang/AST/RecursiveASTVisitor.h 
b/clang/include/clang/AST/RecursiveASTVisitor.h
index d435cda19fa49..1f7c8d762e1b5 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -2646,6 +2646,12 @@ DEF_TRAVERSE_STMT(CXXDependentScopeMemberExpr, {
   }
 })
 
+DEF_TRAVERSE_STMT(DependentTemplateIdExpr, {
+  TRY_TO(TraverseDeclarationNameInfo(S->getNameInfo()));
+  TRY_TO(TraverseTemplateArgumentLocsHelper(S->template_arguments().data(),
+                                            S->getNumTemplateArgs()));
+})
+
 DEF_TRAVERSE_STMT(DeclRefExpr, {
   TRY_TO(TraverseNestedNameSpecifierLoc(S->getQualifierLoc()));
   TRY_TO(TraverseDeclarationNameInfo(S->getNameInfo()));

diff  --git a/clang/include/clang/AST/TemplateBase.h 
b/clang/include/clang/AST/TemplateBase.h
index f7d754ea6082b..b9a787ed1b626 100644
--- a/clang/include/clang/AST/TemplateBase.h
+++ b/clang/include/clang/AST/TemplateBase.h
@@ -775,7 +775,7 @@ struct ASTTemplateArgumentListInfo final
 /// Represents an explicit template argument list in C++, e.g.,
 /// the "<int>" in "sort<int>".
 ///
-/// It is intended to be used as a trailing object on AST nodes, and
+/// It is designed to be usable as a trailing object on AST nodes, and
 /// as such, doesn't contain the array of TemplateArgumentLoc itself,
 /// but expects the containing object to also provide storage for
 /// that.

diff  --git a/clang/include/clang/AST/TextNodeDumper.h 
b/clang/include/clang/AST/TextNodeDumper.h
index 1eaafd49c2f06..5d4a4456b40a1 100644
--- a/clang/include/clang/AST/TextNodeDumper.h
+++ b/clang/include/clang/AST/TextNodeDumper.h
@@ -277,6 +277,7 @@ class TextNodeDumper
   void VisitImplicitCastExpr(const ImplicitCastExpr *Node);
   void VisitDeclRefExpr(const DeclRefExpr *Node);
   void VisitDependentScopeDeclRefExpr(const DependentScopeDeclRefExpr *Node);
+  void VisitDependentTemplateIdExpr(const DependentTemplateIdExpr *Node);
   void VisitSYCLUniqueStableNameExpr(const SYCLUniqueStableNameExpr *Node);
   void VisitPredefinedExpr(const PredefinedExpr *Node);
   void VisitCharacterLiteral(const CharacterLiteral *Node);

diff  --git a/clang/include/clang/Basic/StmtNodes.td 
b/clang/include/clang/Basic/StmtNodes.td
index 6df244d7a8c90..84804d6705d2b 100644
--- a/clang/include/clang/Basic/StmtNodes.td
+++ b/clang/include/clang/Basic/StmtNodes.td
@@ -161,6 +161,7 @@ def ExprWithCleanups : StmtNode<FullExpr>;
 def CXXTemporaryObjectExpr : StmtNode<CXXConstructExpr>;
 def CXXUnresolvedConstructExpr : StmtNode<Expr>;
 def CXXDependentScopeMemberExpr : StmtNode<Expr>;
+def DependentTemplateIdExpr : StmtNode<Expr>;
 def OverloadExpr : StmtNode<Expr, 1>;
 def UnresolvedLookupExpr : StmtNode<OverloadExpr>;
 def UnresolvedMemberExpr : StmtNode<OverloadExpr>;

diff  --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 3cb0dcbbba83a..a0f94df1407b2 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -11819,8 +11819,7 @@ class Sema final : public SemaBase {
                                 const TemplateArgumentListInfo *TemplateArgs);
 
   ExprResult CheckVarOrConceptTemplateTemplateId(
-      const CXXScopeSpec &SS, const DeclarationNameInfo &NameInfo,
-      TemplateTemplateParmDecl *Template, SourceLocation TemplateLoc,
+      const DeclarationNameInfo &NameInfo, TemplateTemplateParmDecl *Template,
       const TemplateArgumentListInfo *TemplateArgs);
 
   ExprResult

diff  --git a/clang/include/clang/Serialization/ASTBitCodes.h 
b/clang/include/clang/Serialization/ASTBitCodes.h
index 974761b236411..6a52a9e4fa780 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -1929,6 +1929,7 @@ enum StmtCode {
 
   EXPR_CXX_DEPENDENT_SCOPE_MEMBER,   // CXXDependentScopeMemberExpr
   EXPR_CXX_DEPENDENT_SCOPE_DECL_REF, // DependentScopeDeclRefExpr
+  EXPR_DEPENDENT_TEMPLATE_ID,        // DependentTemplateIdExpr
   EXPR_CXX_UNRESOLVED_CONSTRUCT,     // CXXUnresolvedConstructExpr
   EXPR_CXX_UNRESOLVED_MEMBER,        // UnresolvedMemberExpr
   EXPR_CXX_UNRESOLVED_LOOKUP,        // UnresolvedLookupExpr

diff  --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 725ebbd6ec987..c2164352126ef 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -682,6 +682,7 @@ namespace clang {
     ExpectedStmt VisitCXXMemberCallExpr(CXXMemberCallExpr *E);
     ExpectedStmt VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr 
*E);
     ExpectedStmt VisitDependentScopeDeclRefExpr(DependentScopeDeclRefExpr *E);
+    ExpectedStmt VisitDependentTemplateIdExpr(DependentTemplateIdExpr *E);
     ExpectedStmt VisitCXXUnresolvedConstructExpr(CXXUnresolvedConstructExpr 
*E);
     ExpectedStmt VisitUnresolvedLookupExpr(UnresolvedLookupExpr *E);
     ExpectedStmt VisitUnresolvedMemberExpr(UnresolvedMemberExpr *E);
@@ -8825,6 +8826,29 @@ ExpectedStmt 
ASTNodeImporter::VisitCXXDependentScopeMemberExpr(
       ToMemberNameInfo, ResInfo);
 }
 
+ExpectedStmt
+ASTNodeImporter::VisitDependentTemplateIdExpr(DependentTemplateIdExpr *E) {
+  Error Err = Error::success();
+  auto ToName = importChecked(Err, E->getTemplateName());
+  auto ToDeclName = importChecked(Err, E->getName());
+  auto ToNameLoc = importChecked(Err, E->getNameLoc());
+  if (Err)
+    return std::move(Err);
+
+  DeclarationNameInfo ToNameInfo(ToDeclName, ToNameLoc);
+  if (Error Err = ImportDeclarationNameLoc(E->getNameInfo(), ToNameInfo))
+    return std::move(Err);
+
+  TemplateArgumentListInfo ToTAInfo;
+  if (Error Err =
+          ImportTemplateArgumentListInfo(E->getLAngleLoc(), E->getRAngleLoc(),
+                                         E->template_arguments(), ToTAInfo))
+    return std::move(Err);
+
+  return DependentTemplateIdExpr::Create(Importer.getToContext(), ToNameInfo,
+                                         ToName, ToTAInfo);
+}
+
 ExpectedStmt
 ASTNodeImporter::VisitDependentScopeDeclRefExpr(DependentScopeDeclRefExpr *E) {
   Error Err = Error::success();

diff  --git a/clang/lib/AST/ComputeDependence.cpp 
b/clang/lib/AST/ComputeDependence.cpp
index 7e6bd69711c5b..0fc9da18b9394 100644
--- a/clang/lib/AST/ComputeDependence.cpp
+++ b/clang/lib/AST/ComputeDependence.cpp
@@ -831,7 +831,7 @@ clang::computeDependence(OverloadExpr *E, bool 
KnownDependent,
                            ~NestedNameSpecifierDependence::Dependent);
   for (auto *D : E->decls()) {
     if (D->getDeclContext()->isDependentContext() ||
-        isa<UnresolvedUsingValueDecl>(D) || isa<TemplateTemplateParmDecl>(D))
+        isa<UnresolvedUsingValueDecl>(D))
       Deps |= ExprDependence::TypeValueInstantiation;
   }
   // If we have explicit template arguments, check for dependent
@@ -903,6 +903,17 @@ ExprDependence 
clang::computeDependence(CXXDependentScopeMemberExpr *E) {
   return D;
 }
 
+ExprDependence clang::computeDependence(DependentTemplateIdExpr *E) {
+  auto D = ExprDependence::TypeValueInstantiation;
+  if (E->getTemplateName().getDependence() &
+      TemplateNameDependence::UnexpandedPack)
+    D |= ExprDependence::UnexpandedPack;
+  D |= getDependenceInExpr(E->getNameInfo());
+  for (const auto &A : E->template_arguments())
+    D |= toExprDependence(A.getArgument().getDependence());
+  return D;
+}
+
 ExprDependence clang::computeDependence(MaterializeTemporaryExpr *E) {
   return E->getSubExpr()->getDependence();
 }

diff  --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index 5d7ee4710481c..e501527ed9b04 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -3714,6 +3714,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx,
     llvm_unreachable("unexpected Expr kind");
 
   case DependentScopeDeclRefExprClass:
+  case DependentTemplateIdExprClass:
   case CXXUnresolvedConstructExprClass:
   case CXXDependentScopeMemberExprClass:
   case UnresolvedLookupExprClass:

diff  --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index 6c1cde6540d85..08c931d863044 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -401,14 +401,40 @@ 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 (const auto *TTP = llvm::dyn_cast<TemplateTemplateParmDecl>(ND))
-    return TTP->isParameterPack();
-  return false;
+DependentTemplateIdExpr::DependentTemplateIdExpr(
+    const ASTContext &Context, const DeclarationNameInfo &NameInfo,
+    TemplateName Name, const TemplateArgumentListInfo &TemplateArgs)
+    : Expr(DependentTemplateIdExprClass, Context.DependentTy, VK_LValue,
+           OK_Ordinary),
+      NameInfo(NameInfo), Name(Name) {
+  KWAndArgs.initializeFrom(/*TemplateKWLoc=*/{}, TemplateArgs,
+                           getTrailingObjects());
+  setDependence(computeDependence(this));
+}
+
+DependentTemplateIdExpr::DependentTemplateIdExpr(EmptyShell Empty,
+                                                 unsigned NumTemplateArgs)
+    : Expr(DependentTemplateIdExprClass, Empty) {
+  KWAndArgs.NumTemplateArgs = NumTemplateArgs;
+}
+
+DependentTemplateIdExpr *DependentTemplateIdExpr::Create(
+    const ASTContext &Context, const DeclarationNameInfo &NameInfo,
+    TemplateName Name, const TemplateArgumentListInfo &TemplateArgs) {
+  void *Mem = Context.Allocate(
+      totalSizeToAlloc<TemplateArgumentLoc>(TemplateArgs.size()),
+      alignof(DependentTemplateIdExpr));
+  return new (Mem)
+      DependentTemplateIdExpr(Context, NameInfo, Name, TemplateArgs);
+}
+
+DependentTemplateIdExpr *
+DependentTemplateIdExpr::CreateEmpty(const ASTContext &Context,
+                                     unsigned NumTemplateArgs) {
+  void *Mem =
+      Context.Allocate(totalSizeToAlloc<TemplateArgumentLoc>(NumTemplateArgs),
+                       alignof(DependentTemplateIdExpr));
+  return new (Mem) DependentTemplateIdExpr(EmptyShell(), NumTemplateArgs);
 }
 
 // UnresolvedLookupExpr
@@ -419,11 +445,10 @@ UnresolvedLookupExpr::UnresolvedLookupExpr(
     const TemplateArgumentListInfo *TemplateArgs, UnresolvedSetIterator Begin,
     UnresolvedSetIterator End, bool KnownDependent,
     bool KnownInstantiationDependent)
-    : OverloadExpr(
-          UnresolvedLookupExprClass, Context, QualifierLoc, TemplateKWLoc,
-          NameInfo, TemplateArgs, Begin, End, KnownDependent,
-          KnownInstantiationDependent,
-          UnresolvedLookupExprIsVariableOrConceptParameterPack(Begin, End)),
+    : OverloadExpr(UnresolvedLookupExprClass, Context, QualifierLoc,
+                   TemplateKWLoc, NameInfo, TemplateArgs, Begin, End,
+                   KnownDependent, KnownInstantiationDependent,
+                   /*KnownContainsUnexpandedParameterPack=*/false),
       NamingClass(NamingClass) {
   UnresolvedLookupExprBits.RequiresADL = RequiresADL;
 }

diff  --git a/clang/lib/AST/ExprClassification.cpp 
b/clang/lib/AST/ExprClassification.cpp
index ef071cdef66b6..eebae17d7b948 100644
--- a/clang/lib/AST/ExprClassification.cpp
+++ b/clang/lib/AST/ExprClassification.cpp
@@ -130,6 +130,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const 
Expr *E) {
     // FIXME: Is this wise? Should they get their own kind?
   case Expr::UnresolvedLookupExprClass:
   case Expr::UnresolvedMemberExprClass:
+  case Expr::DependentTemplateIdExprClass:
   case Expr::DependentCoawaitExprClass:
   case Expr::CXXDependentScopeMemberExprClass:
   case Expr::DependentScopeDeclRefExprClass:

diff  --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 05f981f671e62..ddcef9e7bfc6f 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -22340,6 +22340,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext 
&Ctx) {
   case Expr::UnresolvedLookupExprClass:
   case Expr::RecoveryExprClass:
   case Expr::DependentScopeDeclRefExprClass:
+  case Expr::DependentTemplateIdExprClass:
   case Expr::CXXConstructExprClass:
   case Expr::CXXInheritedCtorInitExprClass:
   case Expr::CXXStdInitializerListExprClass:

diff  --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index da8339e4a45ee..c77d9d979e932 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -5317,6 +5317,15 @@ void CXXNameMangler::mangleExpression(const Expr *E, 
unsigned Arity,
     break;
   }
 
+  case Expr::DependentTemplateIdExprClass: {
+    NotPrimaryExpr();
+    const auto *DTI = cast<DependentTemplateIdExpr>(E);
+    mangleUnresolvedName(NestedNameSpecifier(), DTI->getName(),
+                         DTI->template_arguments().data(),
+                         DTI->getNumTemplateArgs(), Arity);
+    break;
+  }
+
   case Expr::CXXUnresolvedConstructExprClass: {
     NotPrimaryExpr();
     const CXXUnresolvedConstructExpr *CE = cast<CXXUnresolvedConstructExpr>(E);

diff  --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index eeb377c794e05..e08566e5ae011 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -2671,6 +2671,12 @@ void StmtPrinter::VisitCXXReflectExpr(CXXReflectExpr *S) 
{
   assert(false && "not implemented yet");
 }
 
+void StmtPrinter::VisitDependentTemplateIdExpr(DependentTemplateIdExpr *Node) {
+  OS << Node->getNameInfo();
+  printTemplateArgumentList(OS, Node->template_arguments(), Policy,
+                            Node->getParameter()->getTemplateParameters());
+}
+
 void StmtPrinter::VisitCXXDependentScopeMemberExpr(
                                          CXXDependentScopeMemberExpr *Node) {
   if (!Node->isImplicitAccess()) {

diff  --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index 45c962aa27553..dca2c49ac45d5 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -2353,6 +2353,14 @@ void StmtProfiler::VisitCXXUnresolvedConstructExpr(
   ID.AddInteger(S->isListInitialization());
 }
 
+void StmtProfiler::VisitDependentTemplateIdExpr(
+    const DependentTemplateIdExpr *S) {
+  VisitExpr(S);
+  VisitTemplateName(S->getTemplateName());
+  VisitTemplateArguments(S->template_arguments().data(),
+                         S->getNumTemplateArgs());
+}
+
 void StmtProfiler::VisitCXXDependentScopeMemberExpr(
     const CXXDependentScopeMemberExpr *S) {
   ID.AddBoolean(S->isImplicitAccess());

diff  --git a/clang/lib/AST/TextNodeDumper.cpp 
b/clang/lib/AST/TextNodeDumper.cpp
index d07767aab2068..55e71cf58f248 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -1642,6 +1642,13 @@ void 
clang::TextNodeDumper::VisitDependentScopeDeclRefExpr(
   dumpNestedNameSpecifier(Node->getQualifier());
 }
 
+void clang::TextNodeDumper::VisitDependentTemplateIdExpr(
+    const DependentTemplateIdExpr *Node) {
+  OS << (Node->isConceptReference() ? " concept" : " variable template");
+  OS << ' ';
+  dumpBareTemplateName(Node->getTemplateName());
+}
+
 void TextNodeDumper::VisitUnresolvedLookupExpr(
     const UnresolvedLookupExpr *Node) {
   OS << " (";

diff  --git a/clang/lib/Sema/SemaExceptionSpec.cpp 
b/clang/lib/Sema/SemaExceptionSpec.cpp
index daa4bb5234b7b..8f08e17276ef3 100644
--- a/clang/lib/Sema/SemaExceptionSpec.cpp
+++ b/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -1385,6 +1385,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
   case Expr::UnaryExprOrTypeTraitExprClass:
   case Expr::UnresolvedLookupExprClass:
   case Expr::UnresolvedMemberExprClass:
+  case Expr::DependentTemplateIdExprClass:
     // FIXME: Many of the above can throw.
     return CT_Cannot;
 

diff  --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index c1d8c3500a9a9..1fde412749cc9 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -1238,9 +1238,10 @@ static ExprResult formImmediatelyDeclaredConstraint(
   }
   // We have a template template parameter
   else {
+    assert(SS.isEmpty() && "template parameter with a scope specifier?");
     auto *CDT = dyn_cast<TemplateTemplateParmDecl>(NamedConcept);
-    ImmediatelyDeclaredConstraint = S.CheckVarOrConceptTemplateTemplateId(
-        SS, NameInfo, CDT, SourceLocation(), &ConstraintArgs);
+    ImmediatelyDeclaredConstraint =
+        S.CheckVarOrConceptTemplateTemplateId(NameInfo, CDT, &ConstraintArgs);
   }
   if (ImmediatelyDeclaredConstraint.isInvalid() || !EllipsisLoc.isValid())
     return ImmediatelyDeclaredConstraint;
@@ -2789,16 +2790,10 @@ 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 VisitDependentTemplateIdExpr(DependentTemplateIdExpr *E) override {
+    if (Matches(E->getParameter()->getDepth(), E->getExprLoc()))
+      return false;
+    return DynamicRecursiveASTVisitor::VisitDependentTemplateIdExpr(E);
   }
 
   bool VisitSubstTemplateTypeParmType(SubstTemplateTypeParmType *T) override {
@@ -4836,8 +4831,7 @@ ExprResult Sema::CheckVarTemplateId(
 }
 
 ExprResult Sema::CheckVarOrConceptTemplateTemplateId(
-    const CXXScopeSpec &SS, const DeclarationNameInfo &NameInfo,
-    TemplateTemplateParmDecl *Template, SourceLocation TemplateLoc,
+    const DeclarationNameInfo &NameInfo, TemplateTemplateParmDecl *Template,
     const TemplateArgumentListInfo *TemplateArgs) {
   assert(Template && "A variable template id without template?");
 
@@ -4849,7 +4843,7 @@ ExprResult Sema::CheckVarOrConceptTemplateTemplateId(
   // Check that the template argument list is well-formed for this template.
   CheckTemplateArgumentInfo CTAI;
   if (CheckTemplateArgumentList(
-          Template, TemplateLoc,
+          Template, /*Template kw loc=*/{},
           // FIXME: TemplateArgs will not be modified because
           // UpdateArgsWithConversions is false, however, we should
           // CheckTemplateArgumentList to be const-correct.
@@ -4858,18 +4852,8 @@ ExprResult Sema::CheckVarOrConceptTemplateTemplateId(
           /*UpdateArgsWithConversions=*/false))
     return true;
 
-  UnresolvedSet<1> R;
-  R.addDecl(Template);
-
-  // FIXME: We model references to variable template and concept parameters
-  // as an UnresolvedLookupExpr. This is because they encapsulate the same
-  // data, can generally be used in the same places and work the same way.
-  // However, it might be cleaner to use a dedicated AST node in the long run.
-  return UnresolvedLookupExpr::Create(
-      getASTContext(), nullptr, SS.getWithLocInContext(getASTContext()),
-      SourceLocation(), NameInfo, false, TemplateArgs, R.begin(), R.end(),
-      /*KnownDependent=*/false,
-      /*KnownInstantiationDependent=*/false);
+  return DependentTemplateIdExpr::Create(getASTContext(), NameInfo,
+                                         TemplateName(Template), 
*TemplateArgs);
 }
 
 void Sema::diagnoseMissingTemplateArguments(TemplateName Name,
@@ -4999,6 +4983,8 @@ ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec 
&SS,
   R.suppressDiagnostics();
 
   if (R.getAsSingle<ConceptDecl>()) {
+    assert(TemplateKWLoc.isInvalid() &&
+           "template keyword in front of a concept id?");
     return CheckConceptTemplateId(SS, TemplateKWLoc, R.getLookupNameInfo(),
                                   R.getRepresentativeDecl(),
                                   R.getAsSingle<ConceptDecl>(), TemplateArgs);
@@ -5007,10 +4993,14 @@ ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec 
&SS,
   // Check variable template ids (C++17) and concept template parameters
   // (C++26).
   UnresolvedLookupExpr *ULE;
-  if (R.getAsSingle<TemplateTemplateParmDecl>())
+  if (R.getAsSingle<TemplateTemplateParmDecl>()) {
+    assert(SS.isEmpty() && "template parameter with a scope specifier?");
+    assert(TemplateKWLoc.isInvalid() &&
+           "template keyword in front of a template parameter?");
     return CheckVarOrConceptTemplateTemplateId(
-        SS, R.getLookupNameInfo(), R.getAsSingle<TemplateTemplateParmDecl>(),
-        TemplateKWLoc, TemplateArgs);
+        R.getLookupNameInfo(), R.getAsSingle<TemplateTemplateParmDecl>(),
+        TemplateArgs);
+  }
 
   // Function templates
   ULE = UnresolvedLookupExpr::Create(
@@ -8704,10 +8694,8 @@ static bool 
CheckNonTypeTemplatePartialSpecializationArgs(
       if (isa<NonTypeTemplateParmDecl>(DRE->getDecl()))
         continue;
 
-    if (auto *ULE = dyn_cast<UnresolvedLookupExpr>(ArgExpr);
-        ULE && (ULE->isConceptReference() || ULE->isVarDeclReference())) {
+    if (isa<DependentTemplateIdExpr>(ArgExpr))
       continue;
-    }
 
     // C++ [temp.class.spec]p9:
     //   Within the argument list of a class template partial

diff  --git a/clang/lib/Sema/SemaTemplateDeduction.cpp 
b/clang/lib/Sema/SemaTemplateDeduction.cpp
index c57a090a8553e..21190031b95ea 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -247,14 +247,10 @@ getDeducedNTTParameterFromExpr(const Expr *E, unsigned 
Depth) {
       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 (const auto *DTI = dyn_cast<DependentTemplateIdExpr>(E))
+    if (DTI->getParameter()->getDepth() == Depth)
+      return DTI->getParameter();
 
-      if (TTP->getDepth() == Depth)
-        return TTP;
-    }
-  }
   return nullptr;
 }
 
@@ -6872,15 +6868,10 @@ struct MarkUsedTemplateParameterVisitor : 
DynamicRecursiveASTVisitor {
     return true;
   }
 
-  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())
-        DynamicRecursiveASTVisitor::TraverseTemplateArgumentLoc(TLoc);
-    }
+  bool VisitDependentTemplateIdExpr(DependentTemplateIdExpr *E) override {
+    TemplateTemplateParmDecl *TTP = E->getParameter();
+    if (TTP->getDepth() == Depth)
+      Used[TTP->getIndex()] = true;
     return true;
   }
 
@@ -6909,11 +6900,10 @@ MarkUsedTemplateParameters(ASTContext &Ctx,
     E = Expansion->getPattern();
 
   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())
+
+  if (const auto *DTI = dyn_cast<DependentTemplateIdExpr>(E)) {
+    Used[DTI->getParameter()->getIndex()] = true;
+    for (const auto &TLoc : DTI->template_arguments())
       MarkUsedTemplateParameters(Ctx, TLoc.getArgument(), OnlyDeduced, Depth,
                                  Used);
     return;

diff  --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp 
b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index d2c72753aee79..f107811be868f 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -4588,6 +4588,7 @@ ExprResult Sema::SubstConceptTemplateArguments(
       case Stmt::ConceptSpecializationExprClass:
       case Stmt::ParenExprClass:
       case Stmt::UnresolvedLookupExprClass:
+      case Stmt::DependentTemplateIdExprClass:
         return Base::TransformExpr(E);
       default:
         break;
@@ -4625,43 +4626,46 @@ ExprResult Sema::SubstConceptTemplateArguments(
       return false;
     }
 
-    ExprResult TransformUnresolvedLookupExpr(UnresolvedLookupExpr *E,
-                                             bool IsAddressOfOperand = false) {
-      if (!E->isConceptReference())
-        return E;
-
-      assert(E->getNumDecls() == 1 &&
-             "ConceptReference must have single declaration");
-      NamedDecl *D = *E->decls_begin();
-      ConceptDecl *ResolvedConcept = nullptr;
-
-      if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(D)) {
-        unsigned Depth = TTP->getDepth();
-        unsigned Pos = TTP->getPosition();
-        if (Depth < MLTAL.getNumLevels() &&
-            MLTAL.hasTemplateArgument(Depth, Pos)) {
-          TemplateArgument Arg = MLTAL(Depth, Pos);
-          assert(Arg.getKind() == TemplateArgument::Template);
-          ResolvedConcept =
-              dyn_cast<ConceptDecl>(Arg.getAsTemplate().getAsTemplateDecl());
-        }
-        if (ResolvedConcept == nullptr)
-          return E;
-      } else
-        ResolvedConcept = cast<ConceptDecl>(D);
-
-      TemplateArgumentListInfo TransArgs(E->getLAngleLoc(), E->getRAngleLoc());
-      if (TransformTemplateArguments(E->getTemplateArgs(),
-                                     E->getNumTemplateArgs(), TransArgs))
+    ExprResult RebuildConceptSpecialization(ConceptDecl *ResolvedConcept,
+                                            SourceLocation NameLoc,
+                                            SourceLocation LAngleLoc,
+                                            SourceLocation RAngleLoc,
+                                            const TemplateArgumentLoc *Args,
+                                            unsigned NumArgs) {
+      TemplateArgumentListInfo TransArgs(LAngleLoc, RAngleLoc);
+      if (TransformTemplateArguments(Args, NumArgs, TransArgs))
         return ExprError();
 
       CXXScopeSpec SS;
-      DeclarationNameInfo NameInfo(ResolvedConcept->getDeclName(),
-                                   E->getNameLoc());
+      DeclarationNameInfo NameInfo(ResolvedConcept->getDeclName(), NameLoc);
       return SemaRef.CheckConceptTemplateId(SS, SourceLocation(), NameInfo,
                                             ResolvedConcept, ResolvedConcept,
                                             &TransArgs, false);
     }
+
+    ExprResult TransformDependentTemplateIdExpr(DependentTemplateIdExpr *E) {
+      if (!E->isConceptReference())
+        return E;
+
+      TemplateTemplateParmDecl *TTP = E->getParameter();
+      unsigned Depth = TTP->getDepth();
+      unsigned Pos = TTP->getPosition();
+      ConceptDecl *ResolvedConcept = nullptr;
+
+      if (MLTAL.hasTemplateArgument(Depth, Pos)) {
+        TemplateArgument Arg = MLTAL(Depth, Pos);
+        assert(Arg.getKind() == TemplateArgument::Template);
+        ResolvedConcept =
+            dyn_cast<ConceptDecl>(Arg.getAsTemplate().getAsTemplateDecl());
+      }
+      if (!ResolvedConcept)
+        return E;
+
+      return RebuildConceptSpecialization(ResolvedConcept, E->getNameLoc(),
+                                          E->getLAngleLoc(), E->getRAngleLoc(),
+                                          E->template_arguments().data(),
+                                          E->getNumTemplateArgs());
+    }
   };
 
   ConstraintExprTransformer Transformer(*this, MLTALForConstraint);

diff  --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index e6abb3ad577c6..b397447ee0e52 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -16532,6 +16532,36 @@ 
TreeTransform<Derived>::TransformCXXUnresolvedConstructExpr(
       T, E->getLParenLoc(), Args, E->getRParenLoc(), 
E->isListInitialization());
 }
 
+template <typename Derived>
+ExprResult TreeTransform<Derived>::TransformDependentTemplateIdExpr(
+    DependentTemplateIdExpr *E) {
+
+  NestedNameSpecifierLoc Loc;
+  TemplateName Name = getDerived().TransformTemplateName(
+      Loc, /*Template Keyword=*/SourceLocation(), E->getTemplateName(),
+      E->getNameLoc());
+  if (Name.isNull())
+    return ExprError();
+
+  TemplateDecl *TD = Name.getAsTemplateDecl();
+
+  assert(TD && "A dependent template id always refers to a template decl");
+
+  TemplateArgumentListInfo TransArgs(E->getLAngleLoc(), E->getRAngleLoc());
+  if (getDerived().TransformTemplateArguments(
+          E->template_arguments().data(), E->getNumTemplateArgs(), TransArgs))
+    return ExprError();
+
+  CXXScopeSpec SS;
+
+  LookupResult R(SemaRef, E->getNameInfo(), Sema::LookupOrdinaryName);
+  R.addDecl(TD);
+  R.resolveKind();
+  return getDerived().RebuildTemplateIdExpr(
+      SS, /*Template Keyword=*/SourceLocation(), R,
+      /*RequiresADL=*/false, &TransArgs);
+}
+
 template<typename Derived>
 ExprResult
 TreeTransform<Derived>::TransformCXXDependentScopeMemberExpr(

diff  --git a/clang/lib/Serialization/ASTReaderStmt.cpp 
b/clang/lib/Serialization/ASTReaderStmt.cpp
index 1afde95b05290..c2d8267b6fcea 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -2095,6 +2095,17 @@ void 
ASTStmtReader::VisitExprWithCleanups(ExprWithCleanups *E) {
   E->SubExpr = Record.readSubExpr();
 }
 
+void ASTStmtReader::VisitDependentTemplateIdExpr(DependentTemplateIdExpr *E) {
+  VisitExpr(E);
+  unsigned NumTemplateArgs = Record.readInt();
+  assert(NumTemplateArgs == E->getNumTemplateArgs() &&
+         "Wrong NumTemplateArgs!");
+  ReadTemplateKWAndArgsInfo(E->KWAndArgs, E->getTrailingObjects(),
+                            NumTemplateArgs);
+  E->NameInfo = Record.readDeclarationNameInfo();
+  E->Name = Record.readTemplateName();
+}
+
 void ASTStmtReader::VisitCXXDependentScopeMemberExpr(
     CXXDependentScopeMemberExpr *E) {
   VisitExpr(E);
@@ -4409,6 +4420,11 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
       break;
     }
 
+    case EXPR_DEPENDENT_TEMPLATE_ID:
+      S = DependentTemplateIdExpr::CreateEmpty(
+          Context, /*NumTemplateArgs=*/Record[ASTStmtReader::NumExprFields]);
+      break;
+
     case EXPR_CXX_DEPENDENT_SCOPE_DECL_REF: {
       BitsUnpacker DependentScopeDeclRefBits(
           Record[ASTStmtReader::NumStmtFields]);

diff  --git a/clang/lib/Serialization/ASTWriter.cpp 
b/clang/lib/Serialization/ASTWriter.cpp
index f34b2ff182bc8..4e9af7c721d2f 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -858,6 +858,7 @@ static void AddStmtsExprs(llvm::BitstreamWriter &Stream,
   RECORD(EXPR_CXX_PSEUDO_DESTRUCTOR);
   RECORD(EXPR_EXPR_WITH_CLEANUPS);
   RECORD(EXPR_CXX_DEPENDENT_SCOPE_MEMBER);
+  RECORD(EXPR_DEPENDENT_TEMPLATE_ID);
   RECORD(EXPR_CXX_DEPENDENT_SCOPE_DECL_REF);
   RECORD(EXPR_CXX_UNRESOLVED_CONSTRUCT);
   RECORD(EXPR_CXX_UNRESOLVED_MEMBER);

diff  --git a/clang/lib/Serialization/ASTWriterStmt.cpp 
b/clang/lib/Serialization/ASTWriterStmt.cpp
index 3854eabd62a46..bf90b247fd6ab 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -2110,6 +2110,15 @@ void 
ASTStmtWriter::VisitExprWithCleanups(ExprWithCleanups *E) {
   Code = serialization::EXPR_EXPR_WITH_CLEANUPS;
 }
 
+void ASTStmtWriter::VisitDependentTemplateIdExpr(DependentTemplateIdExpr *E) {
+  VisitExpr(E);
+  Record.push_back(E->getNumTemplateArgs());
+  AddTemplateKWAndArgsInfo(E->KWAndArgs, E->getTrailingObjects());
+  Record.AddDeclarationNameInfo(E->getNameInfo());
+  Record.AddTemplateName(E->getTemplateName());
+  Code = serialization::EXPR_DEPENDENT_TEMPLATE_ID;
+}
+
 void ASTStmtWriter::VisitCXXDependentScopeMemberExpr(
     CXXDependentScopeMemberExpr *E) {
   VisitExpr(E);

diff  --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp 
b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 41cc820fd547b..e6349eb4eba2a 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1701,6 +1701,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
     case Stmt::ExpressionTraitExprClass:
     case Stmt::UnresolvedLookupExprClass:
     case Stmt::UnresolvedMemberExprClass:
+    case Stmt::DependentTemplateIdExprClass:
     case Stmt::RecoveryExprClass:
     case Stmt::CXXNoexceptExprClass:
     case Stmt::PackExpansionExprClass:

diff  --git a/clang/tools/libclang/CXCursor.cpp 
b/clang/tools/libclang/CXCursor.cpp
index e27964d0ad6e1..768f5465a166a 100644
--- a/clang/tools/libclang/CXCursor.cpp
+++ b/clang/tools/libclang/CXCursor.cpp
@@ -614,6 +614,7 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl 
*Parent,
   case Stmt::SubstNonTypeTemplateParmPackExprClass:
   case Stmt::FunctionParmPackExprClass:
   case Stmt::UnresolvedLookupExprClass:
+  case Stmt::DependentTemplateIdExprClass:
     K = CXCursor_DeclRefExpr;
     break;
 


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to