[clang] [clang-tools-extra] [Clang][C++29] Template pack indexing (PR #218738)
Corentin Jabot via cfe-commits
cfe-commits at lists.llvm.org
Wed Aug 26 06:13:36 PDT 2026
https://github.com/cor3ntin updated https://github.com/llvm/llvm-project/pull/218738
>From ff87a746cbb4e3f3e86f3dfe697c4f4127ff78d4 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Fri, 14 Aug 2026 15:47:15 +0200
Subject: [PATCH 1/4] [Clang][C++29] Template pack indexing.
This partially implement p3670r4 (https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2026/p3670r4.pdf)
I haven't implemented mangling yet, it part to limit the scope
of this change which is somewhat larger than I thought it would be.
This introduces a new uncommon template name storage kind
that stores a pattern and the expanded parameter, like we do
for types and expressions.
The rest is fairly mechanical.
The feature is backported to C++98 (for type template parameters).
Funnilly, the backport of pack indexing of types was never actually
tested in C++98 mode and did not work.
It should be fixed by this PR but I'll write tests for it
as a follow up.
I did my best to accommodate clang-tools-extra.
I wrote some tests to mirror some issues reported for pack indexing of
types, hopefully there is gonna be a shorter trail of bugs...
but because this touches type constraints, who knows.
---
clang-tools-extra/clangd/DumpAST.cpp | 6 +-
clang-tools-extra/clangd/FindTarget.cpp | 12 +-
clang-tools-extra/clangd/Selection.cpp | 5 +-
.../clangd/SemanticHighlighting.cpp | 1 +
.../clangd/unittests/DumpASTTests.cpp | 23 +
.../clangd/unittests/FindTargetTests.cpp | 21 +
clang-tools-extra/modularize/Modularize.cpp | 10 +-
clang/docs/LanguageExtensions.md | 1 +
clang/docs/ReleaseNotes.md | 7 +
clang/include/clang/AST/ASTConcept.h | 2 +-
clang/include/clang/AST/ASTContext.h | 7 +
clang/include/clang/AST/DependenceFlags.h | 7 +
clang/include/clang/AST/ExprCXX.h | 8 +-
clang/include/clang/AST/PropertiesBase.td | 20 +
clang/include/clang/AST/RecursiveASTVisitor.h | 12 +-
clang/include/clang/AST/TemplateName.h | 77 +++-
.../include/clang/Basic/DiagnosticASTKinds.td | 1 +
.../clang/Basic/DiagnosticSemaKinds.td | 3 +
clang/include/clang/ExtractAPI/API.h | 30 +-
clang/include/clang/Parse/Parser.h | 7 +
clang/include/clang/Sema/Sema.h | 26 +-
clang/lib/AST/ASTConcept.cpp | 5 +-
clang/lib/AST/ASTContext.cpp | 41 +-
clang/lib/AST/ASTDumper.cpp | 9 +-
clang/lib/AST/ASTImporter.cpp | 21 +
clang/lib/AST/ASTStructuralEquivalence.cpp | 9 +
clang/lib/AST/ASTTypeTraits.cpp | 2 +-
clang/lib/AST/ComputeDependence.cpp | 4 +-
clang/lib/AST/DeclPrinter.cpp | 18 +-
clang/lib/AST/ItaniumMangle.cpp | 30 ++
clang/lib/AST/ODRHash.cpp | 6 +
clang/lib/AST/StmtPrinter.cpp | 2 +-
clang/lib/AST/TemplateBase.cpp | 11 +-
clang/lib/AST/TemplateName.cpp | 160 ++++++-
clang/lib/AST/TextNodeDumper.cpp | 18 +-
clang/lib/AST/Type.cpp | 4 +-
clang/lib/AST/TypeLoc.cpp | 2 +-
clang/lib/AST/TypePrinter.cpp | 14 +-
clang/lib/ExtractAPI/DeclarationFragments.cpp | 8 +-
clang/lib/Frontend/InitPreprocessor.cpp | 2 +-
clang/lib/Parse/ParseDeclCXX.cpp | 82 ++++
clang/lib/Parse/ParseExpr.cpp | 5 +-
clang/lib/Parse/ParseExprCXX.cpp | 77 ++--
clang/lib/Parse/ParseTemplate.cpp | 31 +-
clang/lib/Sema/SemaCodeComplete.cpp | 3 +-
clang/lib/Sema/SemaExpr.cpp | 6 +
clang/lib/Sema/SemaOverload.cpp | 3 +-
clang/lib/Sema/SemaTemplate.cpp | 62 +--
clang/lib/Sema/SemaTemplateDeduction.cpp | 4 +-
clang/lib/Sema/SemaTemplateInstantiate.cpp | 22 +-
clang/lib/Sema/SemaTemplateVariadic.cpp | 68 +++
clang/lib/Sema/SemaType.cpp | 32 +-
clang/lib/Sema/TreeTransform.h | 159 ++++++-
.../Serialization/TemplateArgumentHasher.cpp | 3 +
.../AST/ast-dump-pack-indexing-template.cpp | 56 +++
.../AST/ast-print-pack-indexing-template.cpp | 42 ++
.../test/ExtractAPI/pack_indexing_concept.cpp | 33 ++
clang/test/Lexer/cxx-features.cpp | 2 +-
.../test/Modules/pack-indexing-template.cppm | 73 ++++
clang/test/PCH/pack-indexing-template.cpp | 67 +++
.../Parser/cxx2d-pack-indexing-template.cpp | 69 +++
...cxx2d-pack-indexing-template-ext-diags.cpp | 54 +++
.../SemaCXX/cxx2d-pack-indexing-template.cpp | 400 ++++++++++++++++++
clang/tools/libclang/CIndex.cpp | 4 +
clang/www/cxx_status.html | 6 +-
65 files changed, 1817 insertions(+), 198 deletions(-)
create mode 100644 clang/test/AST/ast-dump-pack-indexing-template.cpp
create mode 100644 clang/test/AST/ast-print-pack-indexing-template.cpp
create mode 100644 clang/test/ExtractAPI/pack_indexing_concept.cpp
create mode 100644 clang/test/Modules/pack-indexing-template.cppm
create mode 100644 clang/test/PCH/pack-indexing-template.cpp
create mode 100644 clang/test/Parser/cxx2d-pack-indexing-template.cpp
create mode 100644 clang/test/SemaCXX/cxx2d-pack-indexing-template-ext-diags.cpp
create mode 100644 clang/test/SemaCXX/cxx2d-pack-indexing-template.cpp
diff --git a/clang-tools-extra/clangd/DumpAST.cpp b/clang-tools-extra/clangd/DumpAST.cpp
index c3c35c51cf1ea..faa18fb7baa46 100644
--- a/clang-tools-extra/clangd/DumpAST.cpp
+++ b/clang-tools-extra/clangd/DumpAST.cpp
@@ -185,6 +185,7 @@ class DumpVisitor : public RecursiveASTVisitor<DumpVisitor> {
TEMPLATE_KIND(SubstTemplateTemplateParmPack);
TEMPLATE_KIND(UsingTemplate);
TEMPLATE_KIND(DeducedTemplate);
+ TEMPLATE_KIND(PackIndexingTemplate);
#undef TEMPLATE_KIND
}
llvm_unreachable("Unhandled NameKind enum");
@@ -303,7 +304,10 @@ class DumpVisitor : public RecursiveASTVisitor<DumpVisitor> {
return CBS.isVirtual() ? "virtual" : "";
}
std::string getDetail(const ConceptReference *CR) {
- return CR->getNamedConcept().getAsTemplateDecl()->getNameAsString();
+ TemplateName TN = CR->getNamedConcept();
+ if (const auto *TD = TN.getAsTemplateDecl())
+ return TD->getNameAsString();
+ return getDetail(TN);
}
/// Arcana is produced by TextNodeDumper, for the types it supports.
diff --git a/clang-tools-extra/clangd/FindTarget.cpp b/clang-tools-extra/clangd/FindTarget.cpp
index aea04e14b2cf8..142776d0dfaa7 100644
--- a/clang-tools-extra/clangd/FindTarget.cpp
+++ b/clang-tools-extra/clangd/FindTarget.cpp
@@ -128,6 +128,14 @@ bool shouldSkipTypedef(const TypedefNameDecl *TD) {
// template<class X> using pvec = vector<x*>; pvec<int> x;
// There's no Decl `pvec<int>`, we must choose `pvec<X>` or `vector<int*>`
// and both are lossy. We must know upfront what the caller ultimately wants.
+
+static const TemplateDecl *getReferencedConcept(const ConceptReference *CR) {
+ TemplateName TN = CR->getNamedConcept();
+ if (const TemplateDecl *TD = TN.getAsTemplateDecl())
+ return TD;
+ return TN.getAsTemplateTemplateParmDecl();
+}
+
struct TargetFinder {
using RelSet = DeclRelationSet;
using Rel = DeclRelation;
@@ -519,7 +527,7 @@ struct TargetFinder {
}
void add(const ConceptReference *CR, RelSet Flags) {
- add(CR->getNamedConcept().getAsTemplateDecl(), Flags);
+ add(getReferencedConcept(CR), Flags);
}
};
@@ -1081,7 +1089,7 @@ class ExplicitReferenceCollector
return {ReferenceLoc{CR->getNestedNameSpecifierLoc(),
CR->getConceptNameLoc(),
/*IsDecl=*/false,
- {CR->getNamedConcept().getAsTemplateDecl()}}};
+ {getReferencedConcept(CR)}}};
if (const OffsetOfNode *OON = N.get<OffsetOfNode>()) {
if (OON->getKind() == OffsetOfNode::Field)
return {ReferenceLoc{NestedNameSpecifierLoc(),
diff --git a/clang-tools-extra/clangd/Selection.cpp b/clang-tools-extra/clangd/Selection.cpp
index 41d028cfcf4b5..ccd6ade47e6ef 100644
--- a/clang-tools-extra/clangd/Selection.cpp
+++ b/clang-tools-extra/clangd/Selection.cpp
@@ -733,7 +733,10 @@ class SelectionVisitor : public RecursiveASTVisitor<SelectionVisitor> {
return traverseNode(E, [&] { return TraverseStmt(E->getSyntacticForm()); });
}
bool TraverseTypeConstraint(const TypeConstraint *C) {
- if (auto *E = C->getImmediatelyDeclaredConstraint()) {
+ // A concept named through a template template parameter is not part of the
+ // immediately-declared constraint.
+ if (auto *E = C->getImmediatelyDeclaredConstraint();
+ E && !C->getNamedConcept().getAsTemplateTemplateParmDecl()) {
// Technically this expression is 'implicit' and not traversed by the RAV.
// However, the range is correct, so we visit expression to avoid adding
// an extra kind to 'DynTypeNode' that hold 'TypeConstraint'.
diff --git a/clang-tools-extra/clangd/SemanticHighlighting.cpp b/clang-tools-extra/clangd/SemanticHighlighting.cpp
index d2d110b72011b..8948f1476e108 100644
--- a/clang-tools-extra/clangd/SemanticHighlighting.cpp
+++ b/clang-tools-extra/clangd/SemanticHighlighting.cpp
@@ -1024,6 +1024,7 @@ class CollectExtraHighlightings
case TemplateName::SubstTemplateTemplateParmPack:
case TemplateName::UsingTemplate:
case TemplateName::DeducedTemplate:
+ case TemplateName::PackIndexingTemplate:
// Names that could be resolved to a TemplateDecl are handled elsewhere.
break;
}
diff --git a/clang-tools-extra/clangd/unittests/DumpASTTests.cpp b/clang-tools-extra/clangd/unittests/DumpASTTests.cpp
index 1ea25d918dad6..bfdb156ceb6cb 100644
--- a/clang-tools-extra/clangd/unittests/DumpASTTests.cpp
+++ b/clang-tools-extra/clangd/unittests/DumpASTTests.cpp
@@ -227,6 +227,29 @@ TEST(DumpASTTests, UnbalancedBraces) {
ASSERT_EQ(Node.range, Case.range("func"));
}
+bool hasDetail(const ASTNode &Node, llvm::StringRef Detail) {
+ if (Node.detail == Detail)
+ return true;
+ for (const ASTNode &Child : Node.children)
+ if (hasDetail(Child, Detail))
+ return true;
+ return false;
+}
+
+TEST(DumpASTTests, PackIndexedConcept) {
+ auto TU = TestTU::withCode(R"cpp(
+template <template <class> concept... CC, CC...[0] T>
+void func(T);
+ )cpp");
+ TU.ExtraArgs = {"-std=c++2d"};
+ ParsedAST AST = TU.build();
+ const ASTNode Node = dumpAST(
+ DynTypedNode::create(*AST.getASTContext().getTranslationUnitDecl()),
+ AST.getTokens(), AST.getASTContext());
+
+ EXPECT_TRUE(hasDetail(Node, "CC...[0]"));
+}
+
TEST(DumpASTTests, NestedTemplates) {
// Test that we don't crash while trying to dump AST of a template function
// with nested template names such as Foo<V>::template Bar<W>::Value.
diff --git a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp
index 3377b017410b5..87febfef753a1 100644
--- a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp
+++ b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp
@@ -659,6 +659,27 @@ TEST_F(TargetDeclTest, Concept) {
{"template <typename T, typename U> concept Fooable = true"});
}
+TEST_F(TargetDeclTest, PackIndexedConcept) {
+ Flags.push_back("-std=c++2d");
+
+ // constrained-parameter
+ Code = R"cpp(
+ template <template <class> concept... CC>
+ struct S {
+ template <[[CC]]...[0] T>
+ void bar(T t);
+ };
+ )cpp";
+ EXPECT_DECLS("ConceptReference", {"template <class> concept ...CC"});
+
+ // constrained placeholder type
+ Code = R"cpp(
+ template <template <class> concept... CC>
+ void bar([[CC]]...[0] auto t);
+ )cpp";
+ EXPECT_DECLS("ConceptReference", {"template <class> concept ...CC"});
+}
+
TEST_F(TargetDeclTest, Coroutine) {
Flags.push_back("-std=c++20");
diff --git a/clang-tools-extra/modularize/Modularize.cpp b/clang-tools-extra/modularize/Modularize.cpp
index 5af50c71d1c16..be25f3daac798 100644
--- a/clang-tools-extra/modularize/Modularize.cpp
+++ b/clang-tools-extra/modularize/Modularize.cpp
@@ -544,7 +544,10 @@ class CollectEntitiesVisitor
bool TraverseDeclarationNameInfo(DeclarationNameInfo NameInfo) {
return true;
}
- bool TraverseTemplateName(TemplateName Template) { return true; }
+ bool TraverseTemplateName(TemplateName Template,
+ bool TraverseQualifier = true) {
+ return true;
+ }
bool TraverseTemplateArgument(const TemplateArgument &Arg) { return true; }
bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc &ArgLoc) {
return true;
@@ -727,7 +730,10 @@ class CompileCheckVisitor
bool TraverseDeclarationNameInfo(DeclarationNameInfo NameInfo) {
return true;
}
- bool TraverseTemplateName(TemplateName Template) { return true; }
+ bool TraverseTemplateName(TemplateName Template,
+ bool TraverseQualifier = true) {
+ return true;
+ }
bool TraverseTemplateArgument(const TemplateArgument &Arg) { return true; }
bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc &ArgLoc) {
return true;
diff --git a/clang/docs/LanguageExtensions.md b/clang/docs/LanguageExtensions.md
index 5e7f147f23259..6289a2ddc6c56 100644
--- a/clang/docs/LanguageExtensions.md
+++ b/clang/docs/LanguageExtensions.md
@@ -1787,6 +1787,7 @@ More information can be found [here](https://clang.llvm.org/docs/Modules.html).
| Variadic Friends | \_\_cpp_variadic_friend | C++26 | C++03 |
| Trivial Relocatability | \_\_cpp_trivial_relocatability | C++26 | C++03 |
|``auto()`` cast | \_\_cpp_auto_cast | C++26 | C++03 |
+| Pack Indexing for Template Names | \_\_cpp_pack_indexing >= 202606L | C++2d | C++03 |
| Designated initializers (N494) | | C99 | C89 |
| `_Complex` (N693) | | C99 | C89, C++ |
| `_Bool` (N815) | | C99 | C89 |
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index ca0dbfa2af229..6a342c78f66d5 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -136,6 +136,13 @@ features cannot lower the translation-unit ABI level;
following new Unicode recommendations), applied as a DR to all C++ language
modes.
+- Clang now supports [P3670R4](https://wg21.link/p3670r4) (Pack indexing for
+ template names), which allows a pack of templates to be indexed, as in
+ `TT...[0]<int>`. Like pack indexing of types and expressions, this is
+ available in all C++ language modes as an extension, controlled by
+ `-Wc++2d-extensions` and `-Wpre-c++2d-compat`, and `__cpp_pack_indexing` is
+ bumped to `202606L`.
+
#### C++2c Feature Support
- Added `__builtin_type_order` for compatibility with GCC as part of the
diff --git a/clang/include/clang/AST/ASTConcept.h b/clang/include/clang/AST/ASTConcept.h
index 9a65c32b5ca1e..62fe2c0f653d5 100644
--- a/clang/include/clang/AST/ASTConcept.h
+++ b/clang/include/clang/AST/ASTConcept.h
@@ -212,7 +212,7 @@ class ConceptReference {
void print(llvm::raw_ostream &OS, const PrintingPolicy &Policy) const;
void dump() const;
- void dump(llvm::raw_ostream &) const;
+ void dump(llvm::raw_ostream &OS, const ASTContext &Context) const;
};
/// Models the abbreviated syntax to constrain a template type parameter:
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 802f1ebf755b7..c2382d3d16ab4 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -311,6 +311,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
SubstTemplateTemplateParmPacks;
mutable llvm::ContextualFoldingSet<DeducedTemplateStorage, ASTContext &>
DeducedTemplates;
+ mutable llvm::ContextualFoldingSet<PackIndexingTemplateStorage, ASTContext &>
+ PackIndexingTemplates;
mutable llvm::ContextualFoldingSet<ArrayParameterType, ASTContext &>
ArrayParameterTypes;
@@ -2678,6 +2680,11 @@ class ASTContext : public RefCountedBase<ASTContext> {
unsigned Index,
bool Final) const;
+ TemplateName
+ getPackIndexingTemplateName(TemplateName Pattern, Expr *IndexExpr,
+ bool FullySubstituted = false,
+ ArrayRef<TemplateName> Expansions = {}) const;
+
/// Represents a TemplateName which had some of its default arguments
/// deduced. This both represents this default argument deduction as sugar,
/// and provides the support for it's equivalences through canonicalization.
diff --git a/clang/include/clang/AST/DependenceFlags.h b/clang/include/clang/AST/DependenceFlags.h
index c4395259f0758..968fe8dd8c22b 100644
--- a/clang/include/clang/AST/DependenceFlags.h
+++ b/clang/include/clang/AST/DependenceFlags.h
@@ -259,6 +259,9 @@ inline ExprDependence toExprDependenceAsWritten(TypeDependence D) {
inline ExprDependence toExprDependence(NestedNameSpecifierDependence D) {
return Dependence(D).expr();
}
+inline ExprDependence toExprDependence(TemplateNameDependence D) {
+ return Dependence(D).expr();
+}
inline ExprDependence turnTypeToValueDependence(ExprDependence D) {
// Type-dependent expressions are always be value-dependent, so we simply drop
// type dependency.
@@ -315,6 +318,10 @@ toTemplateNameDependence(NestedNameSpecifierDependence D) {
return Dependence(D).templateName();
}
+inline TemplateNameDependence toTemplateNameDependence(ExprDependence D) {
+ return Dependence(D).templateName();
+}
+
inline TemplateNameDependence
toTemplateNameDependence(TemplateArgumentDependence D) {
return Dependence(D).templateName();
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index bb790b1100e7f..7a2022adf09e4 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -3507,12 +3507,12 @@ class DependentTemplateIdExpr final
TemplateName getTemplateName() const { return Name; }
TemplateTemplateParmDecl *getParameter() const {
- return cast<TemplateTemplateParmDecl>(Name.getAsTemplateDecl());
+ TemplateTemplateParmDecl *P = Name.getAsTemplateTemplateParmDecl();
+ assert(P && "A dependent template-id always names a template parameter");
+ return P;
}
- bool isConceptReference() const {
- return getParameter()->templateParameterKind() == TNK_Concept_template;
- }
+ bool isConceptReference() const { return Name.isConceptName(); }
SourceLocation getLAngleLoc() const { return KWAndArgs.LAngleLoc; }
SourceLocation getRAngleLoc() const { return KWAndArgs.RAngleLoc; }
diff --git a/clang/include/clang/AST/PropertiesBase.td b/clang/include/clang/AST/PropertiesBase.td
index d36cbbf7074b5..347f58d45ffa8 100644
--- a/clang/include/clang/AST/PropertiesBase.td
+++ b/clang/include/clang/AST/PropertiesBase.td
@@ -810,6 +810,26 @@ let Class = PropertyTypeCase<TemplateName, "DeducedTemplate"> in {
return ctx.getDeducedTemplateName(underlying, {startPos, defaultArgs});
}]>;
}
+let Class = PropertyTypeCase<TemplateName, "PackIndexingTemplate"> in {
+ def : ReadHelper<[{
+ auto PI = node.getAsPackIndexingTemplate();
+ }]>;
+ def : Property<"pattern", TemplateName> {
+ let Read = [{ PI->getPattern() }];
+ }
+ def : Property<"indexExpression", ExprRef> {
+ let Read = [{ PI->getIndexExpr() }];
+ }
+ def : Property<"isFullySubstituted", Bool> {
+ let Read = [{ PI->isFullySubstituted() }];
+ }
+ def : Property<"expansions", Array<TemplateName>> {
+ let Read = [{ PI->getExpansions() }];
+ }
+ def : Creator<[{
+ return ctx.getPackIndexingTemplateName(pattern, indexExpression, isFullySubstituted, expansions);
+ }]>;
+}
// Type cases for TemplateArgument.
def : PropertyTypeKind<TemplateArgument, TemplateArgumentKind,
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index a933662f77273..ed230d50e92d2 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -540,7 +540,10 @@ bool RecursiveASTVisitor<Derived>::TraverseTypeConstraint(
TRY_TO(TraverseConceptReference(C->getConceptReference()));
return true;
}
- if (Expr *IDC = C->getImmediatelyDeclaredConstraint()) {
+ // A concept named through a template template parameter has no
+ // ConceptSpecializationExpr to carry its ConceptReference.
+ if (Expr *IDC = C->getImmediatelyDeclaredConstraint();
+ IDC && !C->getNamedConcept().getAsTemplateTemplateParmDecl()) {
TRY_TO(TraverseStmt(IDC));
} else {
// Avoid traversing the ConceptReference in the TypeConstraint
@@ -882,6 +885,10 @@ bool RecursiveASTVisitor<Derived>::TraverseTemplateName(
if (TraverseQualifier && QTN->getQualifier()) {
TRY_TO(TraverseNestedNameSpecifier(QTN->getQualifier()));
}
+ } else if (PackIndexingTemplateStorage *PI =
+ Template.getAsPackIndexingTemplate()) {
+ TRY_TO(TraverseTemplateName(PI->getPattern(), TraverseQualifier));
+ TRY_TO(TraverseStmt(PI->getIndexExpr()));
}
return true;
@@ -2635,6 +2642,7 @@ DEF_TRAVERSE_STMT(CXXDependentScopeMemberExpr, {
DEF_TRAVERSE_STMT(DependentTemplateIdExpr, {
TRY_TO(TraverseDeclarationNameInfo(S->getNameInfo()));
+ TRY_TO(TraverseTemplateName(S->getTemplateName()));
TRY_TO(TraverseTemplateArgumentLocsHelper(S->template_arguments().data(),
S->getNumTemplateArgs()));
})
@@ -2733,6 +2741,8 @@ bool RecursiveASTVisitor<Derived>::TraverseConceptReference(
TRY_TO(VisitConceptReference(CR));
TRY_TO(TraverseNestedNameSpecifierLoc(CR->getNestedNameSpecifierLoc()));
TRY_TO(TraverseDeclarationNameInfo(CR->getConceptNameInfo()));
+ TRY_TO(TraverseTemplateName(CR->getNamedConcept(),
+ /*TraverseQualifier=*/false));
if (CR->hasExplicitTemplateArgs())
TRY_TO(TraverseTemplateArgumentLocsHelper(
CR->getTemplateArgsAsWritten()->getTemplateArgs(),
diff --git a/clang/include/clang/AST/TemplateName.h b/clang/include/clang/AST/TemplateName.h
index 47cfec8f61e6f..c1d790e6a9b26 100644
--- a/clang/include/clang/AST/TemplateName.h
+++ b/clang/include/clang/AST/TemplateName.h
@@ -22,6 +22,7 @@
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/PointerUnion.h"
#include "llvm/Support/PointerLikeTypeTraits.h"
+#include "llvm/Support/TrailingObjects.h"
#include <cassert>
#include <optional>
@@ -30,6 +31,7 @@ namespace clang {
class ASTContext;
class Decl;
class DependentTemplateName;
+class Expr;
class IdentifierInfo;
class NamedDecl;
class NestedNameSpecifier;
@@ -37,6 +39,7 @@ enum OverloadedOperatorKind : int;
class OverloadedTemplateStorage;
class AssumedTemplateStorage;
class DeducedTemplateStorage;
+class PackIndexingTemplateStorage;
struct PrintingPolicy;
class QualifiedTemplateName;
class SubstTemplateTemplateParmPackStorage;
@@ -55,7 +58,8 @@ class UncommonTemplateNameStorage {
Assumed, // defined in DeclarationName.h
Deduced,
SubstTemplateTemplateParm,
- SubstTemplateTemplateParmPack
+ SubstTemplateTemplateParmPack,
+ PackIndexing
};
struct BitsTag {
@@ -111,6 +115,12 @@ class UncommonTemplateNameStorage {
? reinterpret_cast<SubstTemplateTemplateParmPackStorage *>(this)
: nullptr;
}
+
+ PackIndexingTemplateStorage *getAsPackIndexingTemplate() {
+ return Bits.Kind == PackIndexing
+ ? reinterpret_cast<PackIndexingTemplateStorage *>(this)
+ : nullptr;
+ }
};
/// A structure for storing the information associated with an
@@ -269,6 +279,9 @@ class TemplateName {
/// A template name that refers to another TemplateName with deduced default
/// arguments.
DeducedTemplate,
+
+ /// A pack-index-template-name.
+ PackIndexingTemplate,
};
TemplateName() = default;
@@ -281,6 +294,7 @@ class TemplateName {
explicit TemplateName(DependentTemplateName *Dep);
explicit TemplateName(UsingShadowDecl *Using);
explicit TemplateName(DeducedTemplateStorage *Deduced);
+ explicit TemplateName(PackIndexingTemplateStorage *PackIndexing);
/// Determine whether this template name is NULL.
bool isNull() const;
@@ -297,6 +311,10 @@ class TemplateName {
/// set of function templates, returns NULL.
TemplateDecl *getAsTemplateDecl(bool IgnoreDeduced = false) const;
+ /// Retrieve the template template parameter that this template name refers
+ /// to, if any.
+ TemplateTemplateParmDecl *getAsTemplateTemplateParmDecl() const;
+
/// Retrieves the underlying template name that
/// this template name refers to, along with the
/// deduced default arguments, if any.
@@ -354,6 +372,9 @@ class TemplateName {
/// Retrieve the deduced template info, if any.
DeducedTemplateStorage *getAsDeducedTemplateName() const;
+ /// Retrieve the pack-index-template-name storage, if any.
+ PackIndexingTemplateStorage *getAsPackIndexingTemplate() const;
+
std::optional<TemplateName> desugar(bool IgnoreDeduced) const;
TemplateName getUnderlying() const;
@@ -371,6 +392,10 @@ class TemplateName {
/// unexpanded parameter pack (for C++0x variadic templates).
bool containsUnexpandedParameterPack() const;
+ /// Determines whether this template name denotes a concept, or a template
+ /// template parameter denoting one.
+ bool isConceptName() const;
+
enum class Qualified { None, AsWritten };
/// Print the template name.
///
@@ -482,6 +507,56 @@ class DeducedTemplateStorage : public UncommonTemplateNameStorage,
TemplateName Underlying, const DefaultArguments &DefArgs);
};
+/// A structure for storing a pack-index-template-name ([temp.names]).
+///
+/// C++29 [temp.names]p1:
+/// pack-index-template-name:
+/// simple-template-name ... [ constant-expression ]
+class PackIndexingTemplateStorage final
+ : public UncommonTemplateNameStorage,
+ public llvm::FoldingSetNode,
+ private llvm::TrailingObjects<PackIndexingTemplateStorage, TemplateName> {
+ friend class ASTContext;
+ friend TrailingObjects;
+
+ TemplateName Pattern;
+ Expr *IndexExpr;
+ bool FullySubstituted;
+
+ PackIndexingTemplateStorage(TemplateName Pattern, Expr *IndexExpr,
+ bool FullySubstituted,
+ ArrayRef<TemplateName> Expansions);
+
+public:
+ TemplateName getPattern() const { return Pattern; }
+
+ Expr *getIndexExpr() const { return IndexExpr; }
+
+ bool isFullySubstituted() const { return FullySubstituted; }
+
+ ArrayRef<TemplateName> getExpansions() const {
+ return getTrailingObjects(Bits.Data);
+ }
+
+ TemplateTemplateParmDecl *getParameterPack() const;
+
+ UnsignedOrNone getSelectedIndex() const;
+
+ TemplateName getSelectedTemplate() const;
+
+ bool expandsToEmptyPack() const {
+ return isFullySubstituted() && Bits.Data == 0;
+ }
+
+ TemplateNameDependence getDependence() const;
+
+ void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context) const;
+
+ static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
+ TemplateName Pattern, Expr *IndexExpr,
+ bool FullySubstituted, ArrayRef<TemplateName> Expansions);
+};
+
inline TemplateName TemplateName::getUnderlying() const {
if (SubstTemplateTemplateParmStorage *subst
= getAsSubstTemplateTemplateParm())
diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td
index 3c39ccf51ab67..219d66de2fa61 100644
--- a/clang/include/clang/Basic/DiagnosticASTKinds.td
+++ b/clang/include/clang/Basic/DiagnosticASTKinds.td
@@ -1067,6 +1067,7 @@ def err_unsupported_itanium_mangling : Error<
"|%UnnamedUnionNTTP{unnamed union non-type template parameter}"
"|%RequiresExprWithSubstitutionFailure{requires-expression "
"containing a substitution failure}"
+ "|%PackIndexTemplateName{pack indexing template name}"
"}0">;
def err_unsupported_itanium_expr_mangling : Error<
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 3a910c9c3f2b9..a98a07df48161 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -77,6 +77,9 @@ defm auto_expr : CXX23Compat<"'auto' as a functional-style cast is">;
// C++26 compatibility with C++23 and earlier.
defm decomp_decl_cond : CXX26Compat<"structured binding declaration in a condition is">;
+// C++29 compatibility with C++26 and earlier.
+defm pack_indexing_template : CXX29Compat<"pack indexing for template names is">;
+
// Compatibility warnings duplicated across multiple language versions.
foreach std = [14, 20, 23] in {
defm cxx#std#_constexpr_body_invalid_stmt : CompatWarning<
diff --git a/clang/include/clang/ExtractAPI/API.h b/clang/include/clang/ExtractAPI/API.h
index d0d05fcab4af6..84bf152841884 100644
--- a/clang/include/clang/ExtractAPI/API.h
+++ b/clang/include/clang/ExtractAPI/API.h
@@ -37,6 +37,15 @@
namespace clang {
namespace extractapi {
+inline std::string getTypeConstraintSpelling(const TypeConstraint *TC,
+ const ASTContext &Context) {
+ std::string Name;
+ llvm::raw_string_ostream OS(Name);
+ TC->getNamedConcept().print(OS, Context.getPrintingPolicy(),
+ TemplateName::Qualified::None);
+ return Name;
+}
+
class Template {
struct TemplateParameter {
// "class", "typename", or concept name
@@ -71,11 +80,8 @@ class Template {
continue;
std::string Type;
if (Param->hasTypeConstraint())
- Type = Param->getTypeConstraint()
- ->getNamedConcept()
- .getAsTemplateDecl()
- ->getName()
- .str();
+ Type = getTypeConstraintSpelling(Param->getTypeConstraint(),
+ Param->getASTContext());
else if (Param->wasDeclaredWithTypename())
Type = "typename";
else
@@ -93,11 +99,8 @@ class Template {
continue;
std::string Type;
if (Param->hasTypeConstraint())
- Type = Param->getTypeConstraint()
- ->getNamedConcept()
- .getAsTemplateDecl()
- ->getName()
- .str();
+ Type = getTypeConstraintSpelling(Param->getTypeConstraint(),
+ Param->getASTContext());
else if (Param->wasDeclaredWithTypename())
Type = "typename";
else
@@ -115,11 +118,8 @@ class Template {
continue;
std::string Type;
if (Param->hasTypeConstraint())
- Type = Param->getTypeConstraint()
- ->getNamedConcept()
- .getAsTemplateDecl()
- ->getName()
- .str();
+ Type = getTypeConstraintSpelling(Param->getTypeConstraint(),
+ Param->getASTContext());
else if (Param->wasDeclaredWithTypename())
Type = "typename";
else
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 163aa483a84e3..ae91153e34e3a 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -3004,6 +3004,13 @@ class Parser : public CodeCompletionHandler {
SourceLocation StartLoc,
SourceLocation EndLoc);
+ TemplateNameKind isPackIndexingTemplateName(UnqualifiedId &Name,
+ TemplateTy &Template);
+
+ bool AnnotatePackIndexingTemplateName(CXXScopeSpec &SS, UnqualifiedId &Name,
+ TemplateTy Template,
+ TemplateNameKind TNK);
+
/// Return true if the next token should be treated as a [[]] attribute,
/// or as a keyword that behaves like one. The former is only true if
/// [[]] attributes are enabled, whereas the latter is true whenever
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index bad4430f78b55..4a01b6d7e6d99 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -11618,7 +11618,7 @@ class Sema final : public SemaBase {
/// of arguments for the named concept).
bool AttachTypeConstraint(NestedNameSpecifierLoc NS,
DeclarationNameInfo NameInfo,
- TemplateDecl *NamedConcept, NamedDecl *FoundDecl,
+ TemplateName NamedConcept, NamedDecl *FoundDecl,
const TemplateArgumentListInfo *TemplateArgs,
TemplateTypeParmDecl *ConstrainedParameter,
SourceLocation EllipsisLoc);
@@ -11827,7 +11827,7 @@ class Sema final : public SemaBase {
const TemplateArgumentListInfo *TemplateArgs);
ExprResult CheckVarOrConceptTemplateTemplateId(
- const DeclarationNameInfo &NameInfo, TemplateTemplateParmDecl *Template,
+ const DeclarationNameInfo &NameInfo, TemplateName Template,
const TemplateArgumentListInfo *TemplateArgs);
ExprResult
@@ -14686,6 +14686,15 @@ class Sema final : public SemaBase {
void collectUnexpandedParameterPacks(
QualType T, SmallVectorImpl<UnexpandedParameterPack> &Unexpanded);
+ /// Collect the set of unexpanded parameter packs within the given
+ /// template name.
+ ///
+ /// \param Template The template name that will be traversed to find
+ /// unexpanded parameter packs.
+ void collectUnexpandedParameterPacks(
+ TemplateName Template,
+ SmallVectorImpl<UnexpandedParameterPack> &Unexpanded);
+
/// Collect the set of unexpanded parameter packs within the given
/// type.
///
@@ -14900,6 +14909,19 @@ class Sema final : public SemaBase {
ArrayRef<Expr *> ExpandedExprs = {},
bool FullySubstituted = false);
+ TemplateName ActOnPackIndexingTemplateName(TemplateName Pattern,
+ SourceLocation NameLoc,
+ Expr *IndexExpr);
+
+ TemplateName
+ BuildPackIndexingTemplateName(TemplateName Pattern, Expr *IndexExpr,
+ bool FullySubstituted = false,
+ ArrayRef<TemplateName> Expansions = {});
+
+ TypeResult
+ ActOnPackIndexingDeducedTemplateSpecializationType(TemplateName Name,
+ SourceLocation NameLoc);
+
/// Handle a C++1z fold-expression: ( expr op ... op expr ).
ExprResult ActOnCXXFoldExpr(Scope *S, SourceLocation LParenLoc, Expr *LHS,
tok::TokenKind Operator,
diff --git a/clang/lib/AST/ASTConcept.cpp b/clang/lib/AST/ASTConcept.cpp
index 6a90d214de7fd..2b04821a157da 100644
--- a/clang/lib/AST/ASTConcept.cpp
+++ b/clang/lib/AST/ASTConcept.cpp
@@ -96,7 +96,8 @@ ConceptReference::Create(const ASTContext &C, NestedNameSpecifierLoc NNS,
NamedDecl *FoundDecl, TemplateName NamedConcept,
const ASTTemplateArgumentListInfo *ArgsAsWritten) {
- assert(NamedConcept.getKind() == TemplateName::Template);
+ assert(NamedConcept.isConceptName() &&
+ "concept reference does not name a concept");
return new (C) ConceptReference(NNS, TemplateKWLoc, ConceptNameInfo,
FoundDecl, NamedConcept, ArgsAsWritten);
@@ -112,7 +113,7 @@ SourceLocation ConceptReference::getBeginLoc() const {
void ConceptReference::print(llvm::raw_ostream &OS,
const PrintingPolicy &Policy) const {
NestedNameSpec.getNestedNameSpecifier().print(OS, Policy);
- ConceptName.printName(OS, Policy);
+ NamedConcept.print(OS, Policy, TemplateName::Qualified::None);
if (hasExplicitTemplateArgs()) {
OS << "<";
llvm::ListSeparator Sep(", ");
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 2be9d9430e1ec..df04410a0cd2f 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -940,8 +940,8 @@ ASTContext::ASTContext(LangOptions &LOpts, SourceManager &SM,
AttributedTypes(this_()), DependentBitIntTypes(this_()),
HLSLAttributedResourceTypes(this_()),
SubstTemplateTemplateParmPacks(this_()), DeducedTemplates(this_()),
- ArrayParameterTypes(this_()), CanonTemplateTemplateParms(this_()),
- SourceMgr(SM), LangOpts(LOpts),
+ PackIndexingTemplates(this_()), ArrayParameterTypes(this_()),
+ CanonTemplateTemplateParms(this_()), SourceMgr(SM), LangOpts(LOpts),
NoSanitizeL(new NoSanitizeList(LangOpts.NoSanitizeFiles, SM)),
XRayFilter(new XRayFunctionFilter(LangOpts.XRayAlwaysInstrumentFiles,
LangOpts.XRayNeverInstrumentFiles,
@@ -7405,6 +7405,10 @@ ASTContext::getNameForTemplate(TemplateName Name,
DeducedTemplateStorage *DTS = Name.getAsDeducedTemplateName();
return getNameForTemplate(DTS->getUnderlying(), NameLoc);
}
+ case TemplateName::PackIndexingTemplate: {
+ PackIndexingTemplateStorage *PI = Name.getAsPackIndexingTemplate();
+ return getNameForTemplate(PI->getPattern(), NameLoc);
+ }
}
llvm_unreachable("bad template name kind!");
@@ -7472,6 +7476,16 @@ TemplateName ASTContext::getCanonicalTemplateName(TemplateName Name,
canonArgPack, subst->getAssociatedDecl()->getCanonicalDecl(),
subst->getIndex(), subst->getFinal());
}
+
+ case TemplateName::PackIndexingTemplate: {
+ PackIndexingTemplateStorage *PI = Name.getAsPackIndexingTemplate();
+ SmallVector<TemplateName, 4> CanonExpansions;
+ for (TemplateName T : PI->getExpansions())
+ CanonExpansions.push_back(getCanonicalTemplateName(T, IgnoreDeduced));
+ return getPackIndexingTemplateName(
+ getCanonicalTemplateName(PI->getPattern(), IgnoreDeduced),
+ PI->getIndexExpr(), PI->isFullySubstituted(), CanonExpansions);
+ }
case TemplateName::DeducedTemplate: {
assert(IgnoreDeduced == false);
DeducedTemplateStorage *DTS = Name.getAsDeducedTemplateName();
@@ -10654,6 +10668,29 @@ ASTContext::getDeducedTemplateName(TemplateName Underlying,
return TemplateName(DTS);
}
+TemplateName ASTContext::getPackIndexingTemplateName(
+ TemplateName Pattern, Expr *IndexExpr, bool FullySubstituted,
+ ArrayRef<TemplateName> Expansions) const {
+ auto &Self = const_cast<ASTContext &>(*this);
+ llvm::FoldingSetNodeID ID;
+ PackIndexingTemplateStorage::Profile(ID, Self, Pattern, IndexExpr,
+ FullySubstituted, Expansions);
+
+ void *InsertPos = nullptr;
+ PackIndexingTemplateStorage *PI =
+ PackIndexingTemplates.FindNodeOrInsertPos(ID, InsertPos);
+ if (!PI) {
+ void *Mem =
+ Allocate(PackIndexingTemplateStorage::totalSizeToAlloc<TemplateName>(
+ Expansions.size()),
+ alignof(PackIndexingTemplateStorage));
+ PI = new (Mem) PackIndexingTemplateStorage(Pattern, IndexExpr,
+ FullySubstituted, Expansions);
+ PackIndexingTemplates.InsertNode(PI, InsertPos);
+ }
+ return TemplateName(PI);
+}
+
/// getFromTargetType - Given one of the integer types provided by
/// TargetInfo, produce the corresponding type. The unsigned @p Type
/// is actually a value of type @c TargetInfo::IntType.
diff --git a/clang/lib/AST/ASTDumper.cpp b/clang/lib/AST/ASTDumper.cpp
index f74fec07b6175..b4570b1223382 100644
--- a/clang/lib/AST/ASTDumper.cpp
+++ b/clang/lib/AST/ASTDumper.cpp
@@ -358,12 +358,13 @@ LLVM_DUMP_METHOD void APValue::dump(raw_ostream &OS,
//===----------------------------------------------------------------------===//
LLVM_DUMP_METHOD void ConceptReference::dump() const {
- dump(llvm::errs());
+ ASTDumper P(llvm::errs(), /*ShowColors=*/false);
+ P.Visit(this);
}
-LLVM_DUMP_METHOD void ConceptReference::dump(raw_ostream &OS) const {
- auto &Ctx = getNamedConcept().getAsTemplateDecl()->getASTContext();
- ASTDumper P(OS, Ctx, showColorsForStream(Ctx, OS));
+LLVM_DUMP_METHOD void ConceptReference::dump(raw_ostream &OS,
+ const ASTContext &Context) const {
+ ASTDumper P(OS, Context, showColorsForStream(Context, OS));
P.Visit(this);
}
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index e34b3b4025c5a..0ad4130ce252a 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -10332,6 +10332,27 @@ Expected<TemplateName> ASTImporter::Import(TemplateName From) {
*ArgPackOrErr, *AssociatedDeclOrErr, SubstPack->getIndex(),
SubstPack->getFinal());
}
+ case TemplateName::PackIndexingTemplate: {
+ PackIndexingTemplateStorage *PI = From.getAsPackIndexingTemplate();
+ auto PatternOrErr = Import(PI->getPattern());
+ if (!PatternOrErr)
+ return PatternOrErr.takeError();
+
+ auto IndexExprOrErr = Import(PI->getIndexExpr());
+ if (!IndexExprOrErr)
+ return IndexExprOrErr.takeError();
+
+ SmallVector<TemplateName, 4> Expansions;
+ for (TemplateName T : PI->getExpansions()) {
+ auto ExpansionOrErr = Import(T);
+ if (!ExpansionOrErr)
+ return ExpansionOrErr.takeError();
+ Expansions.push_back(*ExpansionOrErr);
+ }
+
+ return ToContext.getPackIndexingTemplateName(
+ *PatternOrErr, *IndexExprOrErr, PI->isFullySubstituted(), Expansions);
+ }
case TemplateName::UsingTemplate: {
auto UsingOrError = Import(From.getAsUsingShadowDecl());
if (!UsingOrError)
diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp
index cce78485fad6e..029e33ae0db76 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -720,6 +720,15 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
P1->getIndex() == P2->getIndex();
}
+ case TemplateName::PackIndexingTemplate: {
+ PackIndexingTemplateStorage *P1 = N1.getAsPackIndexingTemplate(),
+ *P2 = N2.getAsPackIndexingTemplate();
+ return IsStructurallyEquivalent(Context, P1->getPattern(),
+ P2->getPattern()) &&
+ IsStructurallyEquivalent(Context, P1->getIndexExpr(),
+ P2->getIndexExpr());
+ }
+
case TemplateName::Template:
case TemplateName::QualifiedTemplate:
case TemplateName::SubstTemplateTemplateParm:
diff --git a/clang/lib/AST/ASTTypeTraits.cpp b/clang/lib/AST/ASTTypeTraits.cpp
index c717fd77d6ba6..27bd21fc75940 100644
--- a/clang/lib/AST/ASTTypeTraits.cpp
+++ b/clang/lib/AST/ASTTypeTraits.cpp
@@ -228,7 +228,7 @@ void DynTypedNode::dump(llvm::raw_ostream &OS,
else if (const Type *T = get<Type>())
T->dump(OS, Context);
else if (const ConceptReference *C = get<ConceptReference>())
- C->dump(OS);
+ C->dump(OS, Context);
else if (const TypeLoc *TL = get<TypeLoc>())
TL->dump(OS, Context);
else
diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp
index 0fc9da18b9394..7ea1c67ff7738 100644
--- a/clang/lib/AST/ComputeDependence.cpp
+++ b/clang/lib/AST/ComputeDependence.cpp
@@ -905,9 +905,7 @@ ExprDependence clang::computeDependence(CXXDependentScopeMemberExpr *E) {
ExprDependence clang::computeDependence(DependentTemplateIdExpr *E) {
auto D = ExprDependence::TypeValueInstantiation;
- if (E->getTemplateName().getDependence() &
- TemplateNameDependence::UnexpandedPack)
- D |= ExprDependence::UnexpandedPack;
+ D |= toExprDependence(E->getTemplateName().getDependence());
D |= getDependenceInExpr(E->getNameInfo());
for (const auto &A : E->template_arguments())
D |= toExprDependence(A.getArgument().getDependence());
diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp
index 1312ffd080342..141ba50941685 100644
--- a/clang/lib/AST/DeclPrinter.cpp
+++ b/clang/lib/AST/DeclPrinter.cpp
@@ -1278,10 +1278,20 @@ void DeclPrinter::VisitTemplateDecl(const TemplateDecl *D) {
if (const TemplateTemplateParmDecl *TTP =
dyn_cast<TemplateTemplateParmDecl>(D)) {
- if (TTP->wasDeclaredWithTypename())
- Out << "typename";
- else
- Out << "class";
+ switch (TTP->templateParameterKind()) {
+ case TemplateNameKind::TNK_Concept_template:
+ Out << "concept";
+ break;
+ case TemplateNameKind::TNK_Var_template:
+ Out << "auto";
+ break;
+ default:
+ if (TTP->wasDeclaredWithTypename())
+ Out << "typename";
+ else
+ Out << "class";
+ break;
+ }
if (TTP->isParameterPack())
Out << " ...";
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 7fb162a68fe88..2c38dbfac0cb2 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -537,6 +537,7 @@ class CXXNameMangler {
void manglePrefix(QualType type);
void mangleTemplatePrefix(GlobalDecl GD, bool NoFunction=false);
void mangleTemplatePrefix(TemplateName Template);
+ void DiagnoseUnsupportedPackIndexTemplateName();
const NamedDecl *getClosurePrefix(const Decl *ND);
void mangleClosurePrefix(const NamedDecl *ND, bool NoFunction = false);
bool mangleUnresolvedTypeOrSimpleId(QualType DestroyedType,
@@ -1266,6 +1267,12 @@ void CXXNameMangler::mangleFixedPointLiteral() {
<< UnsupportedItaniumManglingKind::FixedPointLiteral;
}
+void CXXNameMangler::DiagnoseUnsupportedPackIndexTemplateName() {
+ DiagnosticsEngine &Diags = Context.getDiags();
+ Diags.Report(diag::err_unsupported_itanium_mangling)
+ << UnsupportedItaniumManglingKind::PackIndexTemplateName;
+}
+
void CXXNameMangler::mangleNullPointer(QualType T) {
// <expr-primary> ::= L <type> 0 E
Out << 'L';
@@ -2097,6 +2104,10 @@ void CXXNameMangler::mangleTemplateParameterList(
void CXXNameMangler::mangleTypeConstraint(
TemplateName Concept, ArrayRef<TemplateArgument> Arguments) {
const TemplateDecl *TD = Concept.getAsTemplateDecl();
+ if (!TD) {
+ DiagnoseUnsupportedPackIndexTemplateName();
+ return;
+ }
const DeclContext *DC = Context.getEffectiveDeclContext(TD);
if (!Arguments.empty())
mangleTemplateName(TD, Arguments);
@@ -2274,6 +2285,11 @@ void CXXNameMangler::mangleTemplatePrefix(TemplateName Template) {
if (TemplateDecl *TD = Template.getAsTemplateDecl())
return mangleTemplatePrefix(TD);
+ if (Template.getAsPackIndexingTemplate()) {
+ DiagnoseUnsupportedPackIndexTemplateName();
+ return;
+ }
+
DependentTemplateName *Dependent = Template.getAsDependentTemplateName();
assert(Dependent && "unexpected template name kind");
@@ -2431,6 +2447,11 @@ void CXXNameMangler::mangleType(TemplateName TN) {
Out << "_SUBSTPACK_";
break;
}
+
+ case TemplateName::PackIndexingTemplate:
+ DiagnoseUnsupportedPackIndexTemplateName();
+ return;
+
case TemplateName::DeducedTemplate:
llvm_unreachable("Unexpected DeducedTemplate");
}
@@ -2597,6 +2618,11 @@ bool CXXNameMangler::mangleUnresolvedTypeOrSimpleId(QualType Ty,
Out << "_SUBSTPACK_";
break;
}
+
+ case TemplateName::PackIndexingTemplate:
+ DiagnoseUnsupportedPackIndexTemplateName();
+ return false;
+
case TemplateName::UsingTemplate: {
TemplateDecl *TD = TN.getAsTemplateDecl();
assert(TD && !isa<TemplateTemplateParmDecl>(TD));
@@ -5321,6 +5347,10 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity,
case Expr::DependentTemplateIdExprClass: {
NotPrimaryExpr();
const auto *DTI = cast<DependentTemplateIdExpr>(E);
+ if (DTI->getTemplateName().getAsPackIndexingTemplate()) {
+ DiagnoseUnsupportedPackIndexTemplateName();
+ break;
+ }
mangleUnresolvedName(NestedNameSpecifier(), DTI->getName(),
DTI->template_arguments().data(),
DTI->getNumTemplateArgs(), Arity);
diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp
index 386979d0f9ce2..9151fa2dae5f6 100644
--- a/clang/lib/AST/ODRHash.cpp
+++ b/clang/lib/AST/ODRHash.cpp
@@ -159,6 +159,12 @@ void ODRHash::AddTemplateName(TemplateName Name) {
AddDependentTemplateName(*Name.getAsDependentTemplateName());
break;
}
+ case TemplateName::PackIndexingTemplate: {
+ PackIndexingTemplateStorage *PI = Name.getAsPackIndexingTemplate();
+ AddTemplateName(PI->getPattern());
+ AddStmt(PI->getIndexExpr());
+ break;
+ }
// TODO: Support these cases.
case TemplateName::OverloadedTemplate:
case TemplateName::AssumedTemplate:
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index e957b03b7e898..31d670842ff87 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -2672,7 +2672,7 @@ void StmtPrinter::VisitCXXReflectExpr(CXXReflectExpr *S) {
}
void StmtPrinter::VisitDependentTemplateIdExpr(DependentTemplateIdExpr *Node) {
- OS << Node->getNameInfo();
+ Node->getTemplateName().print(OS, Policy, TemplateName::Qualified::None);
printTemplateArgumentList(OS, Node->template_arguments(), Policy,
Node->getParameter()->getTemplateParameters());
}
diff --git a/clang/lib/AST/TemplateBase.cpp b/clang/lib/AST/TemplateBase.cpp
index 961b6acd022ef..1616e7e457dce 100644
--- a/clang/lib/AST/TemplateBase.cpp
+++ b/clang/lib/AST/TemplateBase.cpp
@@ -366,15 +366,8 @@ bool TemplateArgument::isPackExpansion() const {
}
bool TemplateArgument::isConceptOrConceptTemplateParameter() const {
- if (getKind() != TemplateArgument::Template)
- return false;
-
- if (isa_and_nonnull<ConceptDecl>(getAsTemplate().getAsTemplateDecl()))
- return true;
- if (auto *TTP = llvm::dyn_cast_or_null<TemplateTemplateParmDecl>(
- getAsTemplate().getAsTemplateDecl()))
- return TTP->templateParameterKind() == TNK_Concept_template;
- return false;
+ return getKind() == TemplateArgument::Template &&
+ getAsTemplate().isConceptName();
}
bool TemplateArgument::containsUnexpandedParameterPack() const {
diff --git a/clang/lib/AST/TemplateName.cpp b/clang/lib/AST/TemplateName.cpp
index 797a354c5d0fa..910c5a761dd77 100644
--- a/clang/lib/AST/TemplateName.cpp
+++ b/clang/lib/AST/TemplateName.cpp
@@ -16,6 +16,7 @@
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/DependenceFlags.h"
+#include "clang/AST/Expr.h"
#include "clang/AST/NestedNameSpecifier.h"
#include "clang/AST/PrettyPrinter.h"
#include "clang/AST/TemplateBase.h"
@@ -25,6 +26,7 @@
#include "clang/Basic/OperatorKinds.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/raw_ostream.h"
#include <cassert>
@@ -122,6 +124,95 @@ void SubstTemplateTemplateParmPackStorage::Profile(
ID.AddBoolean(Final);
}
+PackIndexingTemplateStorage::PackIndexingTemplateStorage(
+ TemplateName Pattern, Expr *IndexExpr, bool FullySubstituted,
+ ArrayRef<TemplateName> Expansions)
+ : UncommonTemplateNameStorage(PackIndexing, /*Index=*/0,
+ /*Data=*/Expansions.size()),
+ Pattern(Pattern), IndexExpr(IndexExpr),
+ FullySubstituted(FullySubstituted) {
+ llvm::uninitialized_copy(Expansions, getTrailingObjects());
+}
+
+TemplateTemplateParmDecl *
+PackIndexingTemplateStorage::getParameterPack() const {
+ if (SubstTemplateTemplateParmPackStorage *S =
+ Pattern.getAsSubstTemplateTemplateParmPack())
+ return S->getParameterPack();
+ return dyn_cast_if_present<TemplateTemplateParmDecl>(
+ Pattern.getAsTemplateDecl());
+}
+
+UnsignedOrNone PackIndexingTemplateStorage::getSelectedIndex() const {
+ if (IndexExpr->isInstantiationDependent())
+ return std::nullopt;
+ auto *CE = dyn_cast<ConstantExpr>(IndexExpr);
+ if (!CE)
+ return std::nullopt;
+ llvm::APSInt Index = CE->getResultAsAPSInt();
+ assert(Index.isNonNegative() && "Invalid index");
+ return static_cast<unsigned>(Index.getExtValue());
+}
+
+TemplateName PackIndexingTemplateStorage::getSelectedTemplate() const {
+ if (!isFullySubstituted())
+ return TemplateName();
+ UnsignedOrNone Index = getSelectedIndex();
+ ArrayRef<TemplateName> Expansions = getExpansions();
+ if (!Index || *Index >= Expansions.size())
+ return TemplateName();
+ return Expansions[*Index];
+}
+
+TemplateNameDependence PackIndexingTemplateStorage::getDependence() const {
+ TemplateNameDependence IndexD =
+ toTemplateNameDependence(IndexExpr->getDependence());
+
+ TemplateNameDependence D =
+ IndexD | (IndexExpr->isInstantiationDependent()
+ ? TemplateNameDependence::DependentInstantiation
+ : TemplateNameDependence::None);
+ if (ArrayRef<TemplateName> Expansions = getExpansions(); Expansions.empty())
+ D |= Pattern.getDependence() &
+ TemplateNameDependence::DependentInstantiation;
+ else
+ for (TemplateName T : Expansions)
+ D |= T.getDependence();
+
+ // C++29 [temp.names]p5:
+ // A pack-index-template-name is a pack expansion.
+ if (!(IndexD & TemplateNameDependence::UnexpandedPack))
+ D &= ~TemplateNameDependence::UnexpandedPack;
+
+ // C++29 [temp.names]p3:
+ // The simple-template-name P in a pack-index-template-name shall
+ // denote a pack.
+ if (!Pattern.containsUnexpandedParameterPack())
+ D |= TemplateNameDependence::Error |
+ TemplateNameDependence::DependentInstantiation;
+
+ return D;
+}
+
+void PackIndexingTemplateStorage::Profile(llvm::FoldingSetNodeID &ID,
+ const ASTContext &Context) const {
+ Profile(ID, Context, Pattern, IndexExpr, isFullySubstituted(),
+ getExpansions());
+}
+
+void PackIndexingTemplateStorage::Profile(llvm::FoldingSetNodeID &ID,
+ const ASTContext &Context,
+ TemplateName Pattern, Expr *IndexExpr,
+ bool FullySubstituted,
+ ArrayRef<TemplateName> Expansions) {
+ Pattern.Profile(ID);
+ IndexExpr->Profile(ID, Context, /*Canonical=*/true);
+ ID.AddBoolean(FullySubstituted);
+ ID.AddInteger(Expansions.size());
+ for (TemplateName T : Expansions)
+ T.Profile(ID);
+}
+
IdentifierOrOverloadedOperator::IdentifierOrOverloadedOperator(
const IdentifierInfo *II)
: PtrOrOp(reinterpret_cast<uintptr_t>(II)) {
@@ -165,6 +256,8 @@ TemplateName::TemplateName(DependentTemplateName *Dep) : Storage(Dep) {}
TemplateName::TemplateName(UsingShadowDecl *Using) : Storage(Using) {}
TemplateName::TemplateName(DeducedTemplateStorage *Deduced)
: Storage(Deduced) {}
+TemplateName::TemplateName(PackIndexingTemplateStorage *PackIndexing)
+ : Storage(PackIndexing) {}
bool TemplateName::isNull() const { return Storage.isNull(); }
@@ -191,6 +284,8 @@ TemplateName::NameKind TemplateName::getKind() const {
return SubstTemplateTemplateParm;
if (uncommon->getAsDeducedTemplateName())
return DeducedTemplate;
+ if (uncommon->getAsPackIndexingTemplate())
+ return PackIndexingTemplate;
assert(uncommon->getAsSubstTemplateTemplateParmPack() != nullptr);
return SubstTemplateTemplateParmPack;
@@ -211,6 +306,14 @@ TemplateDecl *TemplateName::getAsTemplateDecl(bool IgnoreDeduced) const {
dyn_cast_if_present<Decl *>(Name.Storage));
}
+TemplateTemplateParmDecl *TemplateName::getAsTemplateTemplateParmDecl() const {
+ if (TemplateDecl *TD = getAsTemplateDecl())
+ return dyn_cast<TemplateTemplateParmDecl>(TD);
+ if (PackIndexingTemplateStorage *PI = getAsPackIndexingTemplate())
+ return PI->getParameterPack();
+ return nullptr;
+}
+
std::pair<TemplateName, DefaultArguments>
TemplateName::getTemplateDeclAndDefaultArgs() const {
DefaultArguments DefArgs;
@@ -243,6 +346,11 @@ std::optional<TemplateName> TemplateName::desugar(bool IgnoreDeduced) const {
return QTN->getUnderlyingTemplate();
if (SubstTemplateTemplateParmStorage *S = getAsSubstTemplateTemplateParm())
return S->getReplacement();
+ if (PackIndexingTemplateStorage *S = getAsPackIndexingTemplate()) {
+ if (TemplateName Selected = S->getSelectedTemplate(); !Selected.isNull())
+ return Selected;
+ return std::nullopt;
+ }
if (IgnoreDeduced)
if (DeducedTemplateStorage *S = getAsDeducedTemplateName())
return S->getUnderlying();
@@ -300,7 +408,8 @@ TemplateName::getQualifierAndTemplateKeyword() const {
if (QualifiedTemplateName *N = Cur->getAsQualifiedTemplateName())
return {N->getQualifier(), N->hasTemplateKeyword()};
if (Cur->getAsSubstTemplateTemplateParm() ||
- Cur->getAsSubstTemplateTemplateParmPack())
+ Cur->getAsSubstTemplateTemplateParmPack() ||
+ Cur->getAsPackIndexingTemplate())
break;
}
return {std::nullopt, false};
@@ -350,6 +459,14 @@ DeducedTemplateStorage *TemplateName::getAsDeducedTemplateName() const {
return nullptr;
}
+PackIndexingTemplateStorage *TemplateName::getAsPackIndexingTemplate() const {
+ if (UncommonTemplateNameStorage *Uncommon =
+ dyn_cast_if_present<UncommonTemplateNameStorage *>(Storage))
+ return Uncommon->getAsPackIndexingTemplate();
+
+ return nullptr;
+}
+
TemplateNameDependence TemplateName::getDependence() const {
switch (getKind()) {
case NameKind::Template:
@@ -395,6 +512,8 @@ TemplateNameDependence TemplateName::getDependence() const {
D |= toTemplateNameDependence(Arg.getDependence());
return D;
}
+ case NameKind::PackIndexingTemplate:
+ return getAsPackIndexingTemplate()->getDependence();
case NameKind::AssumedTemplate:
return TemplateNameDependence::DependentInstantiation;
case NameKind::OverloadedTemplate:
@@ -415,6 +534,35 @@ bool TemplateName::containsUnexpandedParameterPack() const {
return getDependence() & TemplateNameDependence::UnexpandedPack;
}
+bool TemplateName::isConceptName() const {
+ auto namesConcept = [](const TemplateTemplateParmDecl *TTP) {
+ return TTP->templateParameterKind() == TNK_Concept_template;
+ };
+ switch (getKind()) {
+ case NameKind::Template:
+ case NameKind::UsingTemplate: {
+ const TemplateDecl *TD = getAsTemplateDecl();
+ if (const auto *TTP = dyn_cast<TemplateTemplateParmDecl>(TD))
+ return namesConcept(TTP);
+ return isa<ConceptDecl>(TD);
+ }
+ case NameKind::PackIndexingTemplate:
+ return getAsPackIndexingTemplate()->getPattern().isConceptName();
+ case NameKind::SubstTemplateTemplateParmPack:
+ return namesConcept(
+ getAsSubstTemplateTemplateParmPack()->getParameterPack());
+ case NameKind::QualifiedTemplate:
+ case NameKind::SubstTemplateTemplateParm:
+ case NameKind::DeducedTemplate:
+ return desugar(/*IgnoreDeduced=*/true)->isConceptName();
+ case NameKind::OverloadedTemplate:
+ case NameKind::AssumedTemplate:
+ case NameKind::DependentTemplate:
+ return false;
+ }
+ llvm_unreachable("Unknown TemplateName kind");
+}
+
void TemplateName::print(raw_ostream &OS, const PrintingPolicy &Policy,
Qualified Qual) const {
auto handleAnonymousTTP = [&](TemplateDecl *TD, raw_ostream &OS) {
@@ -479,6 +627,16 @@ void TemplateName::print(raw_ostream &OS, const PrintingPolicy &Policy,
} else if (SubstTemplateTemplateParmStorage *subst =
getAsSubstTemplateTemplateParm()) {
subst->getReplacement().print(OS, Policy, Qual);
+ } else if (PackIndexingTemplateStorage *PI = getAsPackIndexingTemplate()) {
+ if (TemplateName Selected = PI->getSelectedTemplate();
+ !Selected.isNull() && Policy.PrintAsCanonical) {
+ Selected.print(OS, Policy, Qual);
+ return;
+ }
+ PI->getPattern().print(OS, Policy, Qual);
+ OS << "...[";
+ PI->getIndexExpr()->printPretty(OS, nullptr, Policy);
+ OS << "]";
} else if (SubstTemplateTemplateParmPackStorage *SubstPack =
getAsSubstTemplateTemplateParmPack())
OS << *SubstPack->getParameterPack();
diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index f58cc4f5761b7..89e01e9b563de 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -968,7 +968,7 @@ void TextNodeDumper::dumpBareConcept(TemplateName TN) {
ColorScope Color(OS, ShowColors, ASTDumpColor::DeclName);
OS << " '";
- OS << TD->getDeclName();
+ TN.print(OS, PrintPolicy, TemplateName::Qualified::None);
OS << '\'';
}
@@ -1391,6 +1391,19 @@ void TextNodeDumper::dumpBareTemplateName(TemplateName TN) {
});
return;
}
+ case TemplateName::PackIndexingTemplate: {
+ OS << " pack_indexing";
+ const PackIndexingTemplateStorage *PI = TN.getAsPackIndexingTemplate();
+ if (PI->isFullySubstituted())
+ OS << " fully_substituted";
+ if (UnsignedOrNone Index = PI->getSelectedIndex())
+ OS << " index " << *Index;
+ dumpTemplateName(PI->getPattern(), "pattern");
+ AddChild("index", [=] { Visit(PI->getIndexExpr()); });
+ for (TemplateName Expansion : PI->getExpansions())
+ dumpTemplateName(Expansion, "expansion");
+ return;
+ }
// FIXME: Implement these.
case TemplateName::OverloadedTemplate:
OS << " overloaded";
@@ -1660,8 +1673,7 @@ void clang::TextNodeDumper::VisitDependentScopeDeclRefExpr(
void clang::TextNodeDumper::VisitDependentTemplateIdExpr(
const DependentTemplateIdExpr *Node) {
OS << (Node->isConceptReference() ? " concept" : " variable template");
- OS << ' ';
- dumpBareTemplateName(Node->getTemplateName());
+ dumpTemplateName(Node->getTemplateName(), "name");
}
void TextNodeDumper::VisitUnresolvedLookupExpr(
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index b92dec99613f7..f396c2a234006 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -5750,8 +5750,8 @@ AutoType::AutoType(DeducedKind DK, QualType DeducedAsTypeOrCanon,
this->TypeConstraintConcept = TypeConstraintConcept;
assert(!TypeConstraintConcept.isNull() || AutoTypeBits.NumArgs == 0);
if (!TypeConstraintConcept.isNull()) {
-
- assert(TypeConstraintConcept.getKind() == TemplateName::Template);
+ assert(TypeConstraintConcept.isConceptName() &&
+ "type-constraint does not name a concept");
auto Dep = toTypeDependence(TypeConstraintConcept.getDependence());
diff --git a/clang/lib/AST/TypeLoc.cpp b/clang/lib/AST/TypeLoc.cpp
index 06406b805c402..682ce9ffacc30 100644
--- a/clang/lib/AST/TypeLoc.cpp
+++ b/clang/lib/AST/TypeLoc.cpp
@@ -768,7 +768,7 @@ static ConceptReference *createTrivialConceptReference(ASTContext &Context,
SourceLocation Loc,
const AutoType *AT) {
DeclarationName ConceptName =
- AT->getTypeConstraintConcept().getAsTemplateDecl()->getDeclName();
+ Context.getNameForTemplate(AT->getTypeConstraintConcept(), Loc).getName();
DeclarationNameInfo DNI = DeclarationNameInfo(ConceptName, Loc, ConceptName);
unsigned size = AT->getTypeConstraintArguments().size();
llvm::SmallVector<TemplateArgumentLocInfo, 8> TALI(size);
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index 74644ac0da0a6..80d63434c28b1 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -1412,14 +1412,16 @@ void TypePrinter::printAutoBefore(const AutoType *T, raw_ostream &OS) {
if (T->isConstrained()) {
// FIXME: Track a TypeConstraint as type sugar, so that we can print the
// type as it was written.
- T->getTypeConstraintConcept().getAsTemplateDecl()->getDeclName().print(
- OS, Policy);
+ TemplateName Concept = T->getTypeConstraintConcept();
+ Concept.print(OS, Policy, TemplateName::Qualified::None);
auto Args = T->getTypeConstraintArguments();
- if (!Args.empty())
+ if (!Args.empty()) {
+ const TemplateDecl *TD = Concept.getAsTemplateDecl();
+ if (!TD)
+ TD = Concept.getAsTemplateTemplateParmDecl();
printTemplateArgumentList(OS, Args, Policy,
- T->getTypeConstraintConcept()
- .getAsTemplateDecl()
- ->getTemplateParameters());
+ TD->getTemplateParameters());
+ }
OS << ' ';
}
switch (T->getKeyword()) {
diff --git a/clang/lib/ExtractAPI/DeclarationFragments.cpp b/clang/lib/ExtractAPI/DeclarationFragments.cpp
index 47b4b8c864a89..150a05afb396f 100644
--- a/clang/lib/ExtractAPI/DeclarationFragments.cpp
+++ b/clang/lib/ExtractAPI/DeclarationFragments.cpp
@@ -1011,11 +1011,9 @@ DeclarationFragmentsBuilder::getFragmentsForTemplateParameters(
if (const auto *TemplateParam =
dyn_cast<TemplateTypeParmDecl>(ParameterArray[i])) {
if (TemplateParam->hasTypeConstraint())
- Fragments.append(TemplateParam->getTypeConstraint()
- ->getNamedConcept()
- .getAsTemplateDecl()
- ->getName()
- .str(),
+ Fragments.append(extractapi::getTypeConstraintSpelling(
+ TemplateParam->getTypeConstraint(),
+ TemplateParam->getASTContext()),
DeclarationFragments::FragmentKind::TypeIdentifier);
else if (TemplateParam->wasDeclaredWithTypename())
Fragments.append("typename",
diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp
index 7cc481b595f36..f37d95cd02068 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -761,7 +761,7 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
Builder.defineMacro("__cpp_placeholder_variables", "202306L");
// C++26 features supported in earlier language modes.
- Builder.defineMacro("__cpp_pack_indexing", "202311L");
+ Builder.defineMacro("__cpp_pack_indexing", "202606L");
Builder.defineMacro("__cpp_deleted_function", "202403L");
Builder.defineMacro("__cpp_variadic_friend", "202403L");
Builder.defineMacro("__cpp_trivial_relocatability", "202502L");
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 0bdea354c2406..468fa8cbadd75 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -1213,6 +1213,88 @@ SourceLocation Parser::ParsePackIndexingType(DeclSpec &DS) {
return T.getCloseLocation();
}
+TemplateNameKind Parser::isPackIndexingTemplateName(UnqualifiedId &Name,
+ TemplateTy &Template) {
+ assert(Tok.is(tok::identifier) && NextToken().is(tok::ellipsis) &&
+ GetLookAheadToken(2).is(tok::l_square) && "expected 'identifier...['");
+
+ // C++29 [temp.names]p1:
+ // pack-index-template-name:
+ // simple-template-name ... [ constant-expression ]
+ Name.setIdentifier(Tok.getIdentifierInfo(), Tok.getLocation());
+ CXXScopeSpec EmptySS;
+ bool MemberOfUnknownSpecialization = false;
+ TemplateNameKind TNK = Actions.isTemplateName(
+ getCurScope(), EmptySS, /*hasTemplateKeyword=*/false, Name,
+ /*ObjectType=*/nullptr, /*EnteringContext=*/false, Template,
+ MemberOfUnknownSpecialization, /*AllowTypoCorrection=*/false);
+
+ if (TNK == TNK_Undeclared_template || !Template)
+ return TNK_Non_template;
+ return TNK;
+}
+
+bool Parser::AnnotatePackIndexingTemplateName(CXXScopeSpec &SS,
+ UnqualifiedId &Name,
+ TemplateTy Template,
+ TemplateNameKind TNK) {
+ assert(Tok.is(tok::identifier) && "expected a simple-template-name");
+ SourceLocation NameLoc = ConsumeToken();
+ ConsumeToken(); // the ellipsis
+
+ BalancedDelimiterTracker T(*this, tok::l_square);
+ if (T.consumeOpen())
+ return true;
+ ExprResult IndexExpr = ParseConstantExpression();
+ if (T.consumeClose() || IndexExpr.isInvalid())
+ return true;
+
+ TemplateName Indexed = Actions.ActOnPackIndexingTemplateName(
+ Template.get(), NameLoc, IndexExpr.get());
+ if (Indexed.isNull())
+ return true;
+ Template = TemplateTy::make(Indexed);
+
+ // C++29 [temp.names]p7:
+ // A < is interpreted as the delimiter of a template-argument-list if
+ // [...] it follows a pack-index-template-name.
+ if (Tok.is(tok::less))
+ return AnnotateTemplateIdToken(Template, TNK, SS,
+ /*TemplateKWLoc=*/SourceLocation(), Name,
+ /*AllowTypeAnnotation=*/false);
+
+ // Every token of the pack-index-template-name has been consumed,
+ // reinject the last token to produce an annotation.
+ if (PP.isBacktrackEnabled())
+ PP.RevertCachedTokens(1);
+ else
+ PP.EnterToken(Tok, /*IsReinject=*/true);
+
+ // C++29 [dcl.type.simple]p1:
+ // A type specifier is a placeholder for a deduced class type if [...] it
+ // is of the form typename pack-index-template-name.
+ if ((TNK == TNK_Type_template || TNK == TNK_Dependent_template_name) &&
+ getLangOpts().CPlusPlus17) {
+ TypeResult Type =
+ Actions.ActOnPackIndexingDeducedTemplateSpecializationType(Indexed,
+ NameLoc);
+ Tok.setKind(tok::annot_typename);
+ setTypeAnnotation(Tok, Type);
+ } else {
+ // A concept-name or a variable-template name.
+ Tok.setKind(tok::annot_template_id);
+ Tok.setAnnotationValue(TemplateIdAnnotation::Create(
+ /*TemplateKWLoc=*/SourceLocation(), NameLoc, Name.Identifier, OO_None,
+ Template, TNK, /*LAngleLoc=*/SourceLocation(),
+ /*RAngleLoc=*/SourceLocation(), /*TemplateArgs=*/{},
+ /*ArgsInvalid=*/false, TemplateIds));
+ }
+ Tok.setLocation(NameLoc);
+ Tok.setAnnotationEndLoc(T.getCloseLocation());
+ PP.AnnotateCachedTokens(Tok);
+ return false;
+}
+
void Parser::AnnotateExistingIndexedTypeNamePack(ParsedType T,
SourceLocation StartLoc,
SourceLocation EndLoc) {
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index 87cd7a01451cf..524f1889e112c 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -902,8 +902,9 @@ Parser::ParseCastExpression(CastParseKind ParseKind, bool isAddressOfOperand,
// Annotate the token and tail recurse.
// If the token is not annotated, then it might be an expression pack
// indexing
- if (!TryAnnotateTypeOrScopeToken() &&
- Tok.isOneOf(tok::annot_pack_indexing_type, tok::annot_cxxscope))
+ if (TryAnnotateTypeOrScopeToken())
+ return ExprError();
+ if (Tok.isAnnotation())
return ParseCastExpression(ParseKind, isAddressOfOperand,
CorrectionBehavior, isVectorLiteral,
NotPrimaryExpression);
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index f9a0dcc7d53af..4a903155377d9 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -188,43 +188,54 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
GetLookAheadToken(1).is(tok::ellipsis) &&
GetLookAheadToken(2).is(tok::l_square) &&
!GetLookAheadToken(3).is(tok::r_square)) {
- SourceLocation Start = Tok.getLocation();
- DeclSpec DS(AttrFactory);
- SourceLocation CCLoc;
- SourceLocation EndLoc = ParsePackIndexingType(DS);
- if (DS.getTypeSpecType() == DeclSpec::TST_error)
- return false;
+ // C++29 [temp.names]p1:
+ // pack-index-template-name:
+ // simple-template-name ... [ constant-expression ]
+ UnqualifiedId TemplateName;
+ TemplateTy Template;
+ TemplateNameKind TNK = isPackIndexingTemplateName(TemplateName, Template);
+ if (TNK != TNK_Non_template) {
+ if (AnnotatePackIndexingTemplateName(SS, TemplateName, Template, TNK))
+ return true;
+ } else {
+ SourceLocation Start = Tok.getLocation();
+ DeclSpec DS(AttrFactory);
+ SourceLocation CCLoc;
+ SourceLocation EndLoc = ParsePackIndexingType(DS);
+ if (DS.getTypeSpecType() == DeclSpec::TST_error)
+ return false;
- QualType Pattern = Sema::GetTypeFromParser(DS.getRepAsType());
- QualType Type =
- Actions.ActOnPackIndexingType(Pattern, DS.getPackIndexingExpr(),
- DS.getBeginLoc(), DS.getEllipsisLoc());
+ QualType Pattern = Sema::GetTypeFromParser(DS.getRepAsType());
+ QualType Type =
+ Actions.ActOnPackIndexingType(Pattern, DS.getPackIndexingExpr(),
+ DS.getBeginLoc(), DS.getEllipsisLoc());
- if (Type.isNull())
- return false;
+ if (Type.isNull())
+ return false;
- // C++ [cpp23.dcl.dcl-2]:
- // Previously, T...[n] would declare a pack of function parameters.
- // T...[n] is now a pack-index-specifier. [...] Valid C++ 2023 code that
- // declares a pack of parameters without specifying a declarator-id
- // becomes ill-formed.
- //
- // However, we still treat it as a pack indexing type because the use case
- // is fairly rare, to ensure semantic consistency given that we have
- // backported this feature to pre-C++26 modes.
- if (!Tok.is(tok::coloncolon) && !getLangOpts().CPlusPlus26 &&
- getCurScope()->isFunctionDeclarationScope())
- Diag(Start, diag::warn_pre_cxx26_ambiguous_pack_indexing_type) << Type;
-
- if (!TryConsumeToken(tok::coloncolon, CCLoc)) {
- AnnotateExistingIndexedTypeNamePack(ParsedType::make(Type), Start,
- EndLoc);
- return false;
+ // C++ [cpp23.dcl.dcl-2]:
+ // Previously, T...[n] would declare a pack of function parameters.
+ // T...[n] is now a pack-index-specifier. [...] Valid C++ 2023 code
+ // that declares a pack of parameters without specifying a
+ // declarator-id becomes ill-formed.
+ //
+ // However, we still treat it as a pack indexing type because the use
+ // case is fairly rare, to ensure semantic consistency given that we have
+ // backported this feature to pre-C++26 modes.
+ if (!Tok.is(tok::coloncolon) && !getLangOpts().CPlusPlus26 &&
+ getCurScope()->isFunctionDeclarationScope())
+ Diag(Start, diag::warn_pre_cxx26_ambiguous_pack_indexing_type) << Type;
+
+ if (!TryConsumeToken(tok::coloncolon, CCLoc)) {
+ AnnotateExistingIndexedTypeNamePack(ParsedType::make(Type), Start,
+ EndLoc);
+ return false;
+ }
+ if (Actions.ActOnCXXNestedNameSpecifierIndexedPack(SS, DS, CCLoc,
+ std::move(Type)))
+ SS.SetInvalid(SourceRange(Start, CCLoc));
+ HasScopeSpecifier = true;
}
- if (Actions.ActOnCXXNestedNameSpecifierIndexedPack(SS, DS, CCLoc,
- std::move(Type)))
- SS.SetInvalid(SourceRange(Start, CCLoc));
- HasScopeSpecifier = true;
}
// Preferred type might change when parsing qualifiers, we need the original.
diff --git a/clang/lib/Parse/ParseTemplate.cpp b/clang/lib/Parse/ParseTemplate.cpp
index 735a9bd1f9f1c..7a3629ddc19a9 100644
--- a/clang/lib/Parse/ParseTemplate.cpp
+++ b/clang/lib/Parse/ParseTemplate.cpp
@@ -1279,6 +1279,7 @@ ParsedTemplateArgument Parser::ParseTemplateTemplateArgument() {
// We may have a (non-dependent) template name.
TemplateTy Template;
UnqualifiedId Name;
+ bool IsPackIndexingTemplateName = false;
if (Tok.is(tok::annot_non_type)) {
NamedDecl *ND = getNonTypeAnnotation(Tok);
if (!isa<VarTemplateDecl>(ND))
@@ -1289,6 +1290,11 @@ ParsedTemplateArgument Parser::ParseTemplateTemplateArgument() {
TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
if (TemplateId->LAngleLoc.isValid())
return Result;
+ if (TemplateId->Template &&
+ TemplateId->Template.get().getAsPackIndexingTemplate()) {
+ Template = TemplateId->Template;
+ IsPackIndexingTemplateName = true;
+ }
Name.setIdentifier(TemplateId->Name, Tok.getLocation());
ConsumeAnnotationToken();
} else {
@@ -1299,18 +1305,23 @@ ParsedTemplateArgument Parser::ParseTemplateTemplateArgument() {
TryConsumeToken(tok::ellipsis, EllipsisLoc);
if (isEndOfTemplateArgument(Tok)) {
- bool MemberOfUnknownSpecialization;
- TemplateNameKind TNK = Actions.isTemplateName(
- getCurScope(), SS,
- /*hasTemplateKeyword=*/false, Name,
- /*ObjectType=*/nullptr,
- /*EnteringContext=*/false, Template, MemberOfUnknownSpecialization);
- if (TNK == TNK_Dependent_template_name || TNK == TNK_Type_template ||
- TNK == TNK_Var_template || TNK == TNK_Concept_template) {
- // We have an id-expression that refers to a class template or
- // (C++0x) alias template.
+ if (IsPackIndexingTemplateName) {
Result = ParsedTemplateArgument(/*TemplateKwLoc=*/SourceLocation(), SS,
Template, Name.StartLocation);
+ } else {
+ bool MemberOfUnknownSpecialization;
+ TemplateNameKind TNK = Actions.isTemplateName(
+ getCurScope(), SS,
+ /*hasTemplateKeyword=*/false, Name,
+ /*ObjectType=*/nullptr,
+ /*EnteringContext=*/false, Template, MemberOfUnknownSpecialization);
+ if (TNK == TNK_Dependent_template_name || TNK == TNK_Type_template ||
+ TNK == TNK_Var_template || TNK == TNK_Concept_template) {
+ // We have an id-expression that refers to a class template or
+ // (C++0x) alias template.
+ Result = ParsedTemplateArgument(/*TemplateKwLoc=*/SourceLocation(),
+ SS, Template, Name.StartLocation);
+ }
}
}
}
diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp
index 4b9b604d27b16..d3cb9b34a7609 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -5951,7 +5951,8 @@ class ConceptInfo {
static QualType deduceType(const TypeConstraint &T) {
// Assume a same_as<T> return type constraint is std::same_as or equivalent.
// In this case the return type is T.
- DeclarationName DN = T.getNamedConcept().getAsTemplateDecl()->getDeclName();
+ DeclarationName DN =
+ T.getConceptReference()->getConceptNameInfo().getName();
if (DN.isIdentifier() && DN.getAsIdentifierInfo()->isStr("same_as"))
if (const auto *Args = T.getTemplateArgsAsWritten())
if (Args->getNumTemplateArgs() == 1) {
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 2b524a956ecc4..34f6ccdbc2fe6 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -2865,6 +2865,12 @@ ExprResult Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS,
IdentifierInfo *II = Name.getAsIdentifierInfo();
SourceLocation NameLoc = NameInfo.getLoc();
+ if (Id.getKind() == UnqualifiedIdKind::IK_TemplateId &&
+ Id.TemplateId->Template)
+ if (TemplateName TN = Id.TemplateId->Template.get();
+ TN.getAsPackIndexingTemplate())
+ return CheckVarOrConceptTemplateTemplateId(NameInfo, TN, TemplateArgs);
+
if (II && II->isEditorPlaceholder()) {
// FIXME: When typed placeholders are supported we can create a typed
// placeholder expression node.
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 81bc6c68f377d..24b41a68db0fd 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -6496,7 +6496,8 @@ static ExprResult BuildConvertedConstantExpression(Sema &S, Expr *From,
NamedDecl *Dest,
APValue &PreNarrowingValue) {
[[maybe_unused]] bool isCCEAllowedPreCXX11 =
- (CCE == CCEKind::TempArgStrict || CCE == CCEKind::ExplicitBool);
+ (CCE == CCEKind::TempArgStrict || CCE == CCEKind::ExplicitBool ||
+ CCE == CCEKind::PackIndex);
assert((S.getLangOpts().CPlusPlus11 || isCCEAllowedPreCXX11) &&
"converted constant expression outside C++11 or TTP matching");
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 64a05a8bc34e3..56d0c5a4d2fe9 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -1127,7 +1127,7 @@ bool Sema::CheckTypeConstraint(TemplateIdAnnotation *TypeConstr) {
NamedDecl *CD = nullptr;
bool IsTypeConcept = false;
bool RequiresArguments = false;
- if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(TN.getAsTemplateDecl())) {
+ if (auto *TTP = TN.getAsTemplateTemplateParmDecl()) {
IsTypeConcept = TTP->isTypeConceptTemplateParam();
RequiresArguments =
TTP->getTemplateParameters()->getMinRequiredArguments() > 1;
@@ -1181,8 +1181,8 @@ bool Sema::BuildTypeConstraint(const CXXScopeSpec &SS,
return true;
TemplateName TN = TypeConstr->Template.get();
- TemplateDecl *CD = cast<TemplateDecl>(TN.getAsTemplateDecl());
UsingShadowDecl *USD = TN.getAsUsingShadowDecl();
+ TemplateDecl *CD = TN.getAsTemplateDecl();
DeclarationNameInfo ConceptName(DeclarationName(TypeConstr->Name),
TypeConstr->TemplateNameLoc);
@@ -1201,7 +1201,8 @@ bool Sema::BuildTypeConstraint(const CXXScopeSpec &SS,
}
return AttachTypeConstraint(
SS.isSet() ? SS.getWithLocInContext(Context) : NestedNameSpecifierLoc(),
- ConceptName, CD, /*FoundDecl=*/USD ? cast<NamedDecl>(USD) : CD,
+ ConceptName, TN,
+ /*FoundDecl=*/USD ? cast<NamedDecl>(USD) : cast_if_present<NamedDecl>(CD),
TypeConstr->LAngleLoc.isValid() ? &TemplateArgs : nullptr,
ConstrainedParameter, EllipsisLoc);
}
@@ -1209,7 +1210,7 @@ bool Sema::BuildTypeConstraint(const CXXScopeSpec &SS,
template <typename ArgumentLocAppender>
static ExprResult formImmediatelyDeclaredConstraint(
Sema &S, NestedNameSpecifierLoc NS, DeclarationNameInfo NameInfo,
- NamedDecl *NamedConcept, NamedDecl *FoundDecl, SourceLocation LAngleLoc,
+ TemplateName NamedConcept, NamedDecl *FoundDecl, SourceLocation LAngleLoc,
SourceLocation RAngleLoc, QualType ConstrainedType,
SourceLocation ParamNameLoc, ArgumentLocAppender Appender,
SourceLocation EllipsisLoc) {
@@ -1229,7 +1230,8 @@ static ExprResult formImmediatelyDeclaredConstraint(
CXXScopeSpec SS;
SS.Adopt(NS);
ExprResult ImmediatelyDeclaredConstraint;
- if (auto *CD = dyn_cast<ConceptDecl>(NamedConcept)) {
+ if (auto *CD =
+ dyn_cast_if_present<ConceptDecl>(NamedConcept.getAsTemplateDecl())) {
ImmediatelyDeclaredConstraint = S.CheckConceptTemplateId(
SS, /*TemplateKWLoc=*/SourceLocation(), NameInfo,
/*FoundDecl=*/FoundDecl ? FoundDecl : CD, CD, &ConstraintArgs,
@@ -1239,9 +1241,8 @@ 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(NameInfo, CDT, &ConstraintArgs);
+ ImmediatelyDeclaredConstraint = S.CheckVarOrConceptTemplateTemplateId(
+ NameInfo, NamedConcept, &ConstraintArgs);
}
if (ImmediatelyDeclaredConstraint.isInvalid() || !EllipsisLoc.isValid())
return ImmediatelyDeclaredConstraint;
@@ -1268,8 +1269,7 @@ static ExprResult formImmediatelyDeclaredConstraint(
bool Sema::AttachTypeConstraint(NestedNameSpecifierLoc NS,
DeclarationNameInfo NameInfo,
- TemplateDecl *NamedConcept,
- NamedDecl *FoundDecl,
+ TemplateName NamedConcept, NamedDecl *FoundDecl,
const TemplateArgumentListInfo *TemplateArgs,
TemplateTypeParmDecl *ConstrainedParameter,
SourceLocation EllipsisLoc) {
@@ -1296,13 +1296,12 @@ bool Sema::AttachTypeConstraint(NestedNameSpecifierLoc NS,
if (ImmediatelyDeclaredConstraint.isInvalid())
return true;
- auto *CL =
- ConceptReference::Create(Context, /*NNS=*/NS,
- /*TemplateKWLoc=*/SourceLocation{},
- /*ConceptNameInfo=*/NameInfo,
- /*FoundDecl=*/FoundDecl,
- /*NamedConcept=*/TemplateName(NamedConcept),
- /*ArgsWritten=*/ArgsAsWritten);
+ auto *CL = ConceptReference::Create(Context, /*NNS=*/NS,
+ /*TemplateKWLoc=*/SourceLocation{},
+ /*ConceptNameInfo=*/NameInfo,
+ /*FoundDecl=*/FoundDecl,
+ /*NamedConcept=*/NamedConcept,
+ /*ArgsWritten=*/ArgsAsWritten);
ConstrainedParameter->setTypeConstraint(
CL, ImmediatelyDeclaredConstraint.get(), std::nullopt);
return false;
@@ -1331,7 +1330,7 @@ bool Sema::AttachTypeConstraint(AutoTypeLoc TL,
return true;
ExprResult ImmediatelyDeclaredConstraint = formImmediatelyDeclaredConstraint(
*this, TL.getNestedNameSpecifierLoc(), TL.getConceptNameInfo(),
- TL.getNamedConcept().getAsTemplateDecl(),
+ TL.getNamedConcept(),
/*FoundDecl=*/TL.getFoundDecl(), TL.getLAngleLoc(), TL.getRAngleLoc(),
BuildDecltypeType(Ref), OrigConstrainedParm->getLocation(),
[&](TemplateArgumentListInfo &ConstraintArgs) {
@@ -3772,6 +3771,10 @@ QualType Sema::CheckTemplateIdType(ElaboratedTypeKeyword Keyword,
if (!Template) {
if (const auto *S = UnderlyingName.getAsSubstTemplateTemplateParmPack()) {
Template = S->getParameterPack();
+ } else if (const auto *PI = UnderlyingName.getAsPackIndexingTemplate()) {
+ Template = PI->getParameterPack();
+ if (!Template)
+ Template = PI->getPattern().getAsTemplateDecl();
} else if (const auto *DTN = UnderlyingName.getAsDependentTemplateName()) {
if (DTN->getName().getIdentifier())
// When building a template-id where the template-name is dependent,
@@ -4833,19 +4836,22 @@ ExprResult Sema::CheckVarTemplateId(
}
ExprResult Sema::CheckVarOrConceptTemplateTemplateId(
- const DeclarationNameInfo &NameInfo, TemplateTemplateParmDecl *Template,
+ const DeclarationNameInfo &NameInfo, TemplateName Template,
const TemplateArgumentListInfo *TemplateArgs) {
- assert(Template && "A variable template id without template?");
+ TemplateTemplateParmDecl *Parameter =
+ Template.getAsTemplateTemplateParmDecl();
+ assert(Parameter && "A variable template id without template?");
- if (Template->templateParameterKind() != TemplateNameKind::TNK_Var_template &&
- Template->templateParameterKind() !=
+ if (Parameter->templateParameterKind() !=
+ TemplateNameKind::TNK_Var_template &&
+ Parameter->templateParameterKind() !=
TemplateNameKind::TNK_Concept_template)
return ExprResult();
// Check that the template argument list is well-formed for this template.
CheckTemplateArgumentInfo CTAI;
if (CheckTemplateArgumentList(
- Template, /*Template kw loc=*/{},
+ Parameter, /*Template kw loc=*/{},
// FIXME: TemplateArgs will not be modified because
// UpdateArgsWithConversions is false, however, we should
// CheckTemplateArgumentList to be const-correct.
@@ -4854,8 +4860,8 @@ ExprResult Sema::CheckVarOrConceptTemplateTemplateId(
/*UpdateArgsWithConversions=*/false))
return true;
- return DependentTemplateIdExpr::Create(getASTContext(), NameInfo,
- TemplateName(Template), *TemplateArgs);
+ return DependentTemplateIdExpr::Create(getASTContext(), NameInfo, Template,
+ *TemplateArgs);
}
void Sema::diagnoseMissingTemplateArguments(TemplateName Name,
@@ -5000,8 +5006,8 @@ ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS,
assert(TemplateKWLoc.isInvalid() &&
"template keyword in front of a template parameter?");
return CheckVarOrConceptTemplateTemplateId(
- R.getLookupNameInfo(), R.getAsSingle<TemplateTemplateParmDecl>(),
- TemplateArgs);
+ R.getLookupNameInfo(),
+ TemplateName(R.getAsSingle<TemplateTemplateParmDecl>()), TemplateArgs);
}
// Function templates
@@ -9316,7 +9322,7 @@ void Sema::CheckConceptRedefinition(ConceptDecl *NewDecl,
}
bool Sema::CheckConceptUseInDefinition(NamedDecl *Concept, SourceLocation Loc) {
- if (auto *CE = llvm::dyn_cast<ConceptDecl>(Concept);
+ if (auto *CE = llvm::dyn_cast_if_present<ConceptDecl>(Concept);
CE && !CE->isInvalidDecl() && !CE->hasDefinition()) {
Diag(Loc, diag::err_recursive_concept) << CE;
Diag(CE->getLocation(), diag::note_declared_at);
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index f177f0aa8645e..fd444e70b5192 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -247,8 +247,10 @@ getDeducedNTTParameterFromExpr(const Expr *E, unsigned Depth) {
if (NTTP->getDepth() == Depth)
return NTTP;
+ // A pack-index-template-name is not deducible.
if (const auto *DTI = dyn_cast<DependentTemplateIdExpr>(E))
- if (DTI->getParameter()->getDepth() == Depth)
+ if (!DTI->getTemplateName().getAsPackIndexingTemplate() &&
+ DTI->getParameter()->getDepth() == Depth)
return DTI->getParameter();
return nullptr;
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 21781f7474e56..ffbe8bb0506bc 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -3158,7 +3158,7 @@ bool Sema::SubstTypeConstraint(
}
return AttachTypeConstraint(
TC->getNestedNameSpecifierLoc(), TC->getConceptNameInfo(),
- TC->getNamedConcept().getAsTemplateDecl(),
+ TC->getNamedConcept(),
/*FoundDecl=*/TC->getConceptReference()->getFoundDecl(), &InstArgs, Inst,
Inst->isParameterPack()
? cast<CXXFoldExpr>(TC->getImmediatelyDeclaredConstraint())
@@ -4649,14 +4649,22 @@ ExprResult Sema::SubstConceptTemplateArguments(
TemplateTemplateParmDecl *TTP = E->getParameter();
unsigned Depth = TTP->getDepth();
unsigned Pos = TTP->getPosition();
- ConceptDecl *ResolvedConcept = nullptr;
+ if (!MLTAL.hasTemplateArgument(Depth, Pos))
+ return E;
- if (MLTAL.hasTemplateArgument(Depth, Pos)) {
- TemplateArgument Arg = MLTAL(Depth, Pos);
- assert(Arg.getKind() == TemplateArgument::Template);
- ResolvedConcept =
- dyn_cast<ConceptDecl>(Arg.getAsTemplate().getAsTemplateDecl());
+ TemplateArgument Arg = MLTAL(Depth, Pos);
+ if (PackIndexingTemplateStorage *PI =
+ E->getTemplateName().getAsPackIndexingTemplate()) {
+ UnsignedOrNone Index = PI->getSelectedIndex();
+ if (Arg.getKind() != TemplateArgument::Pack || !Index ||
+ *Index >= Arg.pack_size())
+ return E;
+ Arg = Arg.getPackAsArray()[*Index];
}
+ if (Arg.getKind() != TemplateArgument::Template)
+ return E;
+ ConceptDecl *ResolvedConcept = dyn_cast_if_present<ConceptDecl>(
+ Arg.getAsTemplate().getAsTemplateDecl());
if (!ResolvedConcept)
return E;
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index a53500f2da497..83a81f2df69cf 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -145,6 +145,11 @@ class CollectUnexpandedParameterPacksVisitor
/// Record occurrences of template template parameter packs.
bool TraverseTemplateName(TemplateName Template,
bool TraverseQualifier = true) override {
+
+ if (PackIndexingTemplateStorage *PI =
+ Template.getAsPackIndexingTemplate())
+ return DynamicRecursiveASTVisitor::TraverseStmt(PI->getIndexExpr());
+
if (auto *TTP = dyn_cast_or_null<TemplateTemplateParmDecl>(
Template.getAsTemplateDecl())) {
if (TTP->isParameterPack())
@@ -683,6 +688,13 @@ void Sema::collectUnexpandedParameterPacks(QualType T,
CollectUnexpandedParameterPacksVisitor(Unexpanded).TraverseType(T);
}
+void Sema::collectUnexpandedParameterPacks(
+ TemplateName Template,
+ SmallVectorImpl<UnexpandedParameterPack> &Unexpanded) {
+ CollectUnexpandedParameterPacksVisitor(Unexpanded)
+ .TraverseTemplateName(Template);
+}
+
void Sema::collectUnexpandedParameterPacks(TypeLoc TL,
SmallVectorImpl<UnexpandedParameterPack> &Unexpanded) {
CollectUnexpandedParameterPacksVisitor(Unexpanded).TraverseTypeLoc(TL);
@@ -1386,6 +1398,62 @@ ExprResult Sema::BuildPackIndexingExpr(Expr *PackExpression,
ExpandedExprs, FullySubstituted);
}
+TemplateName Sema::ActOnPackIndexingTemplateName(TemplateName Pattern,
+ SourceLocation NameLoc,
+ Expr *IndexExpr) {
+ if (Pattern.isNull() || !IndexExpr)
+ return TemplateName();
+
+ // C++29 [temp.names]p3:
+ // The simple-template-name P in a pack-index-template-name shall denote a
+ // pack.
+ bool DenotesPack = Pattern.containsUnexpandedParameterPack();
+ if (!DenotesPack)
+ Diag(NameLoc, diag::err_expected_name_of_pack) << Pattern;
+
+ TemplateName Name = BuildPackIndexingTemplateName(Pattern, IndexExpr);
+ if (!Name.isNull() && DenotesPack)
+ DiagCompat(NameLoc, diag_compat::pack_indexing_template);
+ return Name;
+}
+
+TemplateName
+Sema::BuildPackIndexingTemplateName(TemplateName Pattern, Expr *IndexExpr,
+ bool FullySubstituted,
+ ArrayRef<TemplateName> Expansions) {
+ if (!IndexExpr->isInstantiationDependent()) {
+ llvm::APSInt Value(Context.getIntWidth(Context.getSizeType()));
+ ExprResult Res = CheckConvertedConstantExpression(
+ IndexExpr, Context.getSizeType(), Value, CCEKind::PackIndex);
+ if (!Res.isUsable() || !Value.isRepresentableByInt64())
+ return TemplateName();
+
+ IndexExpr = Res.get();
+ uint64_t V = Value.getZExtValue();
+ if (FullySubstituted && V >= Expansions.size()) {
+ Diag(IndexExpr->getBeginLoc(), diag::err_pack_index_out_of_bound)
+ << V << Pattern << Expansions.size();
+ return TemplateName();
+ }
+ }
+
+ return Context.getPackIndexingTemplateName(Pattern, IndexExpr,
+ FullySubstituted, Expansions);
+}
+
+TypeResult Sema::ActOnPackIndexingDeducedTemplateSpecializationType(
+ TemplateName Name, SourceLocation NameLoc) {
+
+ QualType T = Context.getDeducedTemplateSpecializationType(
+ DeducedKind::Undeduced, QualType(), ElaboratedTypeKeyword::None, Name);
+ TypeLocBuilder TLB;
+ auto TL = TLB.push<DeducedTemplateSpecializationTypeLoc>(T);
+ TL.setElaboratedKeywordLoc(SourceLocation());
+ TL.setQualifierLoc(NestedNameSpecifierLoc());
+ TL.setNameLoc(NameLoc);
+ return CreateParsedType(T, TLB.getTypeSourceInfo(Context, T));
+}
+
TemplateArgumentLoc Sema::getTemplateArgumentPackExpansionPattern(
TemplateArgumentLoc OrigLoc, SourceLocation &Ellipsis,
UnsignedOrNone &NumExpansions) const {
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 27197f36bcc5a..ad9204b12524b 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -1325,12 +1325,11 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) {
? AutoTypeKeyword::DecltypeAuto
: AutoTypeKeyword::Auto;
- TemplateDecl *TypeConstraintConcept = nullptr;
+ TemplateName TypeConstraintConcept;
llvm::SmallVector<TemplateArgument, 8> TemplateArgs;
if (DS.isConstrainedAuto()) {
if (TemplateIdAnnotation *TemplateId = DS.getRepAsTemplateId()) {
- TypeConstraintConcept =
- cast<TemplateDecl>(TemplateId->Template.get().getAsTemplateDecl());
+ TypeConstraintConcept = TemplateId->Template.get();
TemplateArgumentListInfo TemplateArgsInfo;
TemplateArgsInfo.setLAngleLoc(TemplateId->LAngleLoc);
TemplateArgsInfo.setRAngleLoc(TemplateId->RAngleLoc);
@@ -1344,8 +1343,7 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) {
}
}
Result = S.Context.getAutoType(DeducedKind::Undeduced, QualType(), AutoKW,
- TemplateName(TypeConstraintConcept),
- TemplateArgs);
+ TypeConstraintConcept, TemplateArgs);
break;
}
@@ -3116,7 +3114,7 @@ InventTemplateParameter(TypeProcessingState &state, QualType T,
if (!Invalid) {
S.AttachTypeConstraint(
AutoLoc.getNestedNameSpecifierLoc(), AutoLoc.getConceptNameInfo(),
- AutoLoc.getNamedConcept().getAsTemplateDecl(),
+ AutoLoc.getNamedConcept(),
/*FoundDecl=*/AutoLoc.getFoundDecl(),
AutoLoc.hasExplicitTemplateArgs() ? &TAL : nullptr,
InventedTemplateParam, D.getEllipsisLoc());
@@ -3144,15 +3142,16 @@ InventTemplateParameter(TypeProcessingState &state, QualType T,
}
}
if (!Invalid) {
- UsingShadowDecl *USD =
- TemplateId->Template.get().getAsUsingShadowDecl();
- TemplateDecl *CD = TemplateId->Template.get().getAsTemplateDecl();
+ TemplateName TN = TemplateId->Template.get();
+ UsingShadowDecl *USD = TN.getAsUsingShadowDecl();
+ TemplateDecl *CD = TN.getAsTemplateDecl();
S.AttachTypeConstraint(
D.getDeclSpec().getTypeSpecScope().getWithLocInContext(S.Context),
DeclarationNameInfo(DeclarationName(TemplateId->Name),
TemplateId->TemplateNameLoc),
- CD,
- /*FoundDecl=*/USD ? cast<NamedDecl>(USD) : CD,
+ TN,
+ /*FoundDecl=*/
+ USD ? cast<NamedDecl>(USD) : cast_if_present<NamedDecl>(CD),
TemplateId->LAngleLoc.isValid() ? &TemplateArgsInfo : nullptr,
InventedTemplateParam, D.getEllipsisLoc());
}
@@ -4358,7 +4357,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
// and at most one function declarator if this is a function declaration.
// If T is a deduced class template specialization type, only parentheses
// are allowed.
- if (auto *DT = T->getAs<DeducedType>()) {
+ if (auto *DT = T->getAs<DeducedType>(); DT && !T->containsErrors()) {
const AutoType *AT = T->getAs<AutoType>();
bool IsClassTemplateDeduction = isa<DeducedTemplateSpecializationType>(DT);
if ((AT && AT->isDecltypeAuto()) || IsClassTemplateDeduction) {
@@ -6140,12 +6139,9 @@ namespace {
TemplateId->NumArgs);
SemaRef.translateTemplateArguments(TemplateArgsPtr, TemplateArgsInfo);
}
- DeclarationNameInfo DNI =
- DeclarationNameInfo(TL.getTypePtr()
- ->getTypeConstraintConcept()
- .getAsTemplateDecl()
- ->getDeclName(),
- TemplateId->TemplateNameLoc);
+ DeclarationNameInfo DNI = Context.getNameForTemplate(
+ TL.getTypePtr()->getTypeConstraintConcept(),
+ TemplateId->TemplateNameLoc);
NamedDecl *FoundDecl;
if (auto TN = TemplateId->Template.get();
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index b0c836325a971..755f20ffe85f0 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -639,6 +639,9 @@ class TreeTransform {
NamedDecl *FirstQualifierInScope = nullptr,
bool AllowInjectedClassName = false);
+ TemplateName TransformConceptTemplateName(TemplateName Name,
+ SourceLocation NameLoc);
+
/// Transform the given template argument.
///
/// By default, this operation transforms the type, expression, or
@@ -1143,11 +1146,10 @@ class TreeTransform {
/// By default, builds a new AutoType with the given deduced type.
QualType RebuildAutoType(DeducedKind DK, QualType DeducedAsType,
AutoTypeKeyword Keyword,
- ConceptDecl *TypeConstraintConcept,
+ TemplateName TypeConstraintConcept,
ArrayRef<TemplateArgument> TypeConstraintArgs) {
- return SemaRef.Context.getAutoType(DK, DeducedAsType, Keyword,
- TemplateName(TypeConstraintConcept),
- TypeConstraintArgs);
+ return SemaRef.Context.getAutoType(
+ DK, DeducedAsType, Keyword, TypeConstraintConcept, TypeConstraintArgs);
}
/// By default, builds a new DeducedTemplateSpecializationType with the given
@@ -1355,6 +1357,18 @@ class TreeTransform {
ArgPack, AssociatedDecl, Index, Final);
}
+ /// Build a new pack-index-template-name ([temp.names]).
+ ///
+ /// By default, performs semantic analysis to build the new template name.
+ /// Subclasses may override this routine to provide different behavior.
+ TemplateName
+ RebuildPackIndexingTemplateName(TemplateName Pattern, Expr *IndexExpr,
+ bool FullySubstituted,
+ ArrayRef<TemplateName> Expansions = {}) {
+ return getSema().BuildPackIndexingTemplateName(
+ Pattern, IndexExpr, FullySubstituted, Expansions);
+ }
+
/// Build a new compound statement.
///
/// By default, performs semantic analysis to build the new statement.
@@ -4973,6 +4987,101 @@ TemplateName TreeTransform<Derived>::TransformTemplateName(
S->getFinal());
}
+ if (PackIndexingTemplateStorage *PI = Name.getAsPackIndexingTemplate()) {
+ assert(!QualifierLoc && "Unexpected qualified pack-index-template-name");
+
+ ExprResult IndexExpr;
+ {
+ EnterExpressionEvaluationContext ConstantContext(
+ SemaRef, Sema::ExpressionEvaluationContext::ConstantEvaluated);
+ IndexExpr = getDerived().TransformExpr(PI->getIndexExpr());
+ if (IndexExpr.isInvalid())
+ return TemplateName();
+ }
+
+ auto TransformOne = [&](TemplateName N) {
+ NestedNameSpecifierLoc NoQualifier;
+ return getDerived().TransformTemplateName(
+ NoQualifier, TemplateKWLoc, N, NameLoc, ObjectType,
+ FirstQualifierInScope, AllowInjectedClassName);
+ };
+
+ TemplateName Pattern = PI->getPattern();
+ SmallVector<TemplateName, 4> SubstitutedNames;
+ ArrayRef<TemplateName> Names = PI->getExpansions();
+
+ bool NotYetExpanded = Names.empty();
+ bool FullySubstituted = true;
+
+ if (Names.empty() && !PI->expandsToEmptyPack())
+ Names = ArrayRef(&Pattern, 1);
+
+ for (TemplateName N : Names) {
+ if (!N.containsUnexpandedParameterPack()) {
+ TemplateName Transformed = TransformOne(N);
+ if (Transformed.isNull())
+ return TemplateName();
+ SubstitutedNames.push_back(Transformed);
+ continue;
+ }
+
+ SmallVector<UnexpandedParameterPack, 2> Unexpanded;
+ getSema().collectUnexpandedParameterPacks(N, Unexpanded);
+ assert(!Unexpanded.empty() && "Pack expansion without parameter packs?");
+
+ bool ShouldExpand = true;
+ bool RetainExpansion = false;
+ UnsignedOrNone NumExpansions = std::nullopt;
+ if (getDerived().TryExpandParameterPacks(
+ NameLoc, SourceRange(), Unexpanded,
+ /*FailOnPackProducingTemplates=*/true, ShouldExpand,
+ RetainExpansion, NumExpansions))
+ return TemplateName();
+
+ if (!ShouldExpand) {
+ Sema::ArgPackSubstIndexRAII SubstIndex(getSema(), std::nullopt);
+ TemplateName Pack = TransformOne(N);
+ if (Pack.isNull())
+ return TemplateName();
+ if (NotYetExpanded) {
+ FullySubstituted = false;
+ return getDerived().RebuildPackIndexingTemplateName(
+ Pack, IndexExpr.get(), FullySubstituted);
+ }
+ SubstitutedNames.push_back(Pack);
+ continue;
+ }
+
+ for (unsigned I = 0; I != *NumExpansions; ++I) {
+ Sema::ArgPackSubstIndexRAII SubstIndex(getSema(), I);
+ TemplateName Out = TransformOne(N);
+ if (Out.isNull())
+ return TemplateName();
+ SubstitutedNames.push_back(Out);
+ FullySubstituted &= !Out.containsUnexpandedParameterPack();
+ }
+
+ // If we're supposed to retain a pack expansion, do so by temporarily
+ // forgetting the partially-substituted parameter pack.
+ if (RetainExpansion) {
+ FullySubstituted = false;
+ ForgetPartiallySubstitutedPackRAII Forget(getDerived());
+ TemplateName Out = TransformOne(N);
+ if (Out.isNull())
+ return TemplateName();
+ SubstitutedNames.push_back(Out);
+ }
+ }
+
+ Sema::ArgPackSubstIndexRAII SubstIndex(getSema(), std::nullopt);
+ TemplateName NewPattern = TransformOne(Pattern);
+ if (NewPattern.isNull())
+ return TemplateName();
+
+ return getDerived().RebuildPackIndexingTemplateName(
+ NewPattern, IndexExpr.get(), FullySubstituted, SubstitutedNames);
+ }
+
assert(!Name.getAsDeducedTemplateName() &&
"DeducedTemplateName should not escape partial ordering");
@@ -4996,6 +5105,15 @@ TemplateName TreeTransform<Derived>::TransformTemplateName(
llvm_unreachable("overloaded function decl survived to here");
}
+template <typename Derived>
+TemplateName
+TreeTransform<Derived>::TransformConceptTemplateName(TemplateName Name,
+ SourceLocation NameLoc) {
+ NestedNameSpecifierLoc QualifierLoc;
+ return getDerived().TransformTemplateName(
+ QualifierLoc, /*TemplateKWLoc=*/SourceLocation(), Name, NameLoc);
+}
+
template <typename Derived>
TemplateArgument TreeTransform<Derived>::TransformNamedTemplateTemplateArgument(
NestedNameSpecifierLoc &QualifierLoc, SourceLocation TemplateKeywordLoc,
@@ -7567,14 +7685,15 @@ QualType TreeTransform<Derived>::TransformAutoType(TypeLocBuilder &TLB,
return QualType();
}
- ConceptDecl *NewCD = nullptr;
+ TemplateName NewCD;
TemplateArgumentListInfo NewTemplateArgs;
NestedNameSpecifierLoc NewNestedNameSpec;
if (T->isConstrained()) {
assert(TL.getConceptReference());
- NewCD = cast_or_null<ConceptDecl>(getDerived().TransformDecl(
- TL.getConceptNameLoc(),
- T->getTypeConstraintConcept().getAsTemplateDecl()));
+ NewCD = getDerived().TransformConceptTemplateName(
+ T->getTypeConstraintConcept(), TL.getConceptNameLoc());
+ if (NewCD.isNull())
+ return QualType();
NewTemplateArgs.setLAngleLoc(TL.getLAngleLoc());
NewTemplateArgs.setRAngleLoc(TL.getRAngleLoc());
@@ -7614,10 +7733,11 @@ QualType TreeTransform<Derived>::TransformAutoType(TypeLocBuilder &TLB,
NewTL.setConceptReference(nullptr);
if (T->isConstrained()) {
- DeclarationName ConceptName = TL.getTypePtr()
- ->getTypeConstraintConcept()
- .getAsTemplateDecl()
- ->getDeclName();
+ DeclarationName ConceptName =
+ SemaRef.Context
+ .getNameForTemplate(TL.getTypePtr()->getTypeConstraintConcept(),
+ TL.getConceptNameLoc())
+ .getName();
DeclarationNameInfo DNI =
DeclarationNameInfo(ConceptName, TL.getConceptNameLoc(), ConceptName);
auto *CR = ConceptReference::Create(
@@ -16554,22 +16674,21 @@ template <typename Derived>
ExprResult TreeTransform<Derived>::TransformDependentTemplateIdExpr(
DependentTemplateIdExpr *E) {
- NestedNameSpecifierLoc Loc;
- TemplateName Name = getDerived().TransformTemplateName(
- Loc, /*Template Keyword=*/SourceLocation(), E->getTemplateName(),
- E->getNameLoc());
+ TemplateName Name = getDerived().TransformConceptTemplateName(
+ E->getTemplateName(), E->getNameLoc());
if (Name.isNull())
return ExprError();
- TemplateDecl *TD = Name.getAsTemplateDecl();
-
- assert(TD && "A dependent template id always refers to a template decl");
-
TemplateArgumentListInfo TransArgs(E->getLAngleLoc(), E->getRAngleLoc());
if (getDerived().TransformTemplateArguments(
E->template_arguments().data(), E->getNumTemplateArgs(), TransArgs))
return ExprError();
+ TemplateDecl *TD = Name.getAsTemplateDecl();
+ if (!TD)
+ return SemaRef.CheckVarOrConceptTemplateTemplateId(E->getNameInfo(), Name,
+ &TransArgs);
+
CXXScopeSpec SS;
LookupResult R(SemaRef, E->getNameInfo(), Sema::LookupOrdinaryName);
diff --git a/clang/lib/Serialization/TemplateArgumentHasher.cpp b/clang/lib/Serialization/TemplateArgumentHasher.cpp
index 6b598d26c3098..a6d5ea6e0e523 100644
--- a/clang/lib/Serialization/TemplateArgumentHasher.cpp
+++ b/clang/lib/Serialization/TemplateArgumentHasher.cpp
@@ -113,6 +113,9 @@ void TemplateArgumentHasher::AddTemplateName(TemplateName Name) {
AddTemplateName(QTN->getUnderlyingTemplate());
break;
}
+ case TemplateName::PackIndexingTemplate:
+ AddTemplateName(Name.getAsPackIndexingTemplate()->getPattern());
+ break;
case TemplateName::OverloadedTemplate:
case TemplateName::AssumedTemplate:
case TemplateName::DependentTemplate:
diff --git a/clang/test/AST/ast-dump-pack-indexing-template.cpp b/clang/test/AST/ast-dump-pack-indexing-template.cpp
new file mode 100644
index 0000000000000..4fcbd90d16862
--- /dev/null
+++ b/clang/test/AST/ast-dump-pack-indexing-template.cpp
@@ -0,0 +1,56 @@
+// RUN: %clang_cc1 -std=c++2d -ast-dump -ast-dump-filter=Dump %s | FileCheck %s
+
+template <class T> struct A {};
+template <class T> struct B {};
+
+template <template <class> class... TT>
+struct DumpDependent {
+ using type = TT...[1]<int>;
+};
+
+// CHECK-LABEL: Dumping DumpDependent:
+// CHECK: TypeAliasDecl {{.*}} type 'TT...[1]<int>'
+// CHECK-NEXT: `-TemplateSpecializationType {{.*}} 'TT...[1]<int>' dependent
+// CHECK-NEXT: |-name: 'TT...[1]':'template-parameter-0-0...[1]' pack_indexing index 1
+// CHECK-NEXT: | |-pattern: 'TT':'template-parameter-0-0'
+// CHECK-NEXT: | | `-TemplateTemplateParmDecl {{.*}} depth 0 index 0 ... TT
+// CHECK-NEXT: | `-index: ConstantExpr {{.*}} '__size_t':'unsigned long'
+// CHECK-NEXT: | `-value: Int 1
+// CHECK-NEXT: `-TemplateArgument type 'int'
+
+using DumpSubstituted = DumpDependent<A, B>::type;
+
+// CHECK-LABEL: Dumping DumpSubstituted:
+// CHECK: TypeAliasDecl {{.*}} DumpSubstituted 'DumpDependent<A, B>::type':'B<int>'
+// CHECK: TemplateSpecializationType {{.*}} 'TT...[1]<int>' sugar
+// CHECK-NEXT: |-name: 'TT...[1]':'B' pack_indexing fully_substituted index 1
+
+template <class T> concept C = true;
+
+template <template <class> concept... CC>
+constexpr bool DumpConceptId = CC...[0]<int>;
+
+// CHECK-LABEL: Dumping DumpConceptId:
+// CHECK: VarTemplateDecl {{.*}} DumpConceptId
+// CHECK: DependentTemplateIdExpr {{.*}} concept
+// CHECK-NEXT: `-name: 'CC...[0]':'template-parameter-0-0...[0]' pack_indexing index 0
+// CHECK-NEXT: |-pattern: 'CC':'template-parameter-0-0'
+// CHECK-NEXT: | `-TemplateTemplateParmDecl {{.*}} depth 0 index 0 ... CC
+// CHECK-NEXT: `-index: ConstantExpr {{.*}} '__size_t':'unsigned long'
+// CHECK-NEXT: `-value: Int 0
+
+template <template <class> auto... VV>
+constexpr int DumpVariableTemplateId = VV...[1]<int>;
+
+// CHECK-LABEL: Dumping DumpVariableTemplateId:
+// CHECK: DependentTemplateIdExpr {{.*}} variable template
+// CHECK-NEXT: `-name: 'VV...[1]':'template-parameter-0-0...[1]' pack_indexing index 1
+
+template <template <class> concept... CC>
+struct DumpTypeConstraint {
+ template <CC...[0] T>
+ static void f();
+};
+
+// CHECK-LABEL: Dumping DumpTypeConstraint:
+// CHECK: TemplateTypeParmDecl {{.*}} Concept {{.*}} 'CC...[0]' depth 1 index 0 T
diff --git a/clang/test/AST/ast-print-pack-indexing-template.cpp b/clang/test/AST/ast-print-pack-indexing-template.cpp
new file mode 100644
index 0000000000000..fddf0f46c7820
--- /dev/null
+++ b/clang/test/AST/ast-print-pack-indexing-template.cpp
@@ -0,0 +1,42 @@
+// RUN: %clang_cc1 -ast-print -std=c++2d %s | FileCheck %s
+
+template <unsigned N, template <class> class... TT>
+struct S {
+ using type = TT...[N]<int>;
+ using first = TT...[0]<int>;
+};
+
+// CHECK: template <unsigned int N, template <class> class ...TT> struct S {
+// CHECK-NEXT: using type = TT...[N]<int>;
+// CHECK-NEXT: using first = TT...[0]<int>;
+
+template <template <class> class... TT>
+TT...[0]<int> f(TT...[1]<int>);
+
+// CHECK: template <template <class> class ...TT> TT...[0]<int> f(TT...[1]<int>);
+
+template <template <class> concept... CC>
+struct Constrained {
+ template <CC...[0] T>
+ static void f();
+ static void g(CC...[1] auto);
+};
+
+// CHECK: template <template <class> concept ...CC> struct Constrained {
+// CHECK-NEXT: template <CC...[0] T> static void f();
+// CHECK-NEXT: static void g(CC...[1] auto);
+
+template <template <class> concept... CC>
+constexpr bool concept_id = CC...[0]<int>;
+
+// CHECK: template <template <class> concept ...CC> constexpr bool concept_id = CC...[0]<int>;
+
+template <template <class> auto... VV>
+constexpr int variable_template_id = VV...[1]<int>;
+
+// CHECK: template <template <class> auto ...VV> constexpr int variable_template_id = VV...[1]<int>;
+
+template <template <class> concept... CC>
+void requires_clause() requires CC...[0]<int>;
+
+// CHECK: template <template <class> concept ...CC> void requires_clause() requires CC...[0]<int>;
diff --git a/clang/test/ExtractAPI/pack_indexing_concept.cpp b/clang/test/ExtractAPI/pack_indexing_concept.cpp
new file mode 100644
index 0000000000000..38443929937f9
--- /dev/null
+++ b/clang/test/ExtractAPI/pack_indexing_concept.cpp
@@ -0,0 +1,33 @@
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -std=c++2d -extract-api --pretty-sgf --emit-sgf-symbol-labels-for-testing \
+// RUN: --product-name=PackIndexing -triple arm64-apple-macosx -x c++-header %s \
+// RUN: -o %t/pack-indexing.symbols.json -verify
+
+// RUN: FileCheck %s --input-file %t/pack-indexing.symbols.json --check-prefix FUNCTION
+
+template <template <class> concept... CC, CC...[0] T>
+void function(T);
+
+// expected-no-diagnostics
+
+//FUNCTION-LABEL: "!testLabel": "c:@FT@>2#pt>1#T#Tfunction#t0.1#v#"
+//FUNCTION: "declarationFragments": [
+//FUNCTION: "kind": "genericParameter",
+//FUNCTION-NEXT: "spelling": "CC"
+//FUNCTION-NEXT: },
+//FUNCTION-NEXT: {
+//FUNCTION-NEXT: "kind": "text",
+//FUNCTION-NEXT: "spelling": ", "
+//FUNCTION-NEXT: },
+//FUNCTION-NEXT: {
+//FUNCTION-NEXT: "kind": "typeIdentifier",
+//FUNCTION-NEXT: "spelling": "CC...[0]"
+//FUNCTION-NEXT: },
+//FUNCTION-NEXT: {
+//FUNCTION-NEXT: "kind": "text",
+//FUNCTION-NEXT: "spelling": " "
+//FUNCTION-NEXT: },
+//FUNCTION-NEXT: {
+//FUNCTION-NEXT: "kind": "genericParameter",
+//FUNCTION-NEXT: "spelling": "T"
+//FUNCTION-NEXT: },
diff --git a/clang/test/Lexer/cxx-features.cpp b/clang/test/Lexer/cxx-features.cpp
index b505f18dff14f..9845bea38d25b 100644
--- a/clang/test/Lexer/cxx-features.cpp
+++ b/clang/test/Lexer/cxx-features.cpp
@@ -46,7 +46,7 @@
#error "wrong value for __cpp_deleted_function"
#endif
-#if check(pack_indexing, 202311, 202311, 202311, 202311, 202311, 202311, 202311)
+#if check(pack_indexing, 202606, 202606, 202606, 202606, 202606, 202606, 202606)
#error "wrong value for __cpp_pack_indexing"
#endif
diff --git a/clang/test/Modules/pack-indexing-template.cppm b/clang/test/Modules/pack-indexing-template.cppm
new file mode 100644
index 0000000000000..c752cd9176c77
--- /dev/null
+++ b/clang/test/Modules/pack-indexing-template.cppm
@@ -0,0 +1,73 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++2d -triple %itanium_abi_triple -emit-module-interface %t/a.cppm -o %t/a.pcm
+// RUN: %clang_cc1 -std=c++2d -triple %itanium_abi_triple -fmodule-file=a=%t/a.pcm -emit-llvm -o - %t/b.cpp | FileCheck %s
+// RUN: %clang_cc1 -std=c++2d -triple %itanium_abi_triple -fmodule-file=a=%t/a.pcm -fsyntax-only -verify %t/c.cpp
+
+//--- a.cppm
+export module a;
+
+template <class T> struct Wrapper {
+ using type = T;
+ T value;
+};
+template <class T> struct Other {};
+
+export template <unsigned I, template <class> class... TT>
+using Indexed = TT...[I]<int>;
+
+export template <template <class> class... TT>
+struct Holder {
+ using first = TT...[0]<int>;
+ typename TT...[0]<int>::type value;
+};
+
+export template <class T> concept Always = true;
+export template <class T> concept Never = false;
+export template <class T> constexpr int Var = 1;
+export template <class T> constexpr int Var2 = 2;
+
+export template <unsigned I, template <class> concept... CC>
+constexpr bool ConceptId = CC...[I]<int>;
+
+export template <unsigned I, template <class> auto... VV>
+constexpr int VariableTemplateId = VV...[I]<int>;
+
+export template <template <class> concept... CC>
+struct Constrained {
+ template <CC...[0] T>
+ static constexpr int f() { return 3; }
+ static constexpr int g(CC...[0] auto) { return 4; }
+};
+
+export Indexed<0, Wrapper, Other> a = {42};
+
+//--- b.cpp
+import a;
+
+int b() {
+ return a.value;
+}
+
+// CHECK: @_ZW1a1a = external global %struct.Wrapper
+// CHECK: define {{.*}}i32 @_Z1bv()
+
+//--- c.cpp
+// expected-no-diagnostics
+import a;
+
+template <class T> struct Local {
+ using type = T;
+};
+template <class T> struct Unused {};
+
+static_assert(__is_same(Indexed<1, Local, Unused>, Unused<int>));
+static_assert(__is_same(Holder<Local>::first, Local<int>));
+static_assert(__is_same(decltype(Holder<Local>::value), int));
+
+static_assert(ConceptId<0, Always, Never>);
+static_assert(!ConceptId<1, Always, Never>);
+static_assert(VariableTemplateId<1, Var, Var2> == 2);
+static_assert(Constrained<Always>::f<int>() == 3);
+static_assert(Constrained<Always>::g(0) == 4);
diff --git a/clang/test/PCH/pack-indexing-template.cpp b/clang/test/PCH/pack-indexing-template.cpp
new file mode 100644
index 0000000000000..fcb295a2b9ba4
--- /dev/null
+++ b/clang/test/PCH/pack-indexing-template.cpp
@@ -0,0 +1,67 @@
+// RUN: %clang_cc1 -std=c++2d -x c++-header %s -emit-pch -o %t.pch
+// RUN: %clang_cc1 -std=c++2d -x c++ /dev/null -include-pch %t.pch
+
+// RUN: %clang_cc1 -std=c++2d -x c++-header %s -emit-pch -fpch-instantiate-templates -o %t.pch
+// RUN: %clang_cc1 -std=c++2d -x c++ /dev/null -include-pch %t.pch
+
+template <class T> struct A {
+ using type = T;
+};
+template <class T> struct B {};
+template <template <class> class> struct Take {};
+
+template <unsigned I, template <class> class... TT>
+using Indexed = TT...[I]<int>;
+
+template <unsigned I, template <class> class... TT>
+using Nested = typename TT...[I]<int>::type;
+
+template <unsigned I, template <class> class... TT>
+using AsArgument = Take<TT...[I]>;
+
+template <template <class> class... TT>
+struct Base : TT...[0]<int> {};
+
+template <class T> struct Deduce {
+ Deduce(T);
+};
+template <template <class> class... TT>
+auto ctad() {
+ TT...[0] x{0};
+ return x;
+}
+
+template <class T> concept Always = true;
+template <class T> concept Never = false;
+template <class T> constexpr int Var = 1;
+template <class T> constexpr int Var2 = 2;
+
+template <unsigned I, template <class> concept... CC>
+constexpr bool ConceptId = CC...[I]<int>;
+
+template <unsigned I, template <class> auto... VV>
+constexpr int VariableTemplateId = VV...[I]<int>;
+
+template <template <class> concept... CC>
+struct Constrained {
+ template <CC...[0] T>
+ static constexpr int f() { return 3; }
+ static constexpr int g(CC...[0] auto) { return 4; }
+};
+
+template <template <class> concept... CC>
+constexpr int Requires() requires CC...[0]<int> { return 5; }
+
+void fn() {
+ static_assert(__is_same(Indexed<1, A, B>, B<int>));
+ static_assert(__is_same(Nested<0, A, B>, int));
+ static_assert(__is_same(AsArgument<1, A, B>, Take<B>));
+ static_assert(__is_base_of(A<int>, Base<A, B>));
+ static_assert(__is_same(decltype(ctad<Deduce>()), Deduce<int>));
+ static_assert(ConceptId<0, Always, Never>);
+ static_assert(!ConceptId<1, Always, Never>);
+ static_assert(VariableTemplateId<1, Var, Var2> == 2);
+ static_assert(Constrained<Always>::f<int>() == 3);
+ static_assert(Constrained<Always>::g(0) == 4);
+ static_assert(Requires<Always>() == 5);
+}
diff --git a/clang/test/Parser/cxx2d-pack-indexing-template.cpp b/clang/test/Parser/cxx2d-pack-indexing-template.cpp
new file mode 100644
index 0000000000000..b62cecf361451
--- /dev/null
+++ b/clang/test/Parser/cxx2d-pack-indexing-template.cpp
@@ -0,0 +1,69 @@
+// RUN: %clang_cc1 -std=c++2d -verify -fsyntax-only %s
+
+template <class> struct A {};
+template <class> struct B {};
+
+template <template <class> class... TT> // expected-note {{template is declared here}}
+struct Malformed {
+ using a = TT...[<int>; // expected-error 2{{expected expression}} \
+ // expected-error {{expected ']'}} \
+ // expected-note {{to match this '['}} \
+ // expected-error {{expected '(' for function-style cast or type construction}}
+
+ using b = TT...[0<int>; // expected-error {{expected ']'}} \
+ // expected-note {{to match this '['}} \
+ // expected-error {{expected expression}} \
+ // expected-error {{expected '(' for function-style cast or type construction}}
+
+ using c = TT...[]<int>; // expected-error {{use of template template parameter 'TT' requires template arguments}} \
+ // expected-error {{expected ';' after alias declaration}}
+};
+
+template <template <class> class... TT>
+struct S {
+ using type = TT...[0]<int>;
+};
+
+template <class T> struct WithMember { using type = T; };
+
+template <template <class> class... TT>
+using U = typename TT...[0]<int>::type;
+
+namespace ns { }
+template <template <class> class... TT>
+using Q = ns::TT...[0]<int>; // expected-error {{no type named 'TT' in namespace 'ns'}} \
+ // expected-error {{expected ';' after alias declaration}}
+
+template <class T> concept Concept = true;
+template <class T> constexpr int Var = 1;
+
+template <template <class> concept... CC>
+constexpr bool concept_id() {
+ return CC...[0]<int>;
+}
+
+template <template <class> auto... VV>
+constexpr int variable_template_id() {
+ return VV...[0]<int>;
+}
+template <template <class> concept... CC>
+struct TypeConstraint {
+ template <CC...[0] T>
+ static constexpr int f() { return 2; }
+};
+template <template <class> concept... CC>
+struct MalformedConcept {
+ template <CC...[] T> // expected-error {{expected identifier}}
+ static void f(); // expected-error {{no candidate function template was found for dependent member function template specialization}} \
+ // expected-warning {{explicit specialization cannot have a storage class}}
+};
+
+namespace not_a_template {
+template <class... T>
+using U = T...[0];
+static_assert(__is_same(U<int, long>, int));
+
+template <auto... V>
+constexpr auto W = V...[1];
+static_assert(W<1, 2, 3> == 2);
+}
diff --git a/clang/test/SemaCXX/cxx2d-pack-indexing-template-ext-diags.cpp b/clang/test/SemaCXX/cxx2d-pack-indexing-template-ext-diags.cpp
new file mode 100644
index 0000000000000..c29610c010533
--- /dev/null
+++ b/clang/test/SemaCXX/cxx2d-pack-indexing-template-ext-diags.cpp
@@ -0,0 +1,54 @@
+// RUN: %clang_cc1 -std=c++2d -verify=cxx29 -fsyntax-only -Wpre-c++2d-compat %s
+// RUN: %clang_cc1 -std=c++11 -verify=ext -fsyntax-only -Wc++2d-extensions %s
+// RUN: %clang_cc1 -std=c++98 -verify=ext -fsyntax-only -Wc++2d-extensions -Wno-c++11-extensions %s
+
+template <class T> struct A {};
+template <class T> struct B {};
+
+template <template <class> class... TT>
+struct S {
+ // cxx29-warning at +2 {{pack indexing for template names is incompatible with C++ standards before C++2d}}
+ // ext-warning at +1 {{pack indexing for template names is a C++2d extension}}
+ typedef TT...[0]<int> a;
+};
+
+#if __cplusplus > 202302L
+template <class T> concept Concept = true;
+
+template <template <class> concept... CC>
+struct Concepts {
+ // cxx29-warning at +1 {{pack indexing for template names is incompatible with C++ standards before C++2d}}
+ template <CC...[0] T>
+ static void a();
+};
+#endif
+
+template <class... T>
+struct Types {
+ // ext-warning at +1 {{pack indexing is a C++2c extension}}
+ typedef T...[0] a;
+};
+
+template <class T> struct Deduce { Deduce(T); };
+
+// ext-note at +1 {{template is declared here}}
+template <template <class> class... TT>
+void deduced_class_type() {
+ // cxx29-warning at +3 {{pack indexing for template names is incompatible with C++ standards before C++2d}}
+ // ext-warning at +2 {{pack indexing for template names is a C++2d extension}}
+ // ext-error at +1 {{too few template arguments for template template parameter 'TT'}}
+ TT...[0] x = 1;
+ (void)x;
+}
+
+void use() {
+ S<A, B> s;
+ (void)s;
+ Types<int, long> t;
+ (void)t;
+ deduced_class_type<Deduce>();
+#if __cplusplus > 202302L
+ Concepts<Concept> c;
+ (void)c;
+#endif
+}
diff --git a/clang/test/SemaCXX/cxx2d-pack-indexing-template.cpp b/clang/test/SemaCXX/cxx2d-pack-indexing-template.cpp
new file mode 100644
index 0000000000000..38be1ae5c7894
--- /dev/null
+++ b/clang/test/SemaCXX/cxx2d-pack-indexing-template.cpp
@@ -0,0 +1,400 @@
+// RUN: %clang_cc1 -std=c++2d -verify -fsyntax-only %s
+
+template <class T> struct A { using type = T; };
+template <class T> struct B { using type = T *; };
+template <class...> struct List {};
+
+namespace not_a_pack {
+template <template <class> class TT>
+using U = TT...[0]<int>; // expected-error {{'TT' does not refer to the name of a parameter pack}}
+
+using V = A...[0]<int>; // expected-error {{'A' does not refer to the name of a parameter pack}}
+
+template <template <class> concept CC>
+constexpr bool W = CC...[0]<int>; // expected-error {{'CC' does not refer to the name of a parameter pack}}
+
+template <template <class> auto VV>
+constexpr int X = VV...[0]<int>; // expected-error {{'VV' does not refer to the name of a parameter pack}}
+}
+
+namespace index {
+template <template <class> class... TT>
+struct S {
+ using ok = TT...[sizeof...(TT) - 1]<int>;
+};
+static_assert(__is_same(S<A, B>::ok, B<int>));
+
+template <template <class> class... TT>
+using OutOfBounds = TT...[2]<int>; // expected-error {{invalid index 2 for pack 'TT' of size 2}}
+using E1 = OutOfBounds<A, B>; // expected-note {{in instantiation of template type alias 'OutOfBounds' requested here}}
+
+template <template <class> class... TT>
+using Negative = TT...[-1]<int>;
+// expected-error at -1 {{pack index evaluates to -1, which cannot be narrowed to type '__size_t'}}
+// expected-error at -2 {{expected ';' after alias declaration}}
+
+template <template <class> class... TT>
+using Narrowing = TT...[1.0]<int>;
+// expected-error at -1 {{conversion from 'double' to '__size_t' (aka 'unsigned long') is not allowed in a converted constant expression}}
+// expected-error at -2 {{expected ';' after alias declaration}}
+
+template <template <class> class... TT>
+using NonConstant = TT...[x]<int>;
+// expected-error at -1 {{use of undeclared identifier 'x'}}
+// expected-error at -2 {{expected ';' after alias declaration}}
+}
+
+namespace equivalence {
+template <template <class> class... TT>
+void same(TT...[0]<int>);
+template <template <class> class... TT>
+void same(TT...[0]<int>);
+
+template <unsigned N, template <class> class... TT>
+void dependent(TT...[N]<int>);
+template <unsigned N, template <class> class... TT>
+void dependent(TT...[N]<int>);
+
+template <template <class> class... TT>
+void different(TT...[0]<int>);
+template <template <class> class... TT>
+void different(TT...[1]<int>);
+
+void call() {
+ same<A, B>(A<int>{});
+ dependent<1, A, B>(B<int>{});
+ different<A, B>(A<int>{});
+ different<A, B>(B<int>{});
+}
+}
+
+namespace uses {
+template <class T> struct WithNested {
+ using type = T;
+ static constexpr int value = 1;
+};
+
+template <template <class> class... TT>
+struct Everywhere : TT...[0]<int> {
+ using alias = TT...[0]<int>;
+ template <class T> using tmpl_alias = TT...[0]<T>;
+ using nested = typename TT...[0]<int>::type;
+ static constexpr int v = TT...[0]<int>::value;
+ TT...[0]<int> member;
+ TT...[0]<int> fn(TT...[0]<int>);
+};
+
+static_assert(__is_base_of(WithNested<int>, Everywhere<WithNested>));
+static_assert(__is_same(Everywhere<WithNested>::alias, WithNested<int>));
+static_assert(__is_same(Everywhere<WithNested>::tmpl_alias<char>, WithNested<char>));
+static_assert(__is_same(Everywhere<WithNested>::nested, int));
+static_assert(Everywhere<WithNested>::v == 1);
+
+template <template <class> class> struct Take {};
+template <template <class> class... TT>
+using Arg = Take<TT...[1]>;
+static_assert(__is_same(Arg<A, B>, Take<B>));
+
+template <class T> struct Deduce {
+ Deduce(T);
+};
+template <template <class> class... TT>
+auto ctad() {
+ TT...[0] x{42};
+ return x;
+}
+static_assert(__is_same(decltype(ctad<Deduce>()), Deduce<int>));
+}
+
+namespace deduction_guides {
+template <class T> struct C { C(T); };
+
+template <template <class> class... TT>
+TT...[0](int) -> TT...[0]<int>;
+// expected-error at -1 {{expected unqualified-id}}
+// expected-error at -2 {{expected ')'}}
+// expected-note at -3 {{to match this '('}}
+
+template <template <class> class... TT>
+C(int) -> TT...[0]<int>;
+// expected-error at -1 {{deduced type 'TT...[0]<int>' of deduction guide is not written as a specialization of template 'C'}}
+}
+
+namespace pack_expansion {
+template <template <class> class... TT>
+struct S {
+ using one = TT...[0]<int>;
+ template <unsigned... Is>
+ using many = List<TT...[Is]<int>...>;
+};
+static_assert(__is_same(S<A, B>::many<1, 0>, List<B<int>, A<int>>));
+
+template <template <class> class... TT>
+struct Bad {
+ template <unsigned... Is>
+ using type = List<TT...[Is]<int>>; // expected-error {{declaration type contains unexpanded parameter pack 'Is'}}
+};
+}
+
+namespace partial_substitution {
+template <class T> struct Outer {
+ template <template <class> class... TT>
+ using inner = TT...[0]<T>;
+};
+static_assert(__is_same(Outer<int>::inner<A, B>, A<int>));
+
+template <template <class> class... TT>
+struct Nested {
+ template <class... Ts>
+ using apply = List<TT...[0]<Ts>...>;
+};
+static_assert(__is_same(Nested<A>::apply<int, long>, List<A<int>, A<long>>));
+}
+
+namespace empty_pack {
+template <template <class> class... TT>
+using U = TT...[0]<int>; // expected-error {{invalid index 0 for pack 'TT' of size 0}}
+using E = U<>; // expected-note {{in instantiation of template type alias 'U' requested here}}
+}
+
+namespace kinds {
+template <class T> constexpr int Var = 1;
+template <class T> constexpr int Var2 = 2;
+template <class T> concept Always = true;
+template <class T> concept Never = false;
+template <class T, class U> concept Same = __is_same(T, U);
+
+template <template <class> auto V1> struct TakeVar {};
+template <template <class> concept C1> struct TakeConcept {};
+
+template <template <class> auto... VV>
+using AV = TakeVar<VV...[0]>;
+static_assert(__is_same(AV<Var>, TakeVar<Var>));
+
+template <template <class> concept... CC>
+using AC = TakeConcept<CC...[0]>;
+static_assert(__is_same(AC<Always>, TakeConcept<Always>));
+
+template <template <class> auto... VV>
+constexpr int use_var() {
+ return VV...[1]<int>;
+}
+static_assert(use_var<Var, Var2>() == 2);
+static_assert(use_var<Var2, Var>() == 1);
+
+template <template <class> concept... CC>
+constexpr bool use_concept() {
+ return CC...[0]<int>;
+}
+static_assert(use_concept<Always, Never>());
+static_assert(!use_concept<Never, Always>());
+
+template <template <class> concept... CC>
+struct Constrained {
+ template <CC...[0] T>
+ static constexpr int f() { return 1; }
+};
+static_assert(Constrained<Always, Never>::f<int>() == 1);
+
+template <template <class, class> concept... CC>
+struct ConstrainedWithArgs {
+ template <CC...[0]<int> T>
+ static constexpr int f() { return 2; }
+};
+static_assert(ConstrainedWithArgs<Same>::f<int>() == 2);
+
+template <template <class> concept... CC, CC...[0] T>
+constexpr int in_same_list(T) { return 3; }
+static_assert(in_same_list<Always>(0) == 3);
+
+template <template <class> concept... CC>
+constexpr int with_auto(CC...[0] auto x) { return 4; }
+static_assert(with_auto<Always>(0) == 4);
+
+template <template <class> concept... CC>
+constexpr int with_auto_var() {
+ CC...[0] auto x = 5;
+ return x;
+}
+static_assert(with_auto_var<Always>() == 5);
+
+template <template <class> concept... CC>
+constexpr auto with_auto_return() -> CC...[0] auto { return 6; }
+static_assert(with_auto_return<Always>() == 6);
+
+template <template <class> concept... CC>
+constexpr int with_requires() requires CC...[0]<int> { return 7; }
+static_assert(with_requires<Always>() == 7);
+
+template <unsigned N, template <class> auto... VV>
+constexpr int DependentVar = VV...[N]<int>;
+static_assert(DependentVar<0, Var, Var2> == 1);
+static_assert(DependentVar<1, Var, Var2> == 2);
+
+template <unsigned N, template <class> concept... CC>
+constexpr bool DependentConcept = CC...[N]<int>;
+static_assert(DependentConcept<0, Always, Never>);
+static_assert(!DependentConcept<1, Always, Never>);
+
+template <template <class> concept... CC>
+struct Expansion {
+ template <unsigned... Is>
+ static constexpr bool all() { return (CC...[Is]<int> && ...); }
+};
+static_assert(Expansion<Always, Always>::all<0, 1>());
+static_assert(!Expansion<Always, Never>::all<0, 1>());
+
+template <template <class> concept... CC>
+constexpr bool GH218035 = ((CC...[0]<int> == CC<int>) && ...);
+static_assert(GH218035<Always, Always>);
+static_assert(!GH218035<Always, Never>);
+static_assert(!GH218035<Never, Always>);
+
+template <template <class> concept... CC>
+struct GH218548 {
+ template <class... Ts>
+ static constexpr bool f() { return (CC...[sizeof(Ts) - 1]<int> && ...); }
+};
+static_assert(GH218548<Never, Always>::f<short>());
+static_assert(!GH218548<Never, Always>::f<char>());
+static_assert(!GH218548<Never, Always>::f<char, short>());
+}
+
+namespace kind_errors {
+template <class T> constexpr int Var = 1;
+template <class T> constexpr int Var2 = 2;
+template <class T> concept Always = true;
+template <class T> concept Big = sizeof(T) > 1;
+// expected-note at -1 2{{because 'sizeof(char) > 1' (1 > 1) evaluated to false}}
+
+template <template <class> concept... CC>
+void constrained_auto(CC...[1] auto x);
+// expected-note at -1 {{candidate template ignored: constraints not satisfied}}
+// expected-note at -2 {{because 'char' does not satisfy 'Big'}}
+
+void use_constrained_auto() {
+ constrained_auto<Always, Big>('c'); // expected-error {{no matching function for call to 'constrained_auto'}}
+ constrained_auto<Big, Always>('c');
+}
+
+template <template <class> concept... CC>
+struct Constrained {
+ template <CC...[0] T> // expected-note {{because 'char' does not satisfy 'Big'}}
+ static void f(); // expected-note {{candidate template ignored: constraints not satisfied [with T = char]}}
+};
+void use_constrained() {
+ Constrained<Big>::f<char>(); // expected-error {{no matching function for call to 'f'}}
+ Constrained<Big>::f<int>();
+}
+
+template <template <class> auto... VV>
+constexpr int VarOutOfBounds = VV...[2]<int>; // expected-error {{invalid index 2 for pack 'VV' of size 2}}
+constexpr int E1 = VarOutOfBounds<Var, Var2>; // expected-note {{in instantiation of variable template specialization 'kind_errors::VarOutOfBounds<kind_errors::Var, kind_errors::Var2>' requested here}}
+
+template <template <class> concept... CC>
+constexpr bool ConceptOutOfBounds = CC...[1]<int>; // expected-error {{invalid index 1 for pack 'CC' of size 1}}
+constexpr bool E2 = ConceptOutOfBounds<Always>; // expected-note {{in instantiation of variable template specialization 'kind_errors::ConceptOutOfBounds<kind_errors::Always>' requested here}}
+
+template <template <class> concept... CC>
+struct ConstraintOutOfBounds {
+ // expected-note at +1 {{because substituted constraint expression is ill-formed: invalid index 1 for pack 'CC' of size 1}}
+ template <CC...[1] T>
+ static void f(); // expected-note {{candidate template ignored: constraints not satisfied [with T = int]}}
+};
+void use_out_of_bounds() {
+ ConstraintOutOfBounds<Always>::f<int>(); // expected-error {{no matching function for call to 'f'}}
+}
+
+template <auto> concept NonType = true;
+template <template <auto> concept... CC>
+struct NotATypeConcept {
+ template <CC...[0] T> // expected-error {{concept named in type constraint is not a type concept}}
+ static void f();
+};
+}
+
+namespace sfinae {
+template <class T> struct HasType {
+ using type = T;
+};
+template <class T> struct NoType {};
+
+template <template <class> class... TT>
+constexpr int f(typename TT...[0]<int>::type *) { return 1; }
+template <template <class> class... TT>
+constexpr int f(...) { return 2; }
+
+static_assert(f<HasType>(nullptr) == 1);
+static_assert(f<NoType>(nullptr) == 2);
+}
+
+namespace equivalence {
+template <class T> concept Always = true;
+
+template <template <class> concept... CC>
+void same(CC...[0] auto x) {} // expected-note {{previous definition is here}}
+template <template <class> concept... CC>
+void same(CC...[0] auto x) {} // expected-error {{redefinition of 'same'}}
+
+template <template <class> concept... CC>
+void different(CC...[0] auto x) {}
+template <template <class> concept... CC>
+void different(CC...[1] auto x) {}
+
+template <template <class> concept... CC>
+constexpr bool same_id = CC...[0]<int>; // expected-note {{previous definition is here}}
+template <template <class> concept... CC>
+constexpr bool same_id = CC...[0]<int>; // expected-error {{redefinition of 'same_id'}}
+
+template <template <class> concept... CC>
+constexpr bool different_id_a = CC...[0]<int>;
+template <template <class> concept... CC>
+constexpr bool different_id_b = CC...[1]<int>;
+
+template <class T> concept Never = false;
+
+template <template <class> concept... CC, class T> requires CC...[0]<T>
+constexpr int same_constraint(T) { return 1; } // expected-note {{previous definition is here}}
+template <template <class> concept... CC, class T> requires CC...[0]<T>
+constexpr int same_constraint(T) { return 2; } // expected-error {{redefinition of 'same_constraint'}}
+
+template <template <class> concept... CC, class T> requires CC...[0]<T>
+constexpr int different_constraint(T) { return 1; }
+template <template <class> concept... CC, class T> requires CC...[1]<T>
+constexpr int different_constraint(T) { return 2; }
+static_assert(different_constraint<Always, Never>(0) == 1);
+static_assert(different_constraint<Never, Always>(0) == 2);
+}
+
+namespace atomic_constraints {
+template <class T> concept Always = true;
+
+template <template <class> concept... CC>
+struct S {
+ template <class T> requires CC...[0]<T>
+ // expected-note at -1 {{similar constraint expression here}}
+ static constexpr int f() { return 1; } // expected-note {{candidate function [with T = int]}}
+ template <class T> requires CC...[0]<T> && Always<T>
+ // expected-note at -1 {{similar constraint expressions not considered equivalent; constraint expressions cannot be considered equivalent unless they originate from the same concept}}
+ static constexpr int f() { return 2; } // expected-note {{candidate function [with T = int]}}
+};
+constexpr int a = S<Always>::f<int>(); // expected-error {{call to 'f' is ambiguous}}
+
+template <template <class> concept... CC>
+struct T {
+ template <class T> requires CC...[0]<T>
+ static constexpr int f() { return 1; } // expected-note {{candidate function [with T = int]}}
+ template <class T> requires Always<T>
+ static constexpr int f() { return 2; } // expected-note {{candidate function [with T = int]}}
+};
+constexpr int b = T<Always>::f<int>(); // expected-error {{call to 'f' is ambiguous}}
+
+template <template <class> concept... CC>
+struct U {
+ template <class T> requires CC...[0]<T>
+ static constexpr int f() { return 1; } // expected-note {{candidate function [with T = int]}}
+ template <class T> requires CC...[1]<T>
+ static constexpr int f() { return 2; } // expected-note {{candidate function [with T = int]}}
+};
+constexpr int c = U<Always, Always>::f<int>(); // expected-error {{call to 'f' is ambiguous}}
+}
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index 2c5e799d12f18..a165924bebd2d 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -1503,6 +1503,10 @@ bool CursorVisitor::VisitTemplateName(TemplateName Name, SourceLocation NameLoc,
Name.getAsSubstTemplateTemplateParmPack()->getParameterPack(), NameLoc,
TU));
+ case TemplateName::PackIndexingTemplate:
+ return VisitTemplateName(Name.getAsPackIndexingTemplate()->getPattern(),
+ NameLoc, NNS);
+
case TemplateName::DeducedTemplate:
llvm_unreachable("DeducedTemplate shouldn't appear in source");
}
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index 16ebeb1052b79..4baaac74a0758 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -187,7 +187,11 @@ <h2 id="cxx29">C++2d implementation status</h2>
<tr>
<td>Pack indexing for template names</td>
<td><a href="https://wg21.link/P3670R4">P3670R4</a></td>
- <td class="none" align="center">No</td>
+ <td class="partial" align="center">
+ <details><summary>Clang 24 (Partial)</summary>
+ The mangling of a <i>pack-index-template-name</i> is not implemented.
+ </details>
+ </td>
</tr>
<tr>
<td><code>#embed</code> offset parameter</td>
>From 9ecb5bab23e014b5521240f28ce7b6ac3d3c1f87 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Wed, 26 Aug 2026 11:24:25 +0200
Subject: [PATCH 2/4] Address Eli and Younan's feedback
- Add tests with multiple expansions
- remove weird flag in run line
- remove feature test macro update
- remove unused headers
---
.../clangd/unittests/DumpASTTests.cpp | 8 ++++----
clang/lib/AST/TemplateName.cpp | 4 ----
clang/lib/Frontend/InitPreprocessor.cpp | 2 +-
clang/lib/Sema/SemaTemplate.cpp | 2 +-
.../test/ExtractAPI/pack_indexing_concept.cpp | 3 +--
clang/test/Lexer/cxx-features.cpp | 2 +-
.../SemaCXX/cxx2d-pack-indexing-template.cpp | 19 ++++++++++++++++++-
7 files changed, 26 insertions(+), 14 deletions(-)
diff --git a/clang-tools-extra/clangd/unittests/DumpASTTests.cpp b/clang-tools-extra/clangd/unittests/DumpASTTests.cpp
index bfdb156ceb6cb..f116fe56b90cd 100644
--- a/clang-tools-extra/clangd/unittests/DumpASTTests.cpp
+++ b/clang-tools-extra/clangd/unittests/DumpASTTests.cpp
@@ -10,6 +10,7 @@
#include "DumpAST.h"
#include "TestTU.h"
#include "clang/AST/ASTTypeTraits.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/ScopedPrinter.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
@@ -230,10 +231,9 @@ TEST(DumpASTTests, UnbalancedBraces) {
bool hasDetail(const ASTNode &Node, llvm::StringRef Detail) {
if (Node.detail == Detail)
return true;
- for (const ASTNode &Child : Node.children)
- if (hasDetail(Child, Detail))
- return true;
- return false;
+ return llvm::any_of(Node.children, [&Detail](const ASTNode &Child) {
+ return hasDetail(Child, Detail);
+ });
}
TEST(DumpASTTests, PackIndexedConcept) {
diff --git a/clang/lib/AST/TemplateName.cpp b/clang/lib/AST/TemplateName.cpp
index 910c5a761dd77..12c342652c851 100644
--- a/clang/lib/AST/TemplateName.cpp
+++ b/clang/lib/AST/TemplateName.cpp
@@ -16,8 +16,6 @@
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/DependenceFlags.h"
-#include "clang/AST/Expr.h"
-#include "clang/AST/NestedNameSpecifier.h"
#include "clang/AST/PrettyPrinter.h"
#include "clang/AST/TemplateBase.h"
#include "clang/Basic/Diagnostic.h"
@@ -26,8 +24,6 @@
#include "clang/Basic/OperatorKinds.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/FoldingSet.h"
-#include "llvm/ADT/STLExtras.h"
-#include "llvm/Support/Compiler.h"
#include "llvm/Support/raw_ostream.h"
#include <cassert>
#include <optional>
diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp
index f37d95cd02068..7cc481b595f36 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -761,7 +761,7 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
Builder.defineMacro("__cpp_placeholder_variables", "202306L");
// C++26 features supported in earlier language modes.
- Builder.defineMacro("__cpp_pack_indexing", "202606L");
+ Builder.defineMacro("__cpp_pack_indexing", "202311L");
Builder.defineMacro("__cpp_deleted_function", "202403L");
Builder.defineMacro("__cpp_variadic_friend", "202403L");
Builder.defineMacro("__cpp_trivial_relocatability", "202502L");
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 56d0c5a4d2fe9..eb93a5e03b06f 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -9322,7 +9322,7 @@ void Sema::CheckConceptRedefinition(ConceptDecl *NewDecl,
}
bool Sema::CheckConceptUseInDefinition(NamedDecl *Concept, SourceLocation Loc) {
- if (auto *CE = llvm::dyn_cast_if_present<ConceptDecl>(Concept);
+ if (auto *CE = llvm::dyn_cast<ConceptDecl>(Concept);
CE && !CE->isInvalidDecl() && !CE->hasDefinition()) {
Diag(Loc, diag::err_recursive_concept) << CE;
Diag(CE->getLocation(), diag::note_declared_at);
diff --git a/clang/test/ExtractAPI/pack_indexing_concept.cpp b/clang/test/ExtractAPI/pack_indexing_concept.cpp
index 38443929937f9..b0575fc908275 100644
--- a/clang/test/ExtractAPI/pack_indexing_concept.cpp
+++ b/clang/test/ExtractAPI/pack_indexing_concept.cpp
@@ -1,7 +1,6 @@
// RUN: rm -rf %t
// RUN: %clang_cc1 -std=c++2d -extract-api --pretty-sgf --emit-sgf-symbol-labels-for-testing \
-// RUN: --product-name=PackIndexing -triple arm64-apple-macosx -x c++-header %s \
-// RUN: -o %t/pack-indexing.symbols.json -verify
+// RUN: -triple arm64-apple-macosx -x c++-header %s -o %t/pack-indexing.symbols.json -verify
// RUN: FileCheck %s --input-file %t/pack-indexing.symbols.json --check-prefix FUNCTION
diff --git a/clang/test/Lexer/cxx-features.cpp b/clang/test/Lexer/cxx-features.cpp
index 9845bea38d25b..b505f18dff14f 100644
--- a/clang/test/Lexer/cxx-features.cpp
+++ b/clang/test/Lexer/cxx-features.cpp
@@ -46,7 +46,7 @@
#error "wrong value for __cpp_deleted_function"
#endif
-#if check(pack_indexing, 202606, 202606, 202606, 202606, 202606, 202606, 202606)
+#if check(pack_indexing, 202311, 202311, 202311, 202311, 202311, 202311, 202311)
#error "wrong value for __cpp_pack_indexing"
#endif
diff --git a/clang/test/SemaCXX/cxx2d-pack-indexing-template.cpp b/clang/test/SemaCXX/cxx2d-pack-indexing-template.cpp
index 38be1ae5c7894..396fa66f7693c 100644
--- a/clang/test/SemaCXX/cxx2d-pack-indexing-template.cpp
+++ b/clang/test/SemaCXX/cxx2d-pack-indexing-template.cpp
@@ -1,4 +1,5 @@
-// RUN: %clang_cc1 -std=c++2d -verify -fsyntax-only %s
+// RUN: %clang_cc1 -std=c++2c -verify -fsyntax-only -triple x86_64-unknown-linux-gnu -Wno-c++2d-extensions %s
+// RUN: %clang_cc1 -std=c++2d -verify -fsyntax-only -triple x86_64-unknown-linux-gnu %s
template <class T> struct A { using type = T; };
template <class T> struct B { using type = T *; };
@@ -260,6 +261,22 @@ static_assert(!GH218548<Never, Always>::f<char>());
static_assert(!GH218548<Never, Always>::f<char, short>());
}
+template <template <typename> concept... C>
+void multiple_expansions() requires (C...[C<int>]<long long> && ...); // #multiple_expansions1
+
+template <template <typename> concept... C>
+void multiple_expansions_ok() requires (C...[C<long long>]<long long> || ...);
+template<typename T> concept X = sizeof(T) == 4; // #multiple_expansions2
+template<typename T> concept Y = sizeof(T) >= 4;
+void multiple_expansions_test() {
+ multiple_expansions<Y, X>();
+// expected-error at -1 {{no matching function for call to 'multiple_expansions'}}
+// expected-note@#multiple_expansions1 {{candidate template ignored: constraints not satisfied [with C = <Y, X>]}}
+// expected-note@#multiple_expansions1 {{because 'long long' does not satisfy 'X'}}
+// expected-note@#multiple_expansions2 {{because 'sizeof(long long) == 4' (8 == 4) evaluated to false}}
+ multiple_expansions_ok<Y, X>();
+}
+
namespace kind_errors {
template <class T> constexpr int Var = 1;
template <class T> constexpr int Var2 = 2;
>From a29ebdacf92d7ad0844000a6d41c3c478fe3d7b4 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Wed, 26 Aug 2026 14:32:28 +0200
Subject: [PATCH 3/4] address feedback
---
clang/include/clang/AST/ExprCXX.h | 4 ++-
clang/include/clang/AST/TemplateBase.h | 11 --------
clang/include/clang/AST/TemplateName.h | 28 ++++++++++++++++---
clang/lib/AST/TemplateName.cpp | 19 ++++++-------
clang/lib/Parse/ParseDeclCXX.cpp | 2 +-
clang/lib/Parse/ParseExpr.cpp | 3 +-
clang/lib/Sema/SemaTemplateVariadic.cpp | 3 +-
.../SemaCXX/cxx2d-pack-indexing-template.cpp | 13 +++++++++
8 files changed, 53 insertions(+), 30 deletions(-)
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 7a2022adf09e4..1393da4da2721 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -3508,7 +3508,9 @@ class DependentTemplateIdExpr final
TemplateTemplateParmDecl *getParameter() const {
TemplateTemplateParmDecl *P = Name.getAsTemplateTemplateParmDecl();
- assert(P && "A dependent template-id always names a template parameter");
+ assert(
+ P &&
+ "A dependent template-id always names a template template parameter");
return P;
}
diff --git a/clang/include/clang/AST/TemplateBase.h b/clang/include/clang/AST/TemplateBase.h
index 4d1e8beeea18d..5896cc721668d 100644
--- a/clang/include/clang/AST/TemplateBase.h
+++ b/clang/include/clang/AST/TemplateBase.h
@@ -35,17 +35,6 @@ namespace llvm {
class FoldingSetNodeID;
-// Provide PointerLikeTypeTraits for clang::Expr*, this default one requires a
-// full definition of Expr, but this file only sees a forward del because of
-// the dependency.
-template <> struct PointerLikeTypeTraits<clang::Expr *> {
- static inline void *getAsVoidPointer(clang::Expr *P) { return P; }
- static inline clang::Expr *getFromVoidPointer(void *P) {
- return static_cast<clang::Expr *>(P);
- }
- static constexpr int NumLowBitsAvailable = 2;
-};
-
} // namespace llvm
namespace clang {
diff --git a/clang/include/clang/AST/TemplateName.h b/clang/include/clang/AST/TemplateName.h
index c1d790e6a9b26..2ac87bd3cabaa 100644
--- a/clang/include/clang/AST/TemplateName.h
+++ b/clang/include/clang/AST/TemplateName.h
@@ -49,6 +49,25 @@ class TemplateDecl;
class TemplateTemplateParmDecl;
class UsingShadowDecl;
+} // namespace clang
+
+namespace llvm {
+
+// Provide PointerLikeTypeTraits for clang::Expr*, this default one requires a
+// full definition of Expr, but this file only sees a forward del because of
+// the dependency.
+template <> struct PointerLikeTypeTraits<clang::Expr *> {
+ static inline void *getAsVoidPointer(clang::Expr *P) { return P; }
+ static inline clang::Expr *getFromVoidPointer(void *P) {
+ return static_cast<clang::Expr *>(P);
+ }
+ static constexpr int NumLowBitsAvailable = 2;
+};
+
+} // namespace llvm
+
+namespace clang {
+
/// Implementation class used to describe either a set of overloaded
/// template names or an already-substituted template template parameter pack.
class UncommonTemplateNameStorage {
@@ -520,8 +539,7 @@ class PackIndexingTemplateStorage final
friend TrailingObjects;
TemplateName Pattern;
- Expr *IndexExpr;
- bool FullySubstituted;
+ llvm::PointerIntPair<Expr *, 1> IndexAndIsFullySubstitited;
PackIndexingTemplateStorage(TemplateName Pattern, Expr *IndexExpr,
bool FullySubstituted,
@@ -530,9 +548,11 @@ class PackIndexingTemplateStorage final
public:
TemplateName getPattern() const { return Pattern; }
- Expr *getIndexExpr() const { return IndexExpr; }
+ Expr *getIndexExpr() const { return IndexAndIsFullySubstitited.getPointer(); }
- bool isFullySubstituted() const { return FullySubstituted; }
+ bool isFullySubstituted() const {
+ return IndexAndIsFullySubstitited.getInt();
+ }
ArrayRef<TemplateName> getExpansions() const {
return getTrailingObjects(Bits.Data);
diff --git a/clang/lib/AST/TemplateName.cpp b/clang/lib/AST/TemplateName.cpp
index 12c342652c851..4cf58e66a082f 100644
--- a/clang/lib/AST/TemplateName.cpp
+++ b/clang/lib/AST/TemplateName.cpp
@@ -125,8 +125,8 @@ PackIndexingTemplateStorage::PackIndexingTemplateStorage(
ArrayRef<TemplateName> Expansions)
: UncommonTemplateNameStorage(PackIndexing, /*Index=*/0,
/*Data=*/Expansions.size()),
- Pattern(Pattern), IndexExpr(IndexExpr),
- FullySubstituted(FullySubstituted) {
+ Pattern(Pattern),
+ IndexAndIsFullySubstitited(IndexExpr, FullySubstituted) {
llvm::uninitialized_copy(Expansions, getTrailingObjects());
}
@@ -140,9 +140,9 @@ PackIndexingTemplateStorage::getParameterPack() const {
}
UnsignedOrNone PackIndexingTemplateStorage::getSelectedIndex() const {
- if (IndexExpr->isInstantiationDependent())
+ if (getIndexExpr()->isInstantiationDependent())
return std::nullopt;
- auto *CE = dyn_cast<ConstantExpr>(IndexExpr);
+ auto *CE = dyn_cast<ConstantExpr>(getIndexExpr());
if (!CE)
return std::nullopt;
llvm::APSInt Index = CE->getResultAsAPSInt();
@@ -151,21 +151,20 @@ UnsignedOrNone PackIndexingTemplateStorage::getSelectedIndex() const {
}
TemplateName PackIndexingTemplateStorage::getSelectedTemplate() const {
- if (!isFullySubstituted())
+ if (!isFullySubstituted() || getIndexExpr()->isInstantiationDependent())
return TemplateName();
UnsignedOrNone Index = getSelectedIndex();
ArrayRef<TemplateName> Expansions = getExpansions();
- if (!Index || *Index >= Expansions.size())
- return TemplateName();
+ assert(Index && *Index < Expansions.size());
return Expansions[*Index];
}
TemplateNameDependence PackIndexingTemplateStorage::getDependence() const {
TemplateNameDependence IndexD =
- toTemplateNameDependence(IndexExpr->getDependence());
+ toTemplateNameDependence(getIndexExpr()->getDependence());
TemplateNameDependence D =
- IndexD | (IndexExpr->isInstantiationDependent()
+ IndexD | (getIndexExpr()->isInstantiationDependent()
? TemplateNameDependence::DependentInstantiation
: TemplateNameDependence::None);
if (ArrayRef<TemplateName> Expansions = getExpansions(); Expansions.empty())
@@ -192,7 +191,7 @@ TemplateNameDependence PackIndexingTemplateStorage::getDependence() const {
void PackIndexingTemplateStorage::Profile(llvm::FoldingSetNodeID &ID,
const ASTContext &Context) const {
- Profile(ID, Context, Pattern, IndexExpr, isFullySubstituted(),
+ Profile(ID, Context, Pattern, getIndexExpr(), isFullySubstituted(),
getExpansions());
}
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 468fa8cbadd75..d701cbaa43bdd 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -1246,7 +1246,7 @@ bool Parser::AnnotatePackIndexingTemplateName(CXXScopeSpec &SS,
if (T.consumeOpen())
return true;
ExprResult IndexExpr = ParseConstantExpression();
- if (T.consumeClose() || IndexExpr.isInvalid())
+ if (T.consumeClose() || !IndexExpr.isUsable() || Template.get().isNull())
return true;
TemplateName Indexed = Actions.ActOnPackIndexingTemplateName(
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index 524f1889e112c..8f212fab4cdbf 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -904,7 +904,8 @@ Parser::ParseCastExpression(CastParseKind ParseKind, bool isAddressOfOperand,
// indexing
if (TryAnnotateTypeOrScopeToken())
return ExprError();
- if (Tok.isAnnotation())
+ if (Tok.isOneOf(tok::annot_cxxscope, tok::annot_pack_indexing_type,
+ tok::annot_template_id))
return ParseCastExpression(ParseKind, isAddressOfOperand,
CorrectionBehavior, isVectorLiteral,
NotPrimaryExpression);
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index 83a81f2df69cf..fb03591f70583 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -1401,8 +1401,7 @@ ExprResult Sema::BuildPackIndexingExpr(Expr *PackExpression,
TemplateName Sema::ActOnPackIndexingTemplateName(TemplateName Pattern,
SourceLocation NameLoc,
Expr *IndexExpr) {
- if (Pattern.isNull() || !IndexExpr)
- return TemplateName();
+ assert(!Pattern.isNull() && IndexExpr);
// C++29 [temp.names]p3:
// The simple-template-name P in a pack-index-template-name shall denote a
diff --git a/clang/test/SemaCXX/cxx2d-pack-indexing-template.cpp b/clang/test/SemaCXX/cxx2d-pack-indexing-template.cpp
index 396fa66f7693c..91253f86b8024 100644
--- a/clang/test/SemaCXX/cxx2d-pack-indexing-template.cpp
+++ b/clang/test/SemaCXX/cxx2d-pack-indexing-template.cpp
@@ -152,6 +152,19 @@ struct Nested {
static_assert(__is_same(Nested<A>::apply<int, long>, List<A<int>, A<long>>));
}
+namespace partial_substitution2 {
+template <template <typename> typename... TT>
+struct S {
+ using A = TT...[1]<int>;
+};
+
+template <typename>
+struct T;
+
+template <template <typename> typename... TT>
+using X = S<T, TT...>;
+}
+
namespace empty_pack {
template <template <class> class... TT>
using U = TT...[0]<int>; // expected-error {{invalid index 0 for pack 'TT' of size 0}}
>From 228752fa54c5a3e7fea22f39691ac0c625675694 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Wed, 26 Aug 2026 15:13:01 +0200
Subject: [PATCH 4/4] fix windows test
---
clang/test/AST/ast-dump-pack-indexing-template.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/test/AST/ast-dump-pack-indexing-template.cpp b/clang/test/AST/ast-dump-pack-indexing-template.cpp
index 4fcbd90d16862..c9cef1020c6b1 100644
--- a/clang/test/AST/ast-dump-pack-indexing-template.cpp
+++ b/clang/test/AST/ast-dump-pack-indexing-template.cpp
@@ -14,7 +14,7 @@ struct DumpDependent {
// CHECK-NEXT: |-name: 'TT...[1]':'template-parameter-0-0...[1]' pack_indexing index 1
// CHECK-NEXT: | |-pattern: 'TT':'template-parameter-0-0'
// CHECK-NEXT: | | `-TemplateTemplateParmDecl {{.*}} depth 0 index 0 ... TT
-// CHECK-NEXT: | `-index: ConstantExpr {{.*}} '__size_t':'unsigned long'
+// CHECK-NEXT: | `-index: ConstantExpr {{.*}} '__size_t':'{{.*}}'
// CHECK-NEXT: | `-value: Int 1
// CHECK-NEXT: `-TemplateArgument type 'int'
@@ -36,7 +36,7 @@ constexpr bool DumpConceptId = CC...[0]<int>;
// CHECK-NEXT: `-name: 'CC...[0]':'template-parameter-0-0...[0]' pack_indexing index 0
// CHECK-NEXT: |-pattern: 'CC':'template-parameter-0-0'
// CHECK-NEXT: | `-TemplateTemplateParmDecl {{.*}} depth 0 index 0 ... CC
-// CHECK-NEXT: `-index: ConstantExpr {{.*}} '__size_t':'unsigned long'
+// CHECK-NEXT: `-index: ConstantExpr {{.*}} '__size_t':'{{.*}}'
// CHECK-NEXT: `-value: Int 0
template <template <class> auto... VV>
More information about the cfe-commits
mailing list