[clang] [Clang][NFC] Introduce a node to model concept and variable template template parameters (PR #216729)
Corentin Jabot via cfe-commits
cfe-commits at lists.llvm.org
Tue Aug 18 11:52:05 PDT 2026
https://github.com/cor3ntin updated https://github.com/llvm/llvm-project/pull/216729
>From 386aa5d9af53b7f95bb51f1232d970db2bde766c Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Mon, 17 Aug 2026 15:10:26 +0200
Subject: [PATCH 1/7] [Clang] Introduce a node to model concept and variable
template template parameter.
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.
---
clang/include/clang/AST/ComputeDependence.h | 2 +
clang/include/clang/AST/ExprCXX.h | 132 ++++++++++++++----
clang/include/clang/AST/RecursiveASTVisitor.h | 7 +
clang/include/clang/AST/TextNodeDumper.h | 1 +
clang/include/clang/Basic/StmtNodes.td | 1 +
.../include/clang/Serialization/ASTBitCodes.h | 1 +
clang/lib/AST/ASTImporter.cpp | 27 ++++
clang/lib/AST/ComputeDependence.cpp | 14 +-
clang/lib/AST/Expr.cpp | 1 +
clang/lib/AST/ExprCXX.cpp | 56 ++++++--
clang/lib/AST/ExprClassification.cpp | 1 +
clang/lib/AST/ExprConstant.cpp | 1 +
clang/lib/AST/ItaniumMangle.cpp | 9 ++
clang/lib/AST/StmtPrinter.cpp | 9 ++
clang/lib/AST/StmtProfile.cpp | 9 ++
clang/lib/AST/TextNodeDumper.cpp | 8 ++
clang/lib/Sema/SemaExceptionSpec.cpp | 1 +
clang/lib/Sema/SemaTemplate.cpp | 33 ++---
clang/lib/Sema/SemaTemplateDeduction.cpp | 32 ++---
clang/lib/Sema/SemaTemplateInstantiate.cpp | 64 +++++----
clang/lib/Sema/TreeTransform.h | 35 +++++
clang/lib/Serialization/ASTReaderStmt.cpp | 18 +++
clang/lib/Serialization/ASTWriter.cpp | 1 +
clang/lib/Serialization/ASTWriterStmt.cpp | 11 ++
clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 1 +
clang/tools/libclang/CXCursor.cpp | 1 +
26 files changed, 358 insertions(+), 118 deletions(-)
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..8a234b953e44a 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,110 @@ 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,
+ ASTTemplateKWAndArgsInfo,
+ TemplateArgumentLoc> {
+ friend class ASTStmtReader;
+ friend class ASTStmtWriter;
+ friend TrailingObjects;
+
+ NestedNameSpecifierLoc QualifierLoc;
+
+ DeclarationNameInfo NameInfo;
+
+ TemplateName Name;
+
+ DependentTemplateIdExpr(const ASTContext &Context,
+ NestedNameSpecifierLoc QualifierLoc,
+ SourceLocation TemplateKWLoc,
+ const DeclarationNameInfo &NameInfo,
+ TemplateName Name,
+ const TemplateArgumentListInfo &TemplateArgs);
+
+ DependentTemplateIdExpr(EmptyShell Empty, unsigned NumTemplateArgs);
+
+ unsigned numTrailingObjects(OverloadToken<ASTTemplateKWAndArgsInfo>) const {
+ return 1;
+ }
+
+public:
+ static DependentTemplateIdExpr *
+ Create(const ASTContext &Context, NestedNameSpecifierLoc QualifierLoc,
+ SourceLocation TemplateKWLoc, const DeclarationNameInfo &NameInfo,
+ TemplateName Name, const TemplateArgumentListInfo &TemplateArgs);
+
+ static DependentTemplateIdExpr *CreateEmpty(const ASTContext &Context,
+ unsigned NumTemplateArgs);
+
+ NestedNameSpecifierLoc getQualifierLoc() const { return QualifierLoc; }
+ NestedNameSpecifier getQualifier() const {
+ return QualifierLoc.getNestedNameSpecifier();
+ }
+
+ 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 getTemplateKeywordLoc() const {
+ return getTrailingObjects<ASTTemplateKWAndArgsInfo>()->TemplateKWLoc;
+ }
+ SourceLocation getLAngleLoc() const {
+ return getTrailingObjects<ASTTemplateKWAndArgsInfo>()->LAngleLoc;
+ }
+ SourceLocation getRAngleLoc() const {
+ return getTrailingObjects<ASTTemplateKWAndArgsInfo>()->RAngleLoc;
+ }
+
+ unsigned getNumTemplateArgs() const {
+ return getTrailingObjects<ASTTemplateKWAndArgsInfo>()->NumTemplateArgs;
+ }
+
+ ArrayRef<TemplateArgumentLoc> template_arguments() const {
+ return getTrailingObjects<TemplateArgumentLoc>(getNumTemplateArgs());
+ }
+
+ SourceLocation getBeginLoc() const {
+ if (QualifierLoc)
+ return QualifierLoc.getBeginLoc();
+ if (SourceLocation TemplateKWLoc = getTemplateKeywordLoc();
+ TemplateKWLoc.isValid())
+ return TemplateKWLoc;
+ return getNameLoc();
+ }
+
+ SourceLocation getEndLoc() const {
+ if (SourceLocation RAngleLoc = getRAngleLoc(); RAngleLoc.isValid())
+ return RAngleLoc;
+ return NameInfo.getEndLoc();
+ }
+
+ 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..01f8f960c9039 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -2646,6 +2646,13 @@ DEF_TRAVERSE_STMT(CXXDependentScopeMemberExpr, {
}
})
+DEF_TRAVERSE_STMT(DependentTemplateIdExpr, {
+ TRY_TO(TraverseNestedNameSpecifierLoc(S->getQualifierLoc()));
+ 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/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/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..af2950af2134f 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,32 @@ ExpectedStmt ASTNodeImporter::VisitCXXDependentScopeMemberExpr(
ToMemberNameInfo, ResInfo);
}
+ExpectedStmt
+ASTNodeImporter::VisitDependentTemplateIdExpr(DependentTemplateIdExpr *E) {
+ Error Err = Error::success();
+ auto ToQualifierLoc = importChecked(Err, E->getQualifierLoc());
+ auto ToTemplateKeywordLoc = importChecked(Err, E->getTemplateKeywordLoc());
+ 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(),
+ ToQualifierLoc, ToTemplateKeywordLoc,
+ 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..0ee723ee5f04f 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,18 @@ 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 |= toExprDependence(E->getQualifier().getDependence());
+ 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..484a2d06c857d 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -401,14 +401,45 @@ 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, NestedNameSpecifierLoc QualifierLoc,
+ SourceLocation TemplateKWLoc, const DeclarationNameInfo &NameInfo,
+ TemplateName Name, const TemplateArgumentListInfo &TemplateArgs)
+ : Expr(DependentTemplateIdExprClass, Context.DependentTy, VK_LValue,
+ OK_Ordinary),
+ QualifierLoc(QualifierLoc), NameInfo(NameInfo), Name(Name) {
+ getTrailingObjects<ASTTemplateKWAndArgsInfo>()->initializeFrom(
+ TemplateKWLoc, TemplateArgs, getTrailingObjects<TemplateArgumentLoc>());
+ setDependence(computeDependence(this));
+}
+
+DependentTemplateIdExpr::DependentTemplateIdExpr(EmptyShell Empty,
+ unsigned NumTemplateArgs)
+ : Expr(DependentTemplateIdExprClass, Empty) {
+ getTrailingObjects<ASTTemplateKWAndArgsInfo>()->NumTemplateArgs =
+ NumTemplateArgs;
+}
+
+DependentTemplateIdExpr *DependentTemplateIdExpr::Create(
+ const ASTContext &Context, NestedNameSpecifierLoc QualifierLoc,
+ SourceLocation TemplateKWLoc, const DeclarationNameInfo &NameInfo,
+ TemplateName Name, const TemplateArgumentListInfo &TemplateArgs) {
+ void *Mem = Context.Allocate(
+ totalSizeToAlloc<ASTTemplateKWAndArgsInfo, TemplateArgumentLoc>(
+ 1, TemplateArgs.size()),
+ alignof(DependentTemplateIdExpr));
+ return new (Mem) DependentTemplateIdExpr(Context, QualifierLoc, TemplateKWLoc,
+ NameInfo, Name, TemplateArgs);
+}
+
+DependentTemplateIdExpr *
+DependentTemplateIdExpr::CreateEmpty(const ASTContext &Context,
+ unsigned NumTemplateArgs) {
+ void *Mem = Context.Allocate(
+ totalSizeToAlloc<ASTTemplateKWAndArgsInfo, TemplateArgumentLoc>(
+ 1, NumTemplateArgs),
+ alignof(DependentTemplateIdExpr));
+ return new (Mem) DependentTemplateIdExpr(EmptyShell(), NumTemplateArgs);
}
// UnresolvedLookupExpr
@@ -419,11 +450,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..e89c10372a279 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(DTI->getQualifier(), 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..4bbf9e083fd29 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -2671,6 +2671,15 @@ void StmtPrinter::VisitCXXReflectExpr(CXXReflectExpr *S) {
assert(false && "not implemented yet");
}
+void StmtPrinter::VisitDependentTemplateIdExpr(DependentTemplateIdExpr *Node) {
+ Node->getQualifier().print(OS, Policy);
+ if (Node->getTemplateKeywordLoc().isValid())
+ OS << "template ";
+ 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..65673971b2096 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -2353,6 +2353,15 @@ void StmtProfiler::VisitCXXUnresolvedConstructExpr(
ID.AddInteger(S->isListInitialization());
}
+void StmtProfiler::VisitDependentTemplateIdExpr(
+ const DependentTemplateIdExpr *S) {
+ VisitExpr(S);
+ VisitNestedNameSpecifier(S->getQualifier());
+ 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..3976969f7949c 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -1642,6 +1642,14 @@ void clang::TextNodeDumper::VisitDependentScopeDeclRefExpr(
dumpNestedNameSpecifier(Node->getQualifier());
}
+void clang::TextNodeDumper::VisitDependentTemplateIdExpr(
+ const DependentTemplateIdExpr *Node) {
+ OS << (Node->isConceptReference() ? " concept" : " variable template");
+ dumpNestedNameSpecifier(Node->getQualifier());
+ 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..d280ca46b583b 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -2789,16 +2789,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 {
@@ -4858,18 +4852,9 @@ 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(), SS.getWithLocInContext(getASTContext()), TemplateLoc,
+ NameInfo, TemplateName(Template), *TemplateArgs);
}
void Sema::diagnoseMissingTemplateArguments(TemplateName Name,
@@ -8704,10 +8689,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..77bc4866a9c1f 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 (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)
+ 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..f9222f2009116 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -16532,6 +16532,41 @@ TreeTransform<Derived>::TransformCXXUnresolvedConstructExpr(
T, E->getLParenLoc(), Args, E->getRParenLoc(), E->isListInitialization());
}
+template <typename Derived>
+ExprResult TreeTransform<Derived>::TransformDependentTemplateIdExpr(
+ DependentTemplateIdExpr *E) {
+ NestedNameSpecifierLoc QualifierLoc = E->getQualifierLoc();
+ if (QualifierLoc) {
+ QualifierLoc = getDerived().TransformNestedNameSpecifierLoc(QualifierLoc);
+ if (!QualifierLoc)
+ return ExprError();
+ }
+
+ TemplateName Name = getDerived().TransformTemplateName(
+ QualifierLoc, E->getTemplateKeywordLoc(), E->getTemplateName(),
+ E->getNameLoc());
+ if (Name.isNull())
+ return ExprError();
+
+ TemplateDecl *TD = Name.getAsTemplateDecl();
+ if (!TD)
+ return ExprError();
+
+ TemplateArgumentListInfo TransArgs(E->getLAngleLoc(), E->getRAngleLoc());
+ if (getDerived().TransformTemplateArguments(
+ E->template_arguments().data(), E->getNumTemplateArgs(), TransArgs))
+ return ExprError();
+
+ CXXScopeSpec SS;
+ SS.Adopt(QualifierLoc);
+
+ LookupResult R(SemaRef, E->getNameInfo(), Sema::LookupOrdinaryName);
+ R.addDecl(TD);
+ R.resolveKind();
+ return getDerived().RebuildTemplateIdExpr(SS, E->getTemplateKeywordLoc(), 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..6b8ba0bbcd863 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -2095,6 +2095,19 @@ 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->getTrailingObjects<ASTTemplateKWAndArgsInfo>(),
+ E->getTrailingObjects<TemplateArgumentLoc>(),
+ NumTemplateArgs);
+ E->QualifierLoc = Record.readNestedNameSpecifierLoc();
+ E->NameInfo = Record.readDeclarationNameInfo();
+ E->Name = Record.readTemplateName();
+}
+
void ASTStmtReader::VisitCXXDependentScopeMemberExpr(
CXXDependentScopeMemberExpr *E) {
VisitExpr(E);
@@ -4409,6 +4422,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..212ce74f62001 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -2110,6 +2110,17 @@ 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->getTrailingObjects<ASTTemplateKWAndArgsInfo>(),
+ E->getTrailingObjects<TemplateArgumentLoc>());
+ Record.AddNestedNameSpecifierLoc(E->getQualifierLoc());
+ 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;
>From ceb8a3539a4480c859bb1b7e088e6ac1279ec9df Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Mon, 17 Aug 2026 16:09:12 +0200
Subject: [PATCH 2/7] remove spurious line, comment the
ASTTemplateKWAndArgsInfo weirdness
---
clang/include/clang/AST/ExprCXX.h | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 8a234b953e44a..24f44dbe6e7d8 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -3472,9 +3472,7 @@ class DependentTemplateIdExpr final
friend TrailingObjects;
NestedNameSpecifierLoc QualifierLoc;
-
DeclarationNameInfo NameInfo;
-
TemplateName Name;
DependentTemplateIdExpr(const ASTContext &Context,
@@ -3486,6 +3484,9 @@ class DependentTemplateIdExpr final
DependentTemplateIdExpr(EmptyShell Empty, unsigned NumTemplateArgs);
+ // ASTTemplateKWAndArgsInfo expects to be allocated
+ // before the template arguments, and as such is a trailing object,
+ // even though it is always present.
unsigned numTrailingObjects(OverloadToken<ASTTemplateKWAndArgsInfo>) const {
return 1;
}
>From 0d107d16ef7d9e0e7115f04eee2f8fd760ed7821 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Mon, 17 Aug 2026 16:47:48 +0200
Subject: [PATCH 3/7] Make ASTTemplateKWAndArgsInfo a member
---
clang/include/clang/AST/ExprCXX.h | 25 ++++++-----------------
clang/include/clang/AST/TemplateBase.h | 2 +-
clang/lib/AST/ExprCXX.cpp | 16 ++++++---------
clang/lib/Serialization/ASTReaderStmt.cpp | 3 +--
clang/lib/Serialization/ASTWriterStmt.cpp | 3 +--
5 files changed, 15 insertions(+), 34 deletions(-)
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 24f44dbe6e7d8..971d4489a8ba0 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -3465,7 +3465,6 @@ class UnresolvedLookupExpr final
class DependentTemplateIdExpr final
: public Expr,
private llvm::TrailingObjects<DependentTemplateIdExpr,
- ASTTemplateKWAndArgsInfo,
TemplateArgumentLoc> {
friend class ASTStmtReader;
friend class ASTStmtWriter;
@@ -3474,6 +3473,7 @@ class DependentTemplateIdExpr final
NestedNameSpecifierLoc QualifierLoc;
DeclarationNameInfo NameInfo;
TemplateName Name;
+ ASTTemplateKWAndArgsInfo KWAndArgs;
DependentTemplateIdExpr(const ASTContext &Context,
NestedNameSpecifierLoc QualifierLoc,
@@ -3484,13 +3484,6 @@ class DependentTemplateIdExpr final
DependentTemplateIdExpr(EmptyShell Empty, unsigned NumTemplateArgs);
- // ASTTemplateKWAndArgsInfo expects to be allocated
- // before the template arguments, and as such is a trailing object,
- // even though it is always present.
- unsigned numTrailingObjects(OverloadToken<ASTTemplateKWAndArgsInfo>) const {
- return 1;
- }
-
public:
static DependentTemplateIdExpr *
Create(const ASTContext &Context, NestedNameSpecifierLoc QualifierLoc,
@@ -3520,21 +3513,15 @@ class DependentTemplateIdExpr final
}
SourceLocation getTemplateKeywordLoc() const {
- return getTrailingObjects<ASTTemplateKWAndArgsInfo>()->TemplateKWLoc;
- }
- SourceLocation getLAngleLoc() const {
- return getTrailingObjects<ASTTemplateKWAndArgsInfo>()->LAngleLoc;
- }
- SourceLocation getRAngleLoc() const {
- return getTrailingObjects<ASTTemplateKWAndArgsInfo>()->RAngleLoc;
+ return KWAndArgs.TemplateKWLoc;
}
+ SourceLocation getLAngleLoc() const { return KWAndArgs.LAngleLoc; }
+ SourceLocation getRAngleLoc() const { return KWAndArgs.RAngleLoc; }
- unsigned getNumTemplateArgs() const {
- return getTrailingObjects<ASTTemplateKWAndArgsInfo>()->NumTemplateArgs;
- }
+ unsigned getNumTemplateArgs() const { return KWAndArgs.NumTemplateArgs; }
ArrayRef<TemplateArgumentLoc> template_arguments() const {
- return getTrailingObjects<TemplateArgumentLoc>(getNumTemplateArgs());
+ return getTrailingObjects(getNumTemplateArgs());
}
SourceLocation getBeginLoc() const {
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/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index 484a2d06c857d..16e3238c950ec 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -408,16 +408,14 @@ DependentTemplateIdExpr::DependentTemplateIdExpr(
: Expr(DependentTemplateIdExprClass, Context.DependentTy, VK_LValue,
OK_Ordinary),
QualifierLoc(QualifierLoc), NameInfo(NameInfo), Name(Name) {
- getTrailingObjects<ASTTemplateKWAndArgsInfo>()->initializeFrom(
- TemplateKWLoc, TemplateArgs, getTrailingObjects<TemplateArgumentLoc>());
+ KWAndArgs.initializeFrom(TemplateKWLoc, TemplateArgs, getTrailingObjects());
setDependence(computeDependence(this));
}
DependentTemplateIdExpr::DependentTemplateIdExpr(EmptyShell Empty,
unsigned NumTemplateArgs)
: Expr(DependentTemplateIdExprClass, Empty) {
- getTrailingObjects<ASTTemplateKWAndArgsInfo>()->NumTemplateArgs =
- NumTemplateArgs;
+ KWAndArgs.NumTemplateArgs = NumTemplateArgs;
}
DependentTemplateIdExpr *DependentTemplateIdExpr::Create(
@@ -425,8 +423,7 @@ DependentTemplateIdExpr *DependentTemplateIdExpr::Create(
SourceLocation TemplateKWLoc, const DeclarationNameInfo &NameInfo,
TemplateName Name, const TemplateArgumentListInfo &TemplateArgs) {
void *Mem = Context.Allocate(
- totalSizeToAlloc<ASTTemplateKWAndArgsInfo, TemplateArgumentLoc>(
- 1, TemplateArgs.size()),
+ totalSizeToAlloc<TemplateArgumentLoc>(TemplateArgs.size()),
alignof(DependentTemplateIdExpr));
return new (Mem) DependentTemplateIdExpr(Context, QualifierLoc, TemplateKWLoc,
NameInfo, Name, TemplateArgs);
@@ -435,10 +432,9 @@ DependentTemplateIdExpr *DependentTemplateIdExpr::Create(
DependentTemplateIdExpr *
DependentTemplateIdExpr::CreateEmpty(const ASTContext &Context,
unsigned NumTemplateArgs) {
- void *Mem = Context.Allocate(
- totalSizeToAlloc<ASTTemplateKWAndArgsInfo, TemplateArgumentLoc>(
- 1, NumTemplateArgs),
- alignof(DependentTemplateIdExpr));
+ void *Mem =
+ Context.Allocate(totalSizeToAlloc<TemplateArgumentLoc>(NumTemplateArgs),
+ alignof(DependentTemplateIdExpr));
return new (Mem) DependentTemplateIdExpr(EmptyShell(), NumTemplateArgs);
}
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 6b8ba0bbcd863..66c0a1581b5da 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -2100,8 +2100,7 @@ void ASTStmtReader::VisitDependentTemplateIdExpr(DependentTemplateIdExpr *E) {
unsigned NumTemplateArgs = Record.readInt();
assert(NumTemplateArgs == E->getNumTemplateArgs() &&
"Wrong NumTemplateArgs!");
- ReadTemplateKWAndArgsInfo(*E->getTrailingObjects<ASTTemplateKWAndArgsInfo>(),
- E->getTrailingObjects<TemplateArgumentLoc>(),
+ ReadTemplateKWAndArgsInfo(E->KWAndArgs, E->getTrailingObjects(),
NumTemplateArgs);
E->QualifierLoc = Record.readNestedNameSpecifierLoc();
E->NameInfo = Record.readDeclarationNameInfo();
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index 212ce74f62001..27d124f2fb481 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -2113,8 +2113,7 @@ void ASTStmtWriter::VisitExprWithCleanups(ExprWithCleanups *E) {
void ASTStmtWriter::VisitDependentTemplateIdExpr(DependentTemplateIdExpr *E) {
VisitExpr(E);
Record.push_back(E->getNumTemplateArgs());
- AddTemplateKWAndArgsInfo(*E->getTrailingObjects<ASTTemplateKWAndArgsInfo>(),
- E->getTrailingObjects<TemplateArgumentLoc>());
+ AddTemplateKWAndArgsInfo(E->KWAndArgs, E->getTrailingObjects());
Record.AddNestedNameSpecifierLoc(E->getQualifierLoc());
Record.AddDeclarationNameInfo(E->getNameInfo());
Record.AddTemplateName(E->getTemplateName());
>From 9e93910783bf156bb1496b2a7ecdba048318ad53 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Mon, 17 Aug 2026 19:33:33 +0200
Subject: [PATCH 4/7] Remove unneeded NNS
---
clang/include/clang/AST/ExprCXX.h | 15 +++------------
clang/include/clang/AST/RecursiveASTVisitor.h | 1 -
clang/lib/AST/ASTImporter.cpp | 5 ++---
clang/lib/AST/ComputeDependence.cpp | 1 -
clang/lib/AST/ExprCXX.cpp | 18 +++++++++---------
clang/lib/AST/ItaniumMangle.cpp | 2 +-
clang/lib/AST/StmtPrinter.cpp | 1 -
clang/lib/AST/StmtProfile.cpp | 1 -
clang/lib/AST/TextNodeDumper.cpp | 1 -
clang/lib/Sema/SemaTemplate.cpp | 5 ++---
clang/lib/Sema/TreeTransform.h | 11 ++---------
clang/lib/Serialization/ASTReaderStmt.cpp | 1 -
clang/lib/Serialization/ASTWriterStmt.cpp | 1 -
13 files changed, 19 insertions(+), 44 deletions(-)
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 971d4489a8ba0..112c2e223eb84 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -3470,13 +3470,11 @@ class DependentTemplateIdExpr final
friend class ASTStmtWriter;
friend TrailingObjects;
- NestedNameSpecifierLoc QualifierLoc;
DeclarationNameInfo NameInfo;
TemplateName Name;
ASTTemplateKWAndArgsInfo KWAndArgs;
DependentTemplateIdExpr(const ASTContext &Context,
- NestedNameSpecifierLoc QualifierLoc,
SourceLocation TemplateKWLoc,
const DeclarationNameInfo &NameInfo,
TemplateName Name,
@@ -3486,18 +3484,13 @@ class DependentTemplateIdExpr final
public:
static DependentTemplateIdExpr *
- Create(const ASTContext &Context, NestedNameSpecifierLoc QualifierLoc,
- SourceLocation TemplateKWLoc, const DeclarationNameInfo &NameInfo,
- TemplateName Name, const TemplateArgumentListInfo &TemplateArgs);
+ Create(const ASTContext &Context, SourceLocation TemplateKWLoc,
+ const DeclarationNameInfo &NameInfo, TemplateName Name,
+ const TemplateArgumentListInfo &TemplateArgs);
static DependentTemplateIdExpr *CreateEmpty(const ASTContext &Context,
unsigned NumTemplateArgs);
- NestedNameSpecifierLoc getQualifierLoc() const { return QualifierLoc; }
- NestedNameSpecifier getQualifier() const {
- return QualifierLoc.getNestedNameSpecifier();
- }
-
const DeclarationNameInfo &getNameInfo() const { return NameInfo; }
DeclarationName getName() const { return NameInfo.getName(); }
SourceLocation getNameLoc() const { return NameInfo.getLoc(); }
@@ -3525,8 +3518,6 @@ class DependentTemplateIdExpr final
}
SourceLocation getBeginLoc() const {
- if (QualifierLoc)
- return QualifierLoc.getBeginLoc();
if (SourceLocation TemplateKWLoc = getTemplateKeywordLoc();
TemplateKWLoc.isValid())
return TemplateKWLoc;
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index 01f8f960c9039..1f7c8d762e1b5 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -2647,7 +2647,6 @@ DEF_TRAVERSE_STMT(CXXDependentScopeMemberExpr, {
})
DEF_TRAVERSE_STMT(DependentTemplateIdExpr, {
- TRY_TO(TraverseNestedNameSpecifierLoc(S->getQualifierLoc()));
TRY_TO(TraverseDeclarationNameInfo(S->getNameInfo()));
TRY_TO(TraverseTemplateArgumentLocsHelper(S->template_arguments().data(),
S->getNumTemplateArgs()));
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index af2950af2134f..eb1d5739ea3b4 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -8829,7 +8829,6 @@ ExpectedStmt ASTNodeImporter::VisitCXXDependentScopeMemberExpr(
ExpectedStmt
ASTNodeImporter::VisitDependentTemplateIdExpr(DependentTemplateIdExpr *E) {
Error Err = Error::success();
- auto ToQualifierLoc = importChecked(Err, E->getQualifierLoc());
auto ToTemplateKeywordLoc = importChecked(Err, E->getTemplateKeywordLoc());
auto ToName = importChecked(Err, E->getTemplateName());
auto ToDeclName = importChecked(Err, E->getName());
@@ -8848,8 +8847,8 @@ ASTNodeImporter::VisitDependentTemplateIdExpr(DependentTemplateIdExpr *E) {
return std::move(Err);
return DependentTemplateIdExpr::Create(Importer.getToContext(),
- ToQualifierLoc, ToTemplateKeywordLoc,
- ToNameInfo, ToName, ToTAInfo);
+ ToTemplateKeywordLoc, ToNameInfo,
+ ToName, ToTAInfo);
}
ExpectedStmt
diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp
index 0ee723ee5f04f..0fc9da18b9394 100644
--- a/clang/lib/AST/ComputeDependence.cpp
+++ b/clang/lib/AST/ComputeDependence.cpp
@@ -908,7 +908,6 @@ ExprDependence clang::computeDependence(DependentTemplateIdExpr *E) {
if (E->getTemplateName().getDependence() &
TemplateNameDependence::UnexpandedPack)
D |= ExprDependence::UnexpandedPack;
- D |= toExprDependence(E->getQualifier().getDependence());
D |= getDependenceInExpr(E->getNameInfo());
for (const auto &A : E->template_arguments())
D |= toExprDependence(A.getArgument().getDependence());
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index 16e3238c950ec..9bc6d65f1300b 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -402,12 +402,12 @@ SourceLocation CXXPseudoDestructorExpr::getEndLoc() const {
}
DependentTemplateIdExpr::DependentTemplateIdExpr(
- const ASTContext &Context, NestedNameSpecifierLoc QualifierLoc,
- SourceLocation TemplateKWLoc, const DeclarationNameInfo &NameInfo,
- TemplateName Name, const TemplateArgumentListInfo &TemplateArgs)
+ const ASTContext &Context, SourceLocation TemplateKWLoc,
+ const DeclarationNameInfo &NameInfo, TemplateName Name,
+ const TemplateArgumentListInfo &TemplateArgs)
: Expr(DependentTemplateIdExprClass, Context.DependentTy, VK_LValue,
OK_Ordinary),
- QualifierLoc(QualifierLoc), NameInfo(NameInfo), Name(Name) {
+ NameInfo(NameInfo), Name(Name) {
KWAndArgs.initializeFrom(TemplateKWLoc, TemplateArgs, getTrailingObjects());
setDependence(computeDependence(this));
}
@@ -419,14 +419,14 @@ DependentTemplateIdExpr::DependentTemplateIdExpr(EmptyShell Empty,
}
DependentTemplateIdExpr *DependentTemplateIdExpr::Create(
- const ASTContext &Context, NestedNameSpecifierLoc QualifierLoc,
- SourceLocation TemplateKWLoc, const DeclarationNameInfo &NameInfo,
- TemplateName Name, const TemplateArgumentListInfo &TemplateArgs) {
+ const ASTContext &Context, SourceLocation TemplateKWLoc,
+ const DeclarationNameInfo &NameInfo, TemplateName Name,
+ const TemplateArgumentListInfo &TemplateArgs) {
void *Mem = Context.Allocate(
totalSizeToAlloc<TemplateArgumentLoc>(TemplateArgs.size()),
alignof(DependentTemplateIdExpr));
- return new (Mem) DependentTemplateIdExpr(Context, QualifierLoc, TemplateKWLoc,
- NameInfo, Name, TemplateArgs);
+ return new (Mem) DependentTemplateIdExpr(Context, TemplateKWLoc, NameInfo,
+ Name, TemplateArgs);
}
DependentTemplateIdExpr *
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index e89c10372a279..c77d9d979e932 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -5320,7 +5320,7 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity,
case Expr::DependentTemplateIdExprClass: {
NotPrimaryExpr();
const auto *DTI = cast<DependentTemplateIdExpr>(E);
- mangleUnresolvedName(DTI->getQualifier(), DTI->getName(),
+ mangleUnresolvedName(NestedNameSpecifier(), DTI->getName(),
DTI->template_arguments().data(),
DTI->getNumTemplateArgs(), Arity);
break;
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index 4bbf9e083fd29..d7f69e87b7e5c 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -2672,7 +2672,6 @@ void StmtPrinter::VisitCXXReflectExpr(CXXReflectExpr *S) {
}
void StmtPrinter::VisitDependentTemplateIdExpr(DependentTemplateIdExpr *Node) {
- Node->getQualifier().print(OS, Policy);
if (Node->getTemplateKeywordLoc().isValid())
OS << "template ";
OS << Node->getNameInfo();
diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index 65673971b2096..dca2c49ac45d5 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -2356,7 +2356,6 @@ void StmtProfiler::VisitCXXUnresolvedConstructExpr(
void StmtProfiler::VisitDependentTemplateIdExpr(
const DependentTemplateIdExpr *S) {
VisitExpr(S);
- VisitNestedNameSpecifier(S->getQualifier());
VisitTemplateName(S->getTemplateName());
VisitTemplateArguments(S->template_arguments().data(),
S->getNumTemplateArgs());
diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index 3976969f7949c..55e71cf58f248 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -1645,7 +1645,6 @@ void clang::TextNodeDumper::VisitDependentScopeDeclRefExpr(
void clang::TextNodeDumper::VisitDependentTemplateIdExpr(
const DependentTemplateIdExpr *Node) {
OS << (Node->isConceptReference() ? " concept" : " variable template");
- dumpNestedNameSpecifier(Node->getQualifier());
OS << ' ';
dumpBareTemplateName(Node->getTemplateName());
}
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index d280ca46b583b..bc7e9c97cfa9d 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -4852,9 +4852,8 @@ ExprResult Sema::CheckVarOrConceptTemplateTemplateId(
/*UpdateArgsWithConversions=*/false))
return true;
- return DependentTemplateIdExpr::Create(
- getASTContext(), SS.getWithLocInContext(getASTContext()), TemplateLoc,
- NameInfo, TemplateName(Template), *TemplateArgs);
+ return DependentTemplateIdExpr::Create(getASTContext(), TemplateLoc, NameInfo,
+ TemplateName(Template), *TemplateArgs);
}
void Sema::diagnoseMissingTemplateArguments(TemplateName Name,
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index f9222f2009116..c7c2e74fd5e97 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -16535,16 +16535,10 @@ TreeTransform<Derived>::TransformCXXUnresolvedConstructExpr(
template <typename Derived>
ExprResult TreeTransform<Derived>::TransformDependentTemplateIdExpr(
DependentTemplateIdExpr *E) {
- NestedNameSpecifierLoc QualifierLoc = E->getQualifierLoc();
- if (QualifierLoc) {
- QualifierLoc = getDerived().TransformNestedNameSpecifierLoc(QualifierLoc);
- if (!QualifierLoc)
- return ExprError();
- }
+ NestedNameSpecifierLoc Loc;
TemplateName Name = getDerived().TransformTemplateName(
- QualifierLoc, E->getTemplateKeywordLoc(), E->getTemplateName(),
- E->getNameLoc());
+ Loc, E->getTemplateKeywordLoc(), E->getTemplateName(), E->getNameLoc());
if (Name.isNull())
return ExprError();
@@ -16558,7 +16552,6 @@ ExprResult TreeTransform<Derived>::TransformDependentTemplateIdExpr(
return ExprError();
CXXScopeSpec SS;
- SS.Adopt(QualifierLoc);
LookupResult R(SemaRef, E->getNameInfo(), Sema::LookupOrdinaryName);
R.addDecl(TD);
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 66c0a1581b5da..c2d8267b6fcea 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -2102,7 +2102,6 @@ void ASTStmtReader::VisitDependentTemplateIdExpr(DependentTemplateIdExpr *E) {
"Wrong NumTemplateArgs!");
ReadTemplateKWAndArgsInfo(E->KWAndArgs, E->getTrailingObjects(),
NumTemplateArgs);
- E->QualifierLoc = Record.readNestedNameSpecifierLoc();
E->NameInfo = Record.readDeclarationNameInfo();
E->Name = Record.readTemplateName();
}
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index 27d124f2fb481..bf90b247fd6ab 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -2114,7 +2114,6 @@ void ASTStmtWriter::VisitDependentTemplateIdExpr(DependentTemplateIdExpr *E) {
VisitExpr(E);
Record.push_back(E->getNumTemplateArgs());
AddTemplateKWAndArgsInfo(E->KWAndArgs, E->getTrailingObjects());
- Record.AddNestedNameSpecifierLoc(E->getQualifierLoc());
Record.AddDeclarationNameInfo(E->getNameInfo());
Record.AddTemplateName(E->getTemplateName());
Code = serialization::EXPR_DEPENDENT_TEMPLATE_ID;
>From 8d90fe01d11cbc137a7dd08e7b7456d90716826f Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Tue, 18 Aug 2026 09:44:46 +0200
Subject: [PATCH 5/7] address feedback
---
clang/include/clang/Sema/Sema.h | 5 ++---
clang/lib/Sema/SemaTemplate.cpp | 14 ++++++++------
clang/lib/Sema/TreeTransform.h | 4 ++--
3 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 3cb0dcbbba83a..38634eb9e61a9 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -11819,9 +11819,8 @@ class Sema final : public SemaBase {
const TemplateArgumentListInfo *TemplateArgs);
ExprResult CheckVarOrConceptTemplateTemplateId(
- const CXXScopeSpec &SS, const DeclarationNameInfo &NameInfo,
- TemplateTemplateParmDecl *Template, SourceLocation TemplateLoc,
- const TemplateArgumentListInfo *TemplateArgs);
+ const DeclarationNameInfo &NameInfo, TemplateTemplateParmDecl *Template,
+ SourceLocation TemplateLoc, const TemplateArgumentListInfo *TemplateArgs);
ExprResult
CheckConceptTemplateId(const CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index bc7e9c97cfa9d..e742f738c518e 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);
+ NameInfo, CDT, SourceLocation(), &ConstraintArgs);
}
if (ImmediatelyDeclaredConstraint.isInvalid() || !EllipsisLoc.isValid())
return ImmediatelyDeclaredConstraint;
@@ -4830,9 +4831,8 @@ ExprResult Sema::CheckVarTemplateId(
}
ExprResult Sema::CheckVarOrConceptTemplateTemplateId(
- const CXXScopeSpec &SS, const DeclarationNameInfo &NameInfo,
- TemplateTemplateParmDecl *Template, SourceLocation TemplateLoc,
- const TemplateArgumentListInfo *TemplateArgs) {
+ const DeclarationNameInfo &NameInfo, TemplateTemplateParmDecl *Template,
+ SourceLocation TemplateLoc, const TemplateArgumentListInfo *TemplateArgs) {
assert(Template && "A variable template id without template?");
if (Template->templateParameterKind() != TemplateNameKind::TNK_Var_template &&
@@ -4991,10 +4991,12 @@ 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?");
return CheckVarOrConceptTemplateTemplateId(
- SS, R.getLookupNameInfo(), R.getAsSingle<TemplateTemplateParmDecl>(),
+ R.getLookupNameInfo(), R.getAsSingle<TemplateTemplateParmDecl>(),
TemplateKWLoc, TemplateArgs);
+ }
// Function templates
ULE = UnresolvedLookupExpr::Create(
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index c7c2e74fd5e97..aa9c3933eb6a1 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -16543,8 +16543,8 @@ ExprResult TreeTransform<Derived>::TransformDependentTemplateIdExpr(
return ExprError();
TemplateDecl *TD = Name.getAsTemplateDecl();
- if (!TD)
- return ExprError();
+
+ assert(TD && "A dependent template id always refers to a template decl");
TemplateArgumentListInfo TransArgs(E->getLAngleLoc(), E->getRAngleLoc());
if (getDerived().TransformTemplateArguments(
>From a047d4d09123cce18d0f142c8a1c0adea7f35325 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Tue, 18 Aug 2026 11:45:10 +0200
Subject: [PATCH 6/7] assert Depth < getNumLevels()
---
clang/lib/Sema/SemaTemplateInstantiate.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 77bc4866a9c1f..f107811be868f 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -4651,8 +4651,8 @@ ExprResult Sema::SubstConceptTemplateArguments(
unsigned Depth = TTP->getDepth();
unsigned Pos = TTP->getPosition();
ConceptDecl *ResolvedConcept = nullptr;
- if (Depth < MLTAL.getNumLevels() &&
- MLTAL.hasTemplateArgument(Depth, Pos)) {
+
+ if (MLTAL.hasTemplateArgument(Depth, Pos)) {
TemplateArgument Arg = MLTAL(Depth, Pos);
assert(Arg.getKind() == TemplateArgument::Template);
ResolvedConcept =
>From 57d486c7e3262f2a7936e3ca8905dd0af75b80fb Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Tue, 18 Aug 2026 20:51:21 +0200
Subject: [PATCH 7/7] we never have a template keyword; and always arguments
---
clang/include/clang/AST/ExprCXX.h | 13 ++-----------
clang/include/clang/Sema/Sema.h | 2 +-
clang/lib/AST/ASTImporter.cpp | 3 +--
clang/lib/AST/ExprCXX.cpp | 8 ++++----
clang/lib/AST/StmtPrinter.cpp | 2 --
clang/lib/Sema/SemaTemplate.cpp | 12 +++++++-----
clang/lib/Sema/TreeTransform.h | 4 ++--
7 files changed, 17 insertions(+), 27 deletions(-)
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 112c2e223eb84..63fa762e0159a 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -3475,7 +3475,6 @@ class DependentTemplateIdExpr final
ASTTemplateKWAndArgsInfo KWAndArgs;
DependentTemplateIdExpr(const ASTContext &Context,
- SourceLocation TemplateKWLoc,
const DeclarationNameInfo &NameInfo,
TemplateName Name,
const TemplateArgumentListInfo &TemplateArgs);
@@ -3484,7 +3483,7 @@ class DependentTemplateIdExpr final
public:
static DependentTemplateIdExpr *
- Create(const ASTContext &Context, SourceLocation TemplateKWLoc,
+ Create(const ASTContext &Context,
const DeclarationNameInfo &NameInfo, TemplateName Name,
const TemplateArgumentListInfo &TemplateArgs);
@@ -3505,9 +3504,6 @@ class DependentTemplateIdExpr final
return getParameter()->templateParameterKind() == TNK_Concept_template;
}
- SourceLocation getTemplateKeywordLoc() const {
- return KWAndArgs.TemplateKWLoc;
- }
SourceLocation getLAngleLoc() const { return KWAndArgs.LAngleLoc; }
SourceLocation getRAngleLoc() const { return KWAndArgs.RAngleLoc; }
@@ -3518,16 +3514,11 @@ class DependentTemplateIdExpr final
}
SourceLocation getBeginLoc() const {
- if (SourceLocation TemplateKWLoc = getTemplateKeywordLoc();
- TemplateKWLoc.isValid())
- return TemplateKWLoc;
return getNameLoc();
}
SourceLocation getEndLoc() const {
- if (SourceLocation RAngleLoc = getRAngleLoc(); RAngleLoc.isValid())
- return RAngleLoc;
- return NameInfo.getEndLoc();
+ return getRAngleLoc();
}
child_range children() {
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 38634eb9e61a9..a0f94df1407b2 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -11820,7 +11820,7 @@ class Sema final : public SemaBase {
ExprResult CheckVarOrConceptTemplateTemplateId(
const DeclarationNameInfo &NameInfo, TemplateTemplateParmDecl *Template,
- SourceLocation TemplateLoc, const TemplateArgumentListInfo *TemplateArgs);
+ const TemplateArgumentListInfo *TemplateArgs);
ExprResult
CheckConceptTemplateId(const CXXScopeSpec &SS, SourceLocation TemplateKWLoc,
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index eb1d5739ea3b4..7ab0a838a2be8 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -8829,7 +8829,6 @@ ExpectedStmt ASTNodeImporter::VisitCXXDependentScopeMemberExpr(
ExpectedStmt
ASTNodeImporter::VisitDependentTemplateIdExpr(DependentTemplateIdExpr *E) {
Error Err = Error::success();
- auto ToTemplateKeywordLoc = importChecked(Err, E->getTemplateKeywordLoc());
auto ToName = importChecked(Err, E->getTemplateName());
auto ToDeclName = importChecked(Err, E->getName());
auto ToNameLoc = importChecked(Err, E->getNameLoc());
@@ -8847,7 +8846,7 @@ ASTNodeImporter::VisitDependentTemplateIdExpr(DependentTemplateIdExpr *E) {
return std::move(Err);
return DependentTemplateIdExpr::Create(Importer.getToContext(),
- ToTemplateKeywordLoc, ToNameInfo,
+ ToNameInfo,
ToName, ToTAInfo);
}
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index 9bc6d65f1300b..d48f179347586 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -402,13 +402,13 @@ SourceLocation CXXPseudoDestructorExpr::getEndLoc() const {
}
DependentTemplateIdExpr::DependentTemplateIdExpr(
- const ASTContext &Context, SourceLocation TemplateKWLoc,
+ 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());
+ KWAndArgs.initializeFrom(/*TemplateKWLoc=*/{}, TemplateArgs, getTrailingObjects());
setDependence(computeDependence(this));
}
@@ -419,13 +419,13 @@ DependentTemplateIdExpr::DependentTemplateIdExpr(EmptyShell Empty,
}
DependentTemplateIdExpr *DependentTemplateIdExpr::Create(
- const ASTContext &Context, SourceLocation TemplateKWLoc,
+ 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, TemplateKWLoc, NameInfo,
+ return new (Mem) DependentTemplateIdExpr(Context, NameInfo,
Name, TemplateArgs);
}
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index d7f69e87b7e5c..e08566e5ae011 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -2672,8 +2672,6 @@ void StmtPrinter::VisitCXXReflectExpr(CXXReflectExpr *S) {
}
void StmtPrinter::VisitDependentTemplateIdExpr(DependentTemplateIdExpr *Node) {
- if (Node->getTemplateKeywordLoc().isValid())
- OS << "template ";
OS << Node->getNameInfo();
printTemplateArgumentList(OS, Node->template_arguments(), Policy,
Node->getParameter()->getTemplateParameters());
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index e742f738c518e..19eaf2485f5ff 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -1241,7 +1241,7 @@ static ExprResult formImmediatelyDeclaredConstraint(
assert(SS.isEmpty() && "template parameter with a scope specifier?");
auto *CDT = dyn_cast<TemplateTemplateParmDecl>(NamedConcept);
ImmediatelyDeclaredConstraint = S.CheckVarOrConceptTemplateTemplateId(
- NameInfo, CDT, SourceLocation(), &ConstraintArgs);
+ NameInfo, CDT, &ConstraintArgs);
}
if (ImmediatelyDeclaredConstraint.isInvalid() || !EllipsisLoc.isValid())
return ImmediatelyDeclaredConstraint;
@@ -4832,7 +4832,7 @@ ExprResult Sema::CheckVarTemplateId(
ExprResult Sema::CheckVarOrConceptTemplateTemplateId(
const DeclarationNameInfo &NameInfo, TemplateTemplateParmDecl *Template,
- SourceLocation TemplateLoc, const TemplateArgumentListInfo *TemplateArgs) {
+ const TemplateArgumentListInfo *TemplateArgs) {
assert(Template && "A variable template id without template?");
if (Template->templateParameterKind() != TemplateNameKind::TNK_Var_template &&
@@ -4843,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.
@@ -4852,7 +4852,7 @@ ExprResult Sema::CheckVarOrConceptTemplateTemplateId(
/*UpdateArgsWithConversions=*/false))
return true;
- return DependentTemplateIdExpr::Create(getASTContext(), TemplateLoc, NameInfo,
+ return DependentTemplateIdExpr::Create(getASTContext(), NameInfo,
TemplateName(Template), *TemplateArgs);
}
@@ -4983,6 +4983,7 @@ 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);
@@ -4993,9 +4994,10 @@ ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS,
UnresolvedLookupExpr *ULE;
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(
R.getLookupNameInfo(), R.getAsSingle<TemplateTemplateParmDecl>(),
- TemplateKWLoc, TemplateArgs);
+ TemplateArgs);
}
// Function templates
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index aa9c3933eb6a1..0fe2e7195c3e0 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -16538,7 +16538,7 @@ ExprResult TreeTransform<Derived>::TransformDependentTemplateIdExpr(
NestedNameSpecifierLoc Loc;
TemplateName Name = getDerived().TransformTemplateName(
- Loc, E->getTemplateKeywordLoc(), E->getTemplateName(), E->getNameLoc());
+ Loc, /*Template Keyword=*/SourceLocation(), E->getTemplateName(), E->getNameLoc());
if (Name.isNull())
return ExprError();
@@ -16556,7 +16556,7 @@ ExprResult TreeTransform<Derived>::TransformDependentTemplateIdExpr(
LookupResult R(SemaRef, E->getNameInfo(), Sema::LookupOrdinaryName);
R.addDecl(TD);
R.resolveKind();
- return getDerived().RebuildTemplateIdExpr(SS, E->getTemplateKeywordLoc(), R,
+ return getDerived().RebuildTemplateIdExpr(SS, /*Template Keyword=*/SourceLocation(), R,
/*RequiresADL=*/false, &TransArgs);
}
More information about the cfe-commits
mailing list