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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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); } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
