[clang] [Clang][C++26] Implement Pack Indexing (P2662R3). (PR #72644)

via cfe-commits cfe-commits at lists.llvm.org
Fri Jan 12 10:07:11 PST 2024


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

>From d3be2f228ce9d395e539a8827192da3f9b1be676 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Tue, 13 Sep 2022 18:29:34 +0200
Subject: [PATCH 1/5] [Clang][C++26] Implement Pack Indexing (P2662R3).

https://isocpp.org/files/papers/P2662R3.pdf

Because there is a slight chance the syntax might change slightly
(see https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2994r0.html),
the feature is not exposed in other language modes.
---
 clang/docs/ReleaseNotes.rst                   |   2 +
 clang/include/clang-c/Index.h                 |   7 +-
 clang/include/clang/AST/ASTContext.h          |   8 +
 clang/include/clang/AST/ASTNodeTraverser.h    |   6 +
 clang/include/clang/AST/ExprCXX.h             | 106 ++++++++++
 clang/include/clang/AST/RecursiveASTVisitor.h |  11 +
 clang/include/clang/AST/Type.h                |  66 ++++++
 clang/include/clang/AST/TypeLoc.h             |  28 +++
 clang/include/clang/AST/TypeProperties.td     |  14 ++
 .../clang/Basic/DiagnosticSemaKinds.td        |   5 +-
 clang/include/clang/Basic/Specifiers.h        |   1 +
 clang/include/clang/Basic/StmtNodes.td        |   1 +
 clang/include/clang/Basic/TokenKinds.def      |   2 +
 clang/include/clang/Basic/TypeNodes.td        |   1 +
 clang/include/clang/Parse/Parser.h            |   9 +
 clang/include/clang/Sema/DeclSpec.h           |  25 ++-
 clang/include/clang/Sema/Sema.h               |  25 +++
 .../include/clang/Serialization/ASTBitCodes.h |   1 +
 .../clang/Serialization/TypeBitCodes.def      |   2 +
 clang/lib/AST/ASTContext.cpp                  |  43 ++++
 clang/lib/AST/ASTImporter.cpp                 |  12 ++
 clang/lib/AST/ASTStructuralEquivalence.cpp    |  10 +
 clang/lib/AST/Expr.cpp                        |   8 +
 clang/lib/AST/ExprCXX.cpp                     |  37 ++++
 clang/lib/AST/ExprClassification.cpp          |   3 +
 clang/lib/AST/ExprConstant.cpp                |   7 +
 clang/lib/AST/ItaniumMangle.cpp               |   9 +
 clang/lib/AST/MicrosoftMangle.cpp             |   6 +
 clang/lib/AST/StmtPrinter.cpp                 |   4 +
 clang/lib/AST/StmtProfile.cpp                 |   6 +
 clang/lib/AST/Type.cpp                        |  40 ++++
 clang/lib/AST/TypePrinter.cpp                 |  18 ++
 clang/lib/CodeGen/CGDebugInfo.cpp             |   5 +
 clang/lib/CodeGen/CGExpr.cpp                  |   2 +
 clang/lib/CodeGen/CGExprAgg.cpp               |   3 +
 clang/lib/CodeGen/CGExprComplex.cpp           |   4 +
 clang/lib/CodeGen/CGExprConstant.cpp          |   4 +
 clang/lib/CodeGen/CGExprScalar.cpp            |   3 +
 clang/lib/CodeGen/CodeGenFunction.cpp         |   1 +
 clang/lib/Parse/ParseCXXInlineMethods.cpp     |  14 ++
 clang/lib/Parse/ParseDecl.cpp                 |   5 +
 clang/lib/Parse/ParseDeclCXX.cpp              | 101 +++++++++
 clang/lib/Parse/ParseExpr.cpp                 |  13 ++
 clang/lib/Parse/ParseExprCXX.cpp              |  68 +++++-
 clang/lib/Parse/ParseTentative.cpp            |  13 ++
 clang/lib/Parse/Parser.cpp                    |   3 +-
 clang/lib/Sema/DeclSpec.cpp                   |  21 ++
 clang/lib/Sema/SemaCXXScopeSpec.cpp           |  23 ++
 clang/lib/Sema/SemaDecl.cpp                   |   1 +
 clang/lib/Sema/SemaDeclCXX.cpp                |   4 +
 clang/lib/Sema/SemaExceptionSpec.cpp          |   1 +
 clang/lib/Sema/SemaExpr.cpp                   |   3 +
 clang/lib/Sema/SemaExprCXX.cpp                |  32 ++-
 clang/lib/Sema/SemaTemplate.cpp               |   5 +
 clang/lib/Sema/SemaTemplateDeduction.cpp      |  16 ++
 clang/lib/Sema/SemaTemplateVariadic.cpp       |  66 +++++-
 clang/lib/Sema/SemaType.cpp                   |  60 ++++++
 clang/lib/Sema/TreeTransform.h                | 200 ++++++++++++++++++
 clang/lib/Serialization/ASTReader.cpp         |   4 +
 clang/lib/Serialization/ASTReaderStmt.cpp     |  22 ++
 clang/lib/Serialization/ASTWriter.cpp         |   5 +
 clang/lib/Serialization/ASTWriterStmt.cpp     |  16 ++
 clang/lib/StaticAnalyzer/Core/ExprEngine.cpp  |   1 +
 clang/test/PCH/pack_indexing.cpp              |  16 ++
 clang/test/Parser/cxx2b-pack-indexing.cpp     |  65 ++++++
 clang/test/SemaCXX/cxx2b-pack-indexing.cpp    | 115 ++++++++++
 clang/tools/libclang/CIndex.cpp               |   8 +
 clang/tools/libclang/CXCursor.cpp             |   4 +
 clang/www/cxx_status.html                     |   2 +-
 69 files changed, 1435 insertions(+), 17 deletions(-)
 create mode 100644 clang/test/PCH/pack_indexing.cpp
 create mode 100644 clang/test/Parser/cxx2b-pack-indexing.cpp
 create mode 100644 clang/test/SemaCXX/cxx2b-pack-indexing.cpp

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index ed1a978b5382d7..dab67040907767 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -182,6 +182,8 @@ C++2c Feature Support
   This is applied to both C++ standard attributes, and other attributes supported by Clang.
   This completes the implementation of `P2361R6 Unevaluated Strings <https://wg21.link/P2361R6>`_
 
+- Implemented `P2662R3 Pack Indexing <https://wg21.link/P2662R3>`_.
+
 
 Resolutions to C++ Defect Reports
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h
index 64ab3378957c70..2c0b89a0d12b21 100644
--- a/clang/include/clang-c/Index.h
+++ b/clang/include/clang-c/Index.h
@@ -1685,7 +1685,12 @@ enum CXCursorKind {
    */
   CXCursor_CXXParenListInitExpr = 155,
 
-  CXCursor_LastExpr = CXCursor_CXXParenListInitExpr,
+  /**
+   *  Represents a C++26 pack indexing expression
+   */
+  CXCursor_PackIndexingExpr = 156,
+
+  CXCursor_LastExpr = CXCursor_PackIndexingExpr,
 
   /* Statements */
   CXCursor_FirstStmt = 200,
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 3e46a5da3fc043..9e1c44eb19b805 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -214,6 +214,9 @@ class ASTContext : public RefCountedBase<ASTContext> {
       DependentTypeOfExprTypes;
   mutable llvm::ContextualFoldingSet<DependentDecltypeType, ASTContext &>
       DependentDecltypeTypes;
+
+  mutable llvm::FoldingSet<PackIndexingType> DependentPackIndexingTypes;
+
   mutable llvm::FoldingSet<TemplateTypeParmType> TemplateTypeParmTypes;
   mutable llvm::FoldingSet<ObjCTypeParamType> ObjCTypeParamTypes;
   mutable llvm::FoldingSet<SubstTemplateTypeParmType>
@@ -1713,6 +1716,11 @@ class ASTContext : public RefCountedBase<ASTContext> {
   /// C++11 decltype.
   QualType getDecltypeType(Expr *e, QualType UnderlyingType) const;
 
+  QualType getPackIndexingType(QualType Pattern, Expr *IndexExpr,
+                               bool FullyExpanded = false,
+                               ArrayRef<QualType> Expansions = {},
+                               int Index = -1) const;
+
   /// Unary type transforms
   QualType getUnaryTransformType(QualType BaseType, QualType UnderlyingType,
                                  UnaryTransformType::UTTKind UKind) const;
diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h
index cc8dab97f8b010..950da2b8d2c456 100644
--- a/clang/include/clang/AST/ASTNodeTraverser.h
+++ b/clang/include/clang/AST/ASTNodeTraverser.h
@@ -385,6 +385,12 @@ class ASTNodeTraverser
   void VisitDecltypeType(const DecltypeType *T) {
     Visit(T->getUnderlyingExpr());
   }
+
+  void VisitPackIndexingType(const PackIndexingType *T) {
+    Visit(T->getPattern());
+    Visit(T->getIndexExpr());
+  }
+
   void VisitUnaryTransformType(const UnaryTransformType *T) {
     Visit(T->getBaseType());
   }
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index 24278016431837..efd6558326099e 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -4344,6 +4344,112 @@ class SizeOfPackExpr final
   }
 };
 
+class PackIndexingExpr final
+    : public Expr,
+      private llvm::TrailingObjects<PackIndexingExpr, Expr *> {
+  friend class ASTStmtReader;
+  friend class ASTStmtWriter;
+  friend TrailingObjects;
+
+  SourceLocation EllipsisLoc;
+
+  // The location of the closing bracket
+  SourceLocation RSquareLoc;
+
+  // The pack being indexed, followed by the index
+  Stmt *SubExprs[2];
+
+  // The evaluated index
+  std::optional<int64_t> Index;
+
+  size_t TransformedExpressions;
+
+  PackIndexingExpr(QualType Type, SourceLocation EllipsisLoc,
+                   SourceLocation RSquareLoc, Expr *PackIdExpr, Expr *IndexExpr,
+                   std::optional<int64_t> Index = std::nullopt,
+                   ArrayRef<Expr *> SubstitutedExprs = {})
+      : Expr(PackIndexingExprClass, Type, VK_LValue, OK_Ordinary),
+        EllipsisLoc(EllipsisLoc), RSquareLoc(RSquareLoc),
+        SubExprs{PackIdExpr, IndexExpr}, Index(Index),
+        TransformedExpressions(SubstitutedExprs.size()) {
+
+    auto *Exprs = getTrailingObjects<Expr *>();
+    std::uninitialized_copy(SubstitutedExprs.begin(), SubstitutedExprs.end(),
+                            Exprs);
+
+    ExprDependence D = IndexExpr->getDependence();
+    if (SubstitutedExprs.empty())
+      D |= (PackIdExpr->getDependence() |
+            ExprDependence::TypeValueInstantiation) &
+           ~ExprDependence::UnexpandedPack;
+    else if (!IndexExpr->isValueDependent()) {
+      assert(Index && *Index < int64_t(SubstitutedExprs.size()) &&
+             "pack index out of bound");
+      D |= SubstitutedExprs[*Index]->getDependence();
+      setValueKind(SubstitutedExprs[*Index]->getValueKind());
+    }
+    setDependence(D);
+  }
+
+  /// Create an empty expression.
+  PackIndexingExpr(EmptyShell Empty) : Expr(PackIndexingExprClass, Empty) {}
+
+  unsigned numTrailingObjects(OverloadToken<Expr *>) const {
+    return TransformedExpressions;
+  }
+
+public:
+  static PackIndexingExpr *Create(ASTContext &Context,
+                                  SourceLocation EllipsisLoc,
+                                  SourceLocation RSquareLoc, Expr *PackIdExpr,
+                                  Expr *IndexExpr,
+                                  std::optional<int64_t> Index = std::nullopt,
+                                  ArrayRef<Expr *> SubstitutedExprs = {});
+  static PackIndexingExpr *CreateDeserialized(ASTContext &Context,
+                                              unsigned NumTransformedExprs);
+
+  /// Determine the location of the 'sizeof' keyword.
+  SourceLocation getEllipsisLoc() const { return EllipsisLoc; }
+
+  /// Determine the location of the parameter pack.
+  SourceLocation getPackLoc() const { return SubExprs[0]->getBeginLoc(); }
+
+  /// Determine the location of the right parenthesis.
+  SourceLocation getRSquareLoc() const { return RSquareLoc; }
+
+  SourceLocation getBeginLoc() const LLVM_READONLY { return getPackLoc(); }
+  SourceLocation getEndLoc() const LLVM_READONLY { return RSquareLoc; }
+
+  Expr *getPackIdExpression() const { return cast<Expr>(SubExprs[0]); }
+
+  NamedDecl *getPackDecl() const;
+
+  Expr *getIndexExpr() const { return cast<Expr>(SubExprs[1]); }
+
+  Expr *getSelectedExpr() const {
+    assert(Index && !isInstantiationDependent() &&
+           "extracting the indexed expression of a dependant pack");
+    return getTrailingObjects<Expr *>()[*Index];
+  }
+
+  llvm::ArrayRef<Expr *> getExpressions() const {
+    if (TransformedExpressions == 0)
+      return {};
+    return {getTrailingObjects<Expr *>(), TransformedExpressions};
+  }
+
+  static bool classof(const Stmt *T) {
+    return T->getStmtClass() == PackIndexingExprClass;
+  }
+
+  // Iterators
+  child_range children() { return child_range(SubExprs, SubExprs + 2); }
+
+  const_child_range children() const {
+    return const_child_range(SubExprs, SubExprs + 2);
+  }
+};
+
 /// Represents a reference to a non-type template parameter
 /// that has been substituted with a template argument.
 class SubstNonTypeTemplateParmExpr : public Expr {
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index 53bc15e1b19f66..1c26f0512fa134 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -1063,6 +1063,11 @@ DEF_TRAVERSE_TYPE(TypeOfType, { TRY_TO(TraverseType(T->getUnmodifiedType())); })
 DEF_TRAVERSE_TYPE(DecltypeType,
                   { TRY_TO(TraverseStmt(T->getUnderlyingExpr())); })
 
+DEF_TRAVERSE_TYPE(PackIndexingType, {
+  TRY_TO(TraverseType(T->getPattern()));
+  TRY_TO(TraverseStmt(T->getIndexExpr()));
+})
+
 DEF_TRAVERSE_TYPE(UnaryTransformType, {
   TRY_TO(TraverseType(T->getBaseType()));
   TRY_TO(TraverseType(T->getUnderlyingType()));
@@ -1341,6 +1346,11 @@ DEF_TRAVERSE_TYPELOC(DecltypeType, {
   TRY_TO(TraverseStmt(TL.getTypePtr()->getUnderlyingExpr()));
 })
 
+DEF_TRAVERSE_TYPELOC(PackIndexingType, {
+  TRY_TO(TraverseType(TL.getPattern()));
+  TRY_TO(TraverseStmt(TL.getTypePtr()->getIndexExpr()));
+})
+
 DEF_TRAVERSE_TYPELOC(UnaryTransformType, {
   TRY_TO(TraverseTypeLoc(TL.getUnderlyingTInfo()->getTypeLoc()));
 })
@@ -2857,6 +2867,7 @@ DEF_TRAVERSE_STMT(CompoundAssignOperator, {})
 DEF_TRAVERSE_STMT(CXXNoexceptExpr, {})
 DEF_TRAVERSE_STMT(PackExpansionExpr, {})
 DEF_TRAVERSE_STMT(SizeOfPackExpr, {})
+DEF_TRAVERSE_STMT(PackIndexingExpr, {})
 DEF_TRAVERSE_STMT(SubstNonTypeTemplateParmPackExpr, {})
 DEF_TRAVERSE_STMT(SubstNonTypeTemplateParmExpr, {})
 DEF_TRAVERSE_STMT(FunctionParmPackExpr, {})
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index 6c147eb8f64062..a710766d1cf92f 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -4884,6 +4884,72 @@ class DependentDecltypeType : public DecltypeType, public llvm::FoldingSetNode {
                       Expr *E);
 };
 
+class PackIndexingType final
+    : public Type,
+      public llvm::FoldingSetNode,
+      private llvm::TrailingObjects<PackIndexingType, QualType> {
+  friend TrailingObjects;
+
+  const ASTContext &Context;
+  QualType Pattern;
+  Expr *IndexExpr;
+
+  unsigned Size;
+  int Index = -1;
+
+protected:
+  friend class ASTContext; // ASTContext creates these.
+  PackIndexingType(const ASTContext &Context, QualType Canonical,
+                   QualType Pattern, Expr *IndexExpr,
+                   ArrayRef<QualType> Expansions = {}, int Index = -1);
+
+public:
+  Expr *getIndexExpr() const { return IndexExpr; }
+  QualType getPattern() const { return Pattern; }
+
+  bool isSugared() const { return hasSelectedType(); }
+
+  QualType desugar() const {
+    if (hasSelectedType())
+      return getSelectedType();
+    return QualType(this, 0);
+  }
+
+  QualType getSelectedType() const {
+    assert(hasSelectedType() && "Type is dependant");
+    return *(getExpansionsPtr() + Index);
+  }
+
+  bool hasSelectedType() const { return Index != -1 && !isDependentType(); }
+
+  ArrayRef<QualType> getExpansions() const {
+    return {getExpansionsPtr(), Size};
+  }
+
+  static bool classof(const Type *T) {
+    return T->getTypeClass() == PackIndexing;
+  }
+
+  void Profile(llvm::FoldingSetNodeID &ID) {
+    if (hasSelectedType())
+      getSelectedType().Profile(ID);
+    else
+      Profile(ID, Context, getPattern(), getIndexExpr());
+  }
+  static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
+                      QualType Pattern, Expr *E);
+
+private:
+  const QualType *getExpansionsPtr() const {
+    return getTrailingObjects<QualType>();
+  }
+
+  static TypeDependence computeDependence(QualType Pattern, Expr *IndexExpr,
+                                          ArrayRef<QualType> Expansions = {});
+
+  unsigned numTrailingObjects(OverloadToken<QualType>) const { return Size; }
+};
+
 /// A unary type transform, which is a type constructed from another.
 class UnaryTransformType : public Type {
 public:
diff --git a/clang/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h
index 471deb14aba51f..38e40ece425fb8 100644
--- a/clang/include/clang/AST/TypeLoc.h
+++ b/clang/include/clang/AST/TypeLoc.h
@@ -2055,6 +2055,34 @@ class DecltypeTypeLoc
   }
 };
 
+struct PackIndexingTypeLocInfo {
+  SourceLocation EllipsisLoc;
+};
+
+class PackIndexingTypeLoc
+    : public ConcreteTypeLoc<UnqualTypeLoc, PackIndexingTypeLoc,
+                             PackIndexingType, PackIndexingTypeLocInfo> {
+
+public:
+  Expr *getIndexExpr() const { return getTypePtr()->getIndexExpr(); }
+  QualType getPattern() const { return getTypePtr()->getPattern(); }
+
+  SourceLocation getEllipsisLoc() const { return getLocalData()->EllipsisLoc; }
+  void setEllipsisLoc(SourceLocation Loc) { getLocalData()->EllipsisLoc = Loc; }
+
+  void initializeLocal(ASTContext &Context, SourceLocation Loc) {
+    setEllipsisLoc(Loc);
+  }
+
+  TypeLoc getPatternLoc() const { return getInnerTypeLoc(); }
+
+  QualType getInnerType() const { return this->getTypePtr()->getPattern(); }
+
+  SourceRange getLocalSourceRange() const {
+    return SourceRange(getEllipsisLoc(), getEllipsisLoc());
+  }
+};
+
 struct UnaryTransformTypeLocInfo {
   // FIXME: While there's only one unary transform right now, future ones may
   // need different representations
diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td
index 682c869b0c5847..0ba172a4035fdb 100644
--- a/clang/include/clang/AST/TypeProperties.td
+++ b/clang/include/clang/AST/TypeProperties.td
@@ -433,6 +433,20 @@ let Class = DecltypeType in {
   }]>;
 }
 
+let Class = PackIndexingType in {
+  def : Property<"pattern", QualType> {
+    let Read = [{ node->getPattern() }];
+  }
+  def : Property<"indexExpression", ExprRef> {
+    let Read = [{ node->getIndexExpr() }];
+  }
+
+  def : Creator<[{
+    return ctx.getPackIndexingType(pattern, indexExpression);
+  }]>;
+}
+
+
 let Class = UnaryTransformType in {
   def : Property<"baseType", QualType> {
     let Read = [{ node->getBaseType() }];
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index a9dde041bc22a6..656dcbc42aae59 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5681,9 +5681,12 @@ def err_function_parameter_pack_without_parameter_packs : Error<
 def err_ellipsis_in_declarator_not_parameter : Error<
   "only function and template parameters can be parameter packs">;
 
-def err_sizeof_pack_no_pack_name : Error<
+def err_expected_name_of_pack : Error<
   "%0 does not refer to the name of a parameter pack">;
 
+def err_pack_index_out_of_bound : Error<
+  "%0 is not a valid index for pack %1 of size %2">;
+
 def err_fold_expression_packs_both_sides : Error<
   "binary fold expression has unexpanded parameter packs in both operands">;
 def err_fold_expression_empty : Error<
diff --git a/clang/include/clang/Basic/Specifiers.h b/clang/include/clang/Basic/Specifiers.h
index 87f29c8ae10bd9..bff4c1616c1d02 100644
--- a/clang/include/clang/Basic/Specifiers.h
+++ b/clang/include/clang/Basic/Specifiers.h
@@ -94,6 +94,7 @@ namespace clang {
     TST_auto_type,       // __auto_type extension
     TST_unknown_anytype, // __unknown_anytype extension
     TST_atomic,          // C11 _Atomic
+    TST_indexed_typename_pack,
 #define GENERIC_IMAGE_TYPE(ImgType, Id) TST_##ImgType##_t, // OpenCL image types
 #include "clang/Basic/OpenCLImageTypes.def"
     TST_error // erroneous type
diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td
index cec301dfca2817..9d03800840fcd0 100644
--- a/clang/include/clang/Basic/StmtNodes.td
+++ b/clang/include/clang/Basic/StmtNodes.td
@@ -154,6 +154,7 @@ def UnresolvedMemberExpr : StmtNode<OverloadExpr>;
 def CXXNoexceptExpr : StmtNode<Expr>;
 def PackExpansionExpr : StmtNode<Expr>;
 def SizeOfPackExpr : StmtNode<Expr>;
+def PackIndexingExpr : StmtNode<Expr>;
 def SubstNonTypeTemplateParmExpr : StmtNode<Expr>;
 def SubstNonTypeTemplateParmPackExpr : StmtNode<Expr>;
 def FunctionParmPackExpr : StmtNode<Expr>;
diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index 82a503d01068d5..6b5f8210fa6d0e 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -836,6 +836,8 @@ ANNOTATION(primary_expr) // annotation for a primary expression, used when
                          // message send
 ANNOTATION(decltype)     // annotation for a decltype expression,
                          // e.g., "decltype(foo.bar())"
+ANNOTATION(indexed_pack_type) // annotation for an indexed pack of type,
+                              // e.g., "T...[expr]"
 
 // Annotation for #pragma unused(...)
 // For each argument inside the parentheses the pragma handler will produce
diff --git a/clang/include/clang/Basic/TypeNodes.td b/clang/include/clang/Basic/TypeNodes.td
index 649b071cebb940..6419762417371b 100644
--- a/clang/include/clang/Basic/TypeNodes.td
+++ b/clang/include/clang/Basic/TypeNodes.td
@@ -103,6 +103,7 @@ def InjectedClassNameType : TypeNode<Type>, AlwaysDependent, LeafType;
 def DependentNameType : TypeNode<Type>, AlwaysDependent;
 def DependentTemplateSpecializationType : TypeNode<Type>, AlwaysDependent;
 def PackExpansionType : TypeNode<Type>, AlwaysDependent;
+def PackIndexingType  : TypeNode<Type>, NeverCanonicalUnlessDependent;
 def ObjCTypeParamType : TypeNode<Type>, NeverCanonical;
 def ObjCObjectType : TypeNode<Type>;
 def ObjCInterfaceType : TypeNode<ObjCObjectType>, LeafType;
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 30e0352c868637..c7bbc3d6384306 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -1884,6 +1884,10 @@ class Parser : public CodeCompletionHandler {
   // C++ Expressions
   ExprResult tryParseCXXIdExpression(CXXScopeSpec &SS, bool isAddressOfOperand,
                                      Token &Replacement);
+
+  ExprResult tryParseCXXPackIndexingExpression(ExprResult PackIdExpression);
+  ExprResult ParseCXXPackIndexingExpression(ExprResult PackIdExpression);
+
   ExprResult ParseCXXIdExpression(bool isAddressOfOperand = false);
 
   bool areTokensAdjacent(const Token &A, const Token &B);
@@ -2430,6 +2434,11 @@ class Parser : public CodeCompletionHandler {
       DeclSpecContext DSC, LateParsedAttrList *LateAttrs,
       ImplicitTypenameContext AllowImplicitTypename);
 
+  SourceLocation ParseIndexedTypeNamePack(DeclSpec &DS);
+  void AnnotateExistingIndexedTypeNamePack(ParsedType T,
+                                           SourceLocation StartLoc,
+                                           SourceLocation EndLoc);
+
   bool DiagnoseMissingSemiAfterTagDefinition(
       DeclSpec &DS, AccessSpecifier AS, DeclSpecContext DSContext,
       LateParsedAttrList *LateAttrs = nullptr);
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index 4561cca929c0d0..4726cbbd1af50c 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -309,6 +309,7 @@ class DeclSpec {
   static const TST TST_typeof_unqualExpr = clang::TST_typeof_unqualExpr;
   static const TST TST_decltype = clang::TST_decltype;
   static const TST TST_decltype_auto = clang::TST_decltype_auto;
+  static const TST TST_indexed_typename_pack = clang::TST_indexed_typename_pack;
 #define TRANSFORM_TYPE_TRAIT_DEF(_, Trait)                                     \
   static const TST TST_##Trait = clang::TST_##Trait;
 #include "clang/Basic/TransformTypeTraits.def"
@@ -389,6 +390,7 @@ class DeclSpec {
     Expr *ExprRep;
     TemplateIdAnnotation *TemplateIdRep;
   };
+  Expr *PackIndexingExpr = nullptr;
 
   /// ExplicitSpecifier - Store information about explicit spicifer.
   ExplicitSpecifier FS_explicit_specifier;
@@ -405,7 +407,7 @@ class DeclSpec {
 
   SourceLocation StorageClassSpecLoc, ThreadStorageClassSpecLoc;
   SourceRange TSWRange;
-  SourceLocation TSCLoc, TSSLoc, TSTLoc, AltiVecLoc, TSSatLoc;
+  SourceLocation TSCLoc, TSSLoc, TSTLoc, AltiVecLoc, TSSatLoc, EllipsisLoc;
   /// TSTNameLoc - If TypeSpecType is any of class, enum, struct, union,
   /// typename, then this is the location of the named type (if present);
   /// otherwise, it is the same as TSTLoc. Hence, the pair TSTLoc and
@@ -427,7 +429,8 @@ class DeclSpec {
 
   static bool isTypeRep(TST T) {
     return T == TST_atomic || T == TST_typename || T == TST_typeofType ||
-           T == TST_typeof_unqualType || isTransformTypeTrait(T);
+           T == TST_typeof_unqualType || isTransformTypeTrait(T) ||
+           T == TST_indexed_typename_pack;
   }
   static bool isExprRep(TST T) {
     return T == TST_typeofExpr || T == TST_typeof_unqualExpr ||
@@ -530,6 +533,13 @@ class DeclSpec {
     assert(isExprRep((TST) TypeSpecType) && "DeclSpec does not store an expr");
     return ExprRep;
   }
+
+  Expr *getPackIndexingExpr() const {
+    assert(TypeSpecType == TST_indexed_typename_pack &&
+           "DeclSpec is not a pack indexing expr");
+    return PackIndexingExpr;
+  }
+
   TemplateIdAnnotation *getRepAsTemplateId() const {
     assert(isTemplateIdRep((TST) TypeSpecType) &&
            "DeclSpec does not store a template id");
@@ -587,6 +597,7 @@ class DeclSpec {
   SourceLocation getAtomicSpecLoc() const { return TQ_atomicLoc; }
   SourceLocation getUnalignedSpecLoc() const { return TQ_unalignedLoc; }
   SourceLocation getPipeLoc() const { return TQ_pipeLoc; }
+  SourceLocation getEllipsisLoc() const { return EllipsisLoc; }
 
   /// Clear out all of the type qualifiers.
   void ClearTypeQualifiers() {
@@ -743,6 +754,9 @@ class DeclSpec {
                      const PrintingPolicy &Policy);
   bool SetTypeSpecSat(SourceLocation Loc, const char *&PrevSpec,
                       unsigned &DiagID);
+
+  void SetPackIndexingExpr(SourceLocation EllipsisLoc, Expr *Pack);
+
   bool SetTypeSpecError();
   void UpdateDeclRep(Decl *Rep) {
     assert(isDeclRep((TST) TypeSpecType));
@@ -1939,6 +1953,8 @@ class Declarator {
   /// this declarator as a parameter pack.
   SourceLocation EllipsisLoc;
 
+  Expr *PackIndexingExpr;
+
   friend struct DeclaratorChunk;
 
 public:
@@ -2063,6 +2079,7 @@ class Declarator {
     ObjCWeakProperty = false;
     CommaLoc = SourceLocation();
     EllipsisLoc = SourceLocation();
+    PackIndexingExpr = nullptr;
   }
 
   /// mayOmitIdentifier - Return true if the identifier is either optional or
@@ -2648,6 +2665,10 @@ class Declarator {
   SourceLocation getEllipsisLoc() const { return EllipsisLoc; }
   void setEllipsisLoc(SourceLocation EL) { EllipsisLoc = EL; }
 
+  bool hasPackIndexing() const { return PackIndexingExpr != nullptr; }
+  Expr *getPackIndexingExpr() const { return PackIndexingExpr; }
+  void setPackIndexingExpr(Expr *PI) { PackIndexingExpr = PI; }
+
   void setFunctionDefinitionKind(FunctionDefinitionKind Val) {
     FunctionDefinition = static_cast<unsigned>(Val);
   }
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 501dc01200a1c3..cbebb79b91b52a 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2589,6 +2589,14 @@ class Sema final {
   /// context, such as when building a type for decltype(auto).
   QualType BuildDecltypeType(Expr *E, bool AsUnevaluated = true);
 
+  QualType ActOnPackIndexingType(QualType Pattern, Expr *IndexExpr,
+                                 SourceLocation Loc,
+                                 SourceLocation EllipsisLoc);
+  QualType BuildPackIndexingType(QualType Pattern, Expr *IndexExpr,
+                                 SourceLocation Loc, SourceLocation EllipsisLoc,
+                                 bool FullyExpanded = false,
+                                 ArrayRef<QualType> Expansions = {});
+
   using UTTKind = UnaryTransformType::UTTKind;
   QualType BuildUnaryTransformType(QualType BaseType, UTTKind UKind,
                                    SourceLocation Loc);
@@ -5820,6 +5828,18 @@ class Sema final {
                                           IdentifierInfo &Name,
                                           SourceLocation NameLoc,
                                           SourceLocation RParenLoc);
+
+  ExprResult ActOnPackIndexingExpr(Scope *S, Expr *PackExpression,
+                                   SourceLocation EllipsisLoc,
+                                   SourceLocation LSquareLoc, Expr *IndexExpr,
+                                   SourceLocation RSquareLoc);
+
+  ExprResult BuildPackIndexingExpr(Expr *PackExpression,
+                                   SourceLocation EllipsisLoc, Expr *IndexExpr,
+                                   SourceLocation RSquareLoc,
+                                   ArrayRef<Expr *> ExpandedExprs = {},
+                                   bool EmptyPack = false);
+
   ExprResult ActOnPostfixUnaryOp(Scope *S, SourceLocation OpLoc,
                                  tok::TokenKind Kind, Expr *Input);
 
@@ -7087,6 +7107,11 @@ class Sema final {
                                            const DeclSpec &DS,
                                            SourceLocation ColonColonLoc);
 
+  bool ActOnCXXNestedNameSpecifierIndexedPack(CXXScopeSpec &SS,
+                                              const DeclSpec &DS,
+                                              SourceLocation ColonColonLoc,
+                                              QualType Type);
+
   bool IsInvalidUnlessNestedName(Scope *S, CXXScopeSpec &SS,
                                  NestedNameSpecInfo &IdInfo,
                                  bool EnteringContext);
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
index fdd64f2abbe937..ea084b328d6124 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -1895,6 +1895,7 @@ enum StmtCode {
   EXPR_ARRAY_TYPE_TRAIT,            // ArrayTypeTraitIntExpr
 
   EXPR_PACK_EXPANSION,                    // PackExpansionExpr
+  EXPR_PACK_INDEXING,                     // PackIndexingExpr
   EXPR_SIZEOF_PACK,                       // SizeOfPackExpr
   EXPR_SUBST_NON_TYPE_TEMPLATE_PARM,      // SubstNonTypeTemplateParmExpr
   EXPR_SUBST_NON_TYPE_TEMPLATE_PARM_PACK, // SubstNonTypeTemplateParmPackExpr
diff --git a/clang/include/clang/Serialization/TypeBitCodes.def b/clang/include/clang/Serialization/TypeBitCodes.def
index 89ae1a2fa39546..adbd5ec5d4b544 100644
--- a/clang/include/clang/Serialization/TypeBitCodes.def
+++ b/clang/include/clang/Serialization/TypeBitCodes.def
@@ -64,5 +64,7 @@ TYPE_BIT_CODE(ConstantMatrix, CONSTANT_MATRIX, 52)
 TYPE_BIT_CODE(DependentSizedMatrix, DEPENDENT_SIZE_MATRIX, 53)
 TYPE_BIT_CODE(Using, USING, 54)
 TYPE_BIT_CODE(BTFTagAttributed, BTFTAG_ATTRIBUTED, 55)
+TYPE_BIT_CODE(PackIndexing, PACK_INDEXING, 56)
+
 
 #undef TYPE_BIT_CODE
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 4f54791b4c1e5c..da6265a79316ff 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -3595,6 +3595,7 @@ QualType ASTContext::getVariableArrayDecayedType(QualType type) const {
   case Type::Auto:
   case Type::DeducedTemplateSpecialization:
   case Type::PackExpansion:
+  case Type::PackIndexing:
   case Type::BitInt:
   case Type::DependentBitInt:
     llvm_unreachable("type should never be variably-modified");
@@ -5697,6 +5698,39 @@ QualType ASTContext::getDecltypeType(Expr *e, QualType UnderlyingType) const {
   return QualType(dt, 0);
 }
 
+QualType ASTContext::getPackIndexingType(QualType Pattern, Expr *IndexExpr,
+                                         bool FullyExpanded,
+                                         ArrayRef<QualType> Expansions,
+                                         int Index) const {
+  QualType Canonical;
+  if (FullyExpanded && Index != -1) {
+    Canonical = getCanonicalType(Expansions[Index]);
+  } else {
+    llvm::FoldingSetNodeID ID;
+    PackIndexingType::Profile(ID, *this, Pattern, IndexExpr);
+    void *InsertPos = nullptr;
+    PackIndexingType *Canon =
+        DependentPackIndexingTypes.FindNodeOrInsertPos(ID, InsertPos);
+    if (!Canon) {
+      void *Mem = Allocate(
+          PackIndexingType::totalSizeToAlloc<QualType>(Expansions.size()),
+          TypeAlignment);
+      Canon = new (Mem) PackIndexingType(*this, QualType(), Pattern, IndexExpr,
+                                         Expansions, Index);
+      DependentPackIndexingTypes.InsertNode(Canon, InsertPos);
+    }
+    Canonical = QualType(Canon, 0);
+  }
+
+  void *Mem =
+      Allocate(PackIndexingType::totalSizeToAlloc<QualType>(Expansions.size()),
+               TypeAlignment);
+  auto *T = new (Mem)
+      PackIndexingType(*this, Canonical, Pattern, IndexExpr, Expansions, Index);
+  Types.push_back(T);
+  return QualType(T, 0);
+}
+
 /// getUnaryTransformationType - We don't unique these, since the memory
 /// savings are minimal and these are rare.
 QualType ASTContext::getUnaryTransformType(QualType BaseType,
@@ -12897,6 +12931,14 @@ static QualType getCommonNonSugarTypeNode(ASTContext &Ctx, const Type *X,
     // As Decltype is not uniqued, building a common type would be wasteful.
     return QualType(DX, 0);
   }
+  case Type::PackIndexing: {
+    const auto *DX = cast<PackIndexingType>(X);
+    [[maybe_unused]] const auto *DY = cast<PackIndexingType>(Y);
+    assert(DX->isDependentType());
+    assert(DY->isDependentType());
+    assert(Ctx.hasSameExpr(DX->getIndexExpr(), DY->getIndexExpr()));
+    return QualType(DX, 0);
+  }
   case Type::DependentName: {
     const auto *NX = cast<DependentNameType>(X),
                *NY = cast<DependentNameType>(Y);
@@ -13057,6 +13099,7 @@ static QualType getCommonSugarTypeNode(ASTContext &Ctx, const Type *X,
     return Ctx.getAutoType(Ctx.getQualifiedType(Underlying), AX->getKeyword(),
                            /*IsDependent=*/false, /*IsPack=*/false, CD, As);
   }
+  case Type::PackIndexing:
   case Type::Decltype:
     return QualType();
   case Type::DeducedTemplateSpecialization:
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index c4e931e220f69b..fde7f7efa6d5b4 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -1358,6 +1358,18 @@ ExpectedType ASTNodeImporter::VisitParenType(const ParenType *T) {
   return Importer.getToContext().getParenType(*ToInnerTypeOrErr);
 }
 
+ExpectedType
+ASTNodeImporter::VisitPackIndexingType(clang::PackIndexingType const *T) {
+
+  ExpectedType Pattern = import(T->getPattern());
+  if (!Pattern)
+    return Pattern.takeError();
+  ExpectedExpr Index = import(T->getIndexExpr());
+  if (!Index)
+    return Index.takeError();
+  return Importer.getToContext().getPackIndexingType(*Pattern, *Index);
+}
+
 ExpectedType ASTNodeImporter::VisitTypedefType(const TypedefType *T) {
   Expected<TypedefNameDecl *> ToDeclOrErr = import(T->getDecl());
   if (!ToDeclOrErr)
diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp
index 6bb4bf14b873d7..6f9fc93b11bede 100644
--- a/clang/lib/AST/ASTStructuralEquivalence.cpp
+++ b/clang/lib/AST/ASTStructuralEquivalence.cpp
@@ -1232,6 +1232,16 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context,
       return false;
     break;
 
+  case Type::PackIndexing:
+    if (!IsStructurallyEquivalent(Context,
+                                  cast<PackIndexingType>(T1)->getPattern(),
+                                  cast<PackIndexingType>(T2)->getPattern()))
+      if (!IsStructurallyEquivalent(Context,
+                                    cast<PackIndexingType>(T1)->getIndexExpr(),
+                                    cast<PackIndexingType>(T2)->getIndexExpr()))
+        return false;
+    break;
+
   case Type::ObjCInterface: {
     const auto *Iface1 = cast<ObjCInterfaceType>(T1);
     const auto *Iface2 = cast<ObjCInterfaceType>(T2);
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index 55c6b732b7081f..d8204fc5a0d21a 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -3394,6 +3394,11 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef,
       return Exp->getSubExpr()->isConstantInitializer(Ctx, false, Culprit);
     break;
   }
+  case PackIndexingExprClass: {
+    return cast<PackIndexingExpr>(this)
+        ->getSelectedExpr()
+        ->isConstantInitializer(Ctx, false, Culprit);
+  }
   case CXXFunctionalCastExprClass:
   case CXXStaticCastExprClass:
   case ImplicitCastExprClass:
@@ -3569,6 +3574,9 @@ bool Expr::HasSideEffects(const ASTContext &Ctx,
     // These never have a side-effect.
     return false;
 
+  case PackIndexingExprClass:
+    return cast<PackIndexingExpr>(this)->getSelectedExpr()->HasSideEffects(
+        Ctx, IncludePossibleEffects);
   case ConstantExprClass:
     // FIXME: Move this into the "return false;" block above.
     return cast<ConstantExpr>(this)->getSubExpr()->HasSideEffects(
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index 83af7998f68338..df76a5796d25bd 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -1665,6 +1665,43 @@ NonTypeTemplateParmDecl *SubstNonTypeTemplateParmExpr::getParameter() const {
       getReplacedTemplateParameterList(getAssociatedDecl())->asArray()[Index]);
 }
 
+PackIndexingExpr *PackIndexingExpr::Create(ASTContext &Context,
+                                           SourceLocation EllipsisLoc,
+                                           SourceLocation RSquareLoc,
+                                           Expr *PackIdExpr, Expr *IndexExpr,
+                                           std::optional<int64_t> Index,
+                                           ArrayRef<Expr *> SubstitutedExprs) {
+  QualType Type;
+  if (Index && !SubstitutedExprs.empty())
+    Type = SubstitutedExprs[*Index]->getType();
+  else
+    Type = Context.DependentTy;
+
+  void *Storage =
+      Context.Allocate(totalSizeToAlloc<Expr *>(SubstitutedExprs.size()));
+  return new (Storage)
+      PackIndexingExpr(Type, EllipsisLoc, RSquareLoc, PackIdExpr, IndexExpr,
+                       Index, SubstitutedExprs);
+}
+
+NamedDecl *PackIndexingExpr::getPackDecl() const {
+  if (auto *D = dyn_cast<DeclRefExpr>(getPackIdExpression()); D) {
+    NamedDecl *ND = dyn_cast<NamedDecl>(D->getDecl());
+    assert(ND && "exected a named decl");
+    return ND;
+  }
+  assert(false && "Non variables packs not supported");
+  return nullptr;
+}
+
+PackIndexingExpr *
+PackIndexingExpr::CreateDeserialized(ASTContext &Context,
+                                     unsigned NumTransformedExprs) {
+  void *Storage =
+      Context.Allocate(totalSizeToAlloc<Expr *>(NumTransformedExprs));
+  return new (Storage) PackIndexingExpr(EmptyShell{});
+}
+
 QualType SubstNonTypeTemplateParmExpr::getParameterType(
     const ASTContext &Context) const {
   // Note that, for a class type NTTP, we will have an lvalue of type 'const
diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp
index ffa7c6802ea6e1..7026fca8554ce9 100644
--- a/clang/lib/AST/ExprClassification.cpp
+++ b/clang/lib/AST/ExprClassification.cpp
@@ -216,6 +216,9 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
     return ClassifyInternal(Ctx,
                  cast<SubstNonTypeTemplateParmExpr>(E)->getReplacement());
 
+  case Expr::PackIndexingExprClass:
+    return ClassifyInternal(Ctx, cast<PackIndexingExpr>(E)->getSelectedExpr());
+
     // C, C++98 [expr.sub]p1: The result is an lvalue of type "T".
     // C++11 (DR1213): in the case of an array operand, the result is an lvalue
     //                 if that operand is an lvalue and an xvalue otherwise.
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 373972eb6cab11..2c290904a0ad0e 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -8229,6 +8229,10 @@ class ExprEvaluatorBase
     llvm_unreachable("Return from function from the loop above.");
   }
 
+  bool VisitPackIndexingExpr(const PackIndexingExpr *E) {
+    return StmtVisitorTy::Visit(E->getSelectedExpr());
+  }
+
   /// Visit a value which is evaluated, but whose value is ignored.
   void VisitIgnoredValue(const Expr *E) {
     EvaluateIgnoredValue(Info, E);
@@ -16028,6 +16032,9 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) {
   case Expr::SourceLocExprClass:
     return NoDiag();
 
+  case Expr::PackIndexingExprClass:
+    return CheckICE(cast<PackIndexingExpr>(E)->getSelectedExpr(), Ctx);
+
   case Expr::SubstNonTypeTemplateParmExprClass:
     return
       CheckICE(cast<SubstNonTypeTemplateParmExpr>(E)->getReplacement(), Ctx);
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index 2a62ac0175afb7..13e2be98aec223 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -2448,6 +2448,7 @@ bool CXXNameMangler::mangleUnresolvedTypeOrSimpleId(QualType Ty,
   case Type::TypeOfExpr:
   case Type::TypeOf:
   case Type::Decltype:
+  case Type::PackIndexing:
   case Type::TemplateTypeParm:
   case Type::UnaryTransform:
   case Type::SubstTemplateTypeParm:
@@ -4193,6 +4194,13 @@ void CXXNameMangler::mangleType(const PackExpansionType *T) {
   mangleType(T->getPattern());
 }
 
+void CXXNameMangler::mangleType(const PackIndexingType *T) {
+  if (!T->hasSelectedType())
+    mangleType(T->getPattern());
+  else
+    mangleType(T->getSelectedType());
+}
+
 void CXXNameMangler::mangleType(const ObjCInterfaceType *T) {
   mangleSourceName(T->getDecl()->getIdentifier());
 }
@@ -4695,6 +4703,7 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity,
   case Expr::OMPIteratorExprClass:
   case Expr::CXXInheritedCtorInitExprClass:
   case Expr::CXXParenListInitExprClass:
+  case Expr::PackIndexingExprClass:
     llvm_unreachable("unexpected statement kind");
 
   case Expr::ConstantExprClass:
diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp
index 50ab6ea59be9d0..f045148e05d15b 100644
--- a/clang/lib/AST/MicrosoftMangle.cpp
+++ b/clang/lib/AST/MicrosoftMangle.cpp
@@ -3362,6 +3362,12 @@ void MicrosoftCXXNameMangler::mangleType(const PackExpansionType *T, Qualifiers,
     << Range;
 }
 
+void MicrosoftCXXNameMangler::mangleType(const PackIndexingType *T,
+                                         Qualifiers Quals, SourceRange Range) {
+  manglePointerCVQualifiers(Quals);
+  mangleType(T->getSelectedType(), Range);
+}
+
 void MicrosoftCXXNameMangler::mangleType(const TypeOfType *T, Qualifiers,
                                          SourceRange Range) {
   DiagnosticsEngine &Diags = Context.getDiags();
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index ab4a013de5f552..8fd42d0405ba25 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -2449,6 +2449,10 @@ void StmtPrinter::VisitSizeOfPackExpr(SizeOfPackExpr *E) {
   OS << "sizeof...(" << *E->getPack() << ")";
 }
 
+void StmtPrinter::VisitPackIndexingExpr(PackIndexingExpr *E) {
+  OS << E->getPackIdExpression() << "...[" << E->getIndexExpr() << "]";
+}
+
 void StmtPrinter::VisitSubstNonTypeTemplateParmPackExpr(
                                        SubstNonTypeTemplateParmPackExpr *Node) {
   OS << *Node->getParameterPack();
diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index 8128219dd6f63c..6d99cbf5740a45 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -2217,6 +2217,12 @@ void StmtProfiler::VisitSizeOfPackExpr(const SizeOfPackExpr *S) {
   }
 }
 
+void StmtProfiler::VisitPackIndexingExpr(const PackIndexingExpr *E) {
+  VisitExpr(E);
+  VisitExpr(E->getPackIdExpression());
+  VisitExpr(E->getIndexExpr());
+}
+
 void StmtProfiler::VisitSubstNonTypeTemplateParmPackExpr(
     const SubstNonTypeTemplateParmPackExpr *S) {
   VisitExpr(S);
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index c8e452e2feab0b..ab83e833f86939 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -3767,6 +3767,45 @@ void DependentDecltypeType::Profile(llvm::FoldingSetNodeID &ID,
   E->Profile(ID, Context, true);
 }
 
+PackIndexingType::PackIndexingType(const ASTContext &Context,
+                                   QualType Canonical, QualType Pattern,
+                                   Expr *IndexExpr,
+                                   ArrayRef<QualType> Expansions, int Index)
+    : Type(PackIndexing, Canonical,
+           computeDependence(Pattern, IndexExpr, Expansions)),
+      Context(Context), Pattern(Pattern), IndexExpr(IndexExpr),
+      Size(Expansions.size()), Index(Index) {
+
+  std::uninitialized_copy(Expansions.begin(), Expansions.end(),
+                          getTrailingObjects<QualType>());
+}
+
+TypeDependence
+PackIndexingType::computeDependence(QualType Pattern, Expr *IndexExpr,
+                                    ArrayRef<QualType> Expansions) {
+  TypeDependence IndexD = toTypeDependence(IndexExpr->getDependence());
+
+  TypeDependence TD = IndexD | (IndexExpr->isInstantiationDependent()
+                                    ? TypeDependence::DependentInstantiation
+                                    : TypeDependence::None);
+  if (Expansions.empty())
+    TD |= Pattern->getDependence() & TypeDependence::DependentInstantiation;
+  else
+    for (const QualType &T : Expansions)
+      TD |= T->getDependence();
+
+  if (!(IndexD & TypeDependence::UnexpandedPack))
+    TD &= ~TypeDependence::UnexpandedPack;
+  return TD;
+}
+
+void PackIndexingType::Profile(llvm::FoldingSetNodeID &ID,
+                               const ASTContext &Context, QualType Pattern,
+                               Expr *E) {
+  Pattern.Profile(ID);
+  E->Profile(ID, Context, true);
+}
+
 UnaryTransformType::UnaryTransformType(QualType BaseType,
                                        QualType UnderlyingType, UTTKind UKind,
                                        QualType CanonicalType)
@@ -4442,6 +4481,7 @@ bool Type::canHaveNullability(bool ResultIfUnknown) const {
   case Type::TypeOfExpr:
   case Type::TypeOf:
   case Type::Decltype:
+  case Type::PackIndexing:
   case Type::UnaryTransform:
   case Type::TemplateTypeParm:
   case Type::SubstTemplateTypeParmPack:
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index e4f5f40cd62599..673032fced19d5 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -284,6 +284,7 @@ bool TypePrinter::canPrefixQualifiers(const Type *T,
     case Type::FunctionNoProto:
     case Type::Paren:
     case Type::PackExpansion:
+
     case Type::SubstTemplateTypeParm:
     case Type::MacroQualified:
       CanPrefixQualifiers = false;
@@ -296,6 +297,11 @@ bool TypePrinter::canPrefixQualifiers(const Type *T,
       CanPrefixQualifiers = AttrTy->getAttrKind() == attr::AddressSpace;
       break;
     }
+    case Type::PackIndexing: {
+      return canPrefixQualifiers(
+          cast<PackIndexingType>(UnderlyingType)->getPattern().getTypePtr(),
+          NeedARCStrongQualifier);
+    }
   }
 
   return CanPrefixQualifiers;
@@ -1173,6 +1179,18 @@ void TypePrinter::printDecltypeBefore(const DecltypeType *T, raw_ostream &OS) {
   spaceBeforePlaceHolder(OS);
 }
 
+void TypePrinter::printPackIndexingBefore(const PackIndexingType *T,
+                                          raw_ostream &OS) {
+  if (T->isInstantiationDependentType())
+    OS << T->getPattern() << "...[" << T->getIndexExpr() << "]";
+  else
+    OS << T->getSelectedType();
+  spaceBeforePlaceHolder(OS);
+}
+
+void TypePrinter::printPackIndexingAfter(const PackIndexingType *T,
+                                         raw_ostream &OS) {}
+
 void TypePrinter::printDecltypeAfter(const DecltypeType *T, raw_ostream &OS) {}
 
 void TypePrinter::printUnaryTransformBefore(const UnaryTransformType *T,
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index 0b52d99ad07f16..6b344d7d92d514 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -3459,6 +3459,10 @@ static QualType UnwrapTypeForDebugInfo(QualType T, const ASTContext &C) {
       T = DT;
       break;
     }
+    case Type::PackIndexing: {
+      T = cast<PackIndexingType>(T)->getSelectedType();
+      break;
+    }
     case Type::Adjusted:
     case Type::Decayed:
       // Decayed and adjusted types use the adjusted type in LLVM and DWARF.
@@ -3642,6 +3646,7 @@ llvm::DIType *CGDebugInfo::CreateTypeNode(QualType Ty, llvm::DIFile *Unit) {
   case Type::TypeOfExpr:
   case Type::TypeOf:
   case Type::Decltype:
+  case Type::PackIndexing:
   case Type::UnaryTransform:
     break;
   }
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index a75f630e1a4c76..0abb1f00b4b421 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -1560,6 +1560,8 @@ LValue CodeGenFunction::EmitLValueHelper(const Expr *E,
     return EmitCoawaitLValue(cast<CoawaitExpr>(E));
   case Expr::CoyieldExprClass:
     return EmitCoyieldLValue(cast<CoyieldExpr>(E));
+  case Expr::PackIndexingExprClass:
+    return EmitLValue(cast<PackIndexingExpr>(E)->getSelectedExpr());
   }
 }
 
diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp
index 810b28f25fa18b..22f55fe9aac904 100644
--- a/clang/lib/CodeGen/CGExprAgg.cpp
+++ b/clang/lib/CodeGen/CGExprAgg.cpp
@@ -235,6 +235,9 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
     RValue Res = CGF.EmitAtomicExpr(E);
     EmitFinalDestCopy(E->getType(), Res);
   }
+  void VisitPackIndexingExpr(PackIndexingExpr *E) {
+    Visit(E->getSelectedExpr());
+  }
 };
 }  // end anonymous namespace.
 
diff --git a/clang/lib/CodeGen/CGExprComplex.cpp b/clang/lib/CodeGen/CGExprComplex.cpp
index f3cbd1d0451ebe..f9c2f95dc1c40c 100644
--- a/clang/lib/CodeGen/CGExprComplex.cpp
+++ b/clang/lib/CodeGen/CGExprComplex.cpp
@@ -350,6 +350,10 @@ class ComplexExprEmitter
   ComplexPairTy VisitAtomicExpr(AtomicExpr *E) {
     return CGF.EmitAtomicExpr(E).getComplexVal();
   }
+
+  ComplexPairTy VisitPackIndexingExpr(PackIndexingExpr *E) {
+    return Visit(E->getSelectedExpr());
+  }
 };
 }  // end anonymous namespace.
 
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp
index 69616dcc07efe1..7790617b4dcf82 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -1388,6 +1388,10 @@ class ConstExprEmitter :
     return nullptr;
   }
 
+  llvm::Constant *VisitPackIndexingExpr(PackIndexingExpr *E, QualType T) {
+    return Visit(E->getSelectedExpr(), T);
+  }
+
   // Utility methods
   llvm::Type *ConvertType(QualType T) {
     return CGM.getTypes().ConvertType(T);
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 378437364767f6..c8d20704e05aae 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -904,6 +904,9 @@ class ScalarExprEmitter
   }
   Value *VisitAsTypeExpr(AsTypeExpr *CE);
   Value *VisitAtomicExpr(AtomicExpr *AE);
+  Value *VisitPackIndexingExpr(PackIndexingExpr *E) {
+    return Visit(E->getSelectedExpr());
+  }
 };
 }  // end anonymous namespace.
 
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 64521ce7182eee..a61d9024d84fa6 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -2363,6 +2363,7 @@ void CodeGenFunction::EmitVariablyModifiedType(QualType type) {
     case Type::Decltype:
     case Type::Auto:
     case Type::DeducedTemplateSpecialization:
+    case Type::PackIndexing:
       // Stop walking: nothing to do.
       return;
 
diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp
index 573c90a36eeab3..518a168ee246fb 100644
--- a/clang/lib/Parse/ParseCXXInlineMethods.cpp
+++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp
@@ -978,6 +978,20 @@ bool Parser::ConsumeAndStoreFunctionPrologue(CachedTokens &Toks) {
       } else {
         break;
       }
+      // Pack indexing
+      if (getLangOpts().CPlusPlus26 && Tok.is(tok::ellipsis) &&
+          NextToken().is(tok::l_square)) {
+        Toks.push_back(Tok);
+        SourceLocation OpenLoc = ConsumeToken();
+        Toks.push_back(Tok);
+        ConsumeBracket();
+        if (!ConsumeAndStoreUntil(tok::r_square, Toks, /*StopAtSemi=*/true)) {
+          Diag(Tok.getLocation(), diag::err_expected) << tok::r_square;
+          Diag(OpenLoc, diag::note_matching) << tok::l_square;
+          return true;
+        }
+      }
+
     } while (Tok.is(tok::coloncolon));
 
     if (Tok.is(tok::code_completion)) {
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 06f2c8798b049f..37c8be5d8a50cd 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4442,6 +4442,10 @@ void Parser::ParseDeclarationSpecifiers(
       ParseDecltypeSpecifier(DS);
       continue;
 
+    case tok::annot_indexed_pack_type:
+      ParseIndexedTypeNamePack(DS);
+      continue;
+
     case tok::annot_pragma_pack:
       HandlePragmaPack();
       continue;
@@ -5737,6 +5741,7 @@ bool Parser::isDeclarationSpecifier(
 
     // C++11 decltype and constexpr.
   case tok::annot_decltype:
+  case tok::annot_indexed_pack_type:
   case tok::kw_constexpr:
 
     // C++20 consteval and constinit.
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 35b1a93a54a6aa..1987ae6bb0dc94 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -1196,6 +1196,95 @@ void Parser::AnnotateExistingDecltypeSpecifier(const DeclSpec &DS,
   PP.AnnotateCachedTokens(Tok);
 }
 
+SourceLocation Parser::ParseIndexedTypeNamePack(DeclSpec &DS) {
+  assert(Tok.isOneOf(tok::annot_indexed_pack_type, tok::identifier) &&
+         "Expected an identifier");
+
+  TypeResult Type;
+  SourceLocation StartLoc;
+  SourceLocation EllipsisLoc;
+  const char *PrevSpec;
+  unsigned DiagID;
+  const PrintingPolicy &Policy = Actions.getASTContext().getPrintingPolicy();
+
+  if (Tok.is(tok::annot_indexed_pack_type)) {
+    StartLoc = Tok.getLocation();
+    SourceLocation EndLoc;
+    Type = getTypeAnnotation(Tok);
+    EndLoc = Tok.getAnnotationEndLoc();
+    // Unfortunately, we don't know the LParen source location as the annotated
+    // token doesn't have it.
+    DS.setTypeArgumentRange(SourceRange(SourceLocation(), EndLoc));
+    ConsumeAnnotationToken();
+    if (Type.isInvalid()) {
+      DS.SetTypeSpecError();
+      return EndLoc;
+    }
+    DS.SetTypeSpecType(DeclSpec::TST_indexed_typename_pack, StartLoc, PrevSpec,
+                       DiagID, Type, Policy);
+    return EndLoc;
+  } else {
+    if (!NextToken().is(tok::ellipsis) ||
+        !GetLookAheadToken(2).is(tok::l_square)) {
+      DS.SetTypeSpecError();
+      return Tok.getEndLoc();
+    }
+
+    ParsedType Ty = Actions.getTypeName(*Tok.getIdentifierInfo(),
+                                        Tok.getLocation(), getCurScope());
+    if (!Ty) {
+      DS.SetTypeSpecError();
+      return Tok.getEndLoc();
+    }
+    Type = Ty;
+
+    StartLoc = ConsumeToken();
+    EllipsisLoc = ConsumeToken();
+    BalancedDelimiterTracker T(*this, tok::l_square);
+    T.consumeOpen();
+    ExprResult IndexExpr = ParseConstantExpression();
+    if (T.consumeClose()) {
+      DS.SetTypeSpecError();
+      return IndexExpr.isInvalid() ? StartLoc : IndexExpr.get()->getEndLoc();
+    }
+    if (IndexExpr.isInvalid()) {
+      DS.SetTypeSpecError();
+      return T.getCloseLocation();
+    }
+    DS.SetRangeStart(StartLoc);
+    DS.SetRangeEnd(T.getCloseLocation());
+    DS.SetTypeSpecType(DeclSpec::TST_typename, StartLoc, PrevSpec, DiagID, Type,
+                       Policy);
+    DS.SetPackIndexingExpr(EllipsisLoc, IndexExpr.get());
+    return T.getCloseLocation();
+    ;
+  }
+  return SourceLocation();
+}
+
+void Parser::AnnotateExistingIndexedTypeNamePack(ParsedType T,
+                                                 SourceLocation StartLoc,
+                                                 SourceLocation EndLoc) {
+  // make sure we have a token we can turn into an annotation token
+  if (PP.isBacktrackEnabled()) {
+    PP.RevertCachedTokens(1);
+    if (!T) {
+      // We encountered an error in parsing 'decltype(...)' so lets annotate all
+      // the tokens in the backtracking cache - that we likely had to skip over
+      // to get to a token that allows us to resume parsing, such as a
+      // semi-colon.
+      EndLoc = PP.getLastCachedTokenLocation();
+    }
+  } else
+    PP.EnterToken(Tok, /*IsReinject*/ true);
+
+  Tok.setKind(tok::annot_indexed_pack_type);
+  setTypeAnnotation(Tok, T);
+  Tok.setAnnotationEndLoc(EndLoc);
+  Tok.setLocation(StartLoc);
+  PP.AnnotateCachedTokens(Tok);
+}
+
 DeclSpec::TST Parser::TypeTransformTokToDeclSpec() {
   switch (Tok.getKind()) {
 #define TRANSFORM_TYPE_TRAIT_DEF(_, Trait)                                     \
@@ -1293,6 +1382,14 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc,
     return Actions.ActOnTypeName(getCurScope(), DeclaratorInfo);
   }
 
+  if (Tok.is(tok::annot_indexed_pack_type)) {
+    DeclSpec DS(AttrFactory);
+    ParseIndexedTypeNamePack(DS);
+    Declarator DeclaratorInfo(DS, ParsedAttributesView::none(),
+                              DeclaratorContext::TypeName);
+    return Actions.ActOnTypeName(getCurScope(), DeclaratorInfo);
+  }
+
   // Check whether we have a template-id that names a type.
   if (Tok.is(tok::annot_template_id)) {
     TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok);
@@ -3826,6 +3923,10 @@ MemInitResult Parser::ParseMemInitializer(Decl *ConstructorDecl) {
     // ParseOptionalCXXScopeSpecifier at this point.
     // FIXME: Can we get here with a scope specifier?
     ParseDecltypeSpecifier(DS);
+  } else if (Tok.is(tok::annot_indexed_pack_type)) {
+    // Uses of T...[N] will already have been converted to
+    // annot_indexed_pack_type by ParseOptionalCXXScopeSpecifier at this point.
+    ParseIndexedTypeNamePack(DS);
   } else {
     TemplateIdAnnotation *TemplateId = Tok.is(tok::annot_template_id)
                                            ? takeTemplateIdAnnotation(Tok)
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index 89781055797615..aa5c83f3a64193 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -1067,6 +1067,17 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
       // where the syntax forbids a type.
       const Token &Next = NextToken();
 
+      if (getLangOpts().CPlusPlus26 && Next.is(tok::ellipsis) &&
+          Tok.is(tok::identifier) && GetLookAheadToken(2).is(tok::l_square)) {
+        // Annotate the token and tail recurse.
+        // If the token is not annotated, then it might be an expression pack
+        // indexing
+        if (!TryAnnotateTypeOrScopeToken() &&
+            Tok.is(tok::annot_indexed_pack_type))
+          return ParseCastExpression(ParseKind, isAddressOfOperand, isTypeCast,
+                                     isVectorLiteral, NotPrimaryExpression);
+      }
+
       // If this identifier was reverted from a token ID, and the next token
       // is a parenthesis, this is likely to be a use of a type trait. Check
       // those tokens.
@@ -1290,6 +1301,7 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
                                  /*isVectorLiteral=*/false,
                                  NotPrimaryExpression);
     }
+    Res = tryParseCXXPackIndexingExpression(Res);
     if (!Res.isInvalid() && Tok.is(tok::less))
       checkPotentialAngleBracket(Res);
     break;
@@ -1550,6 +1562,7 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
     [[fallthrough]];
 
   case tok::annot_decltype:
+  case tok::annot_indexed_pack_type:
   case tok::kw_char:
   case tok::kw_wchar_t:
   case tok::kw_char8_t:
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 79db094e098f8e..236418291d549a 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -233,6 +233,32 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
     HasScopeSpecifier = true;
   }
 
+  else if (getLangOpts().CPlusPlus26 && !HasScopeSpecifier &&
+           Tok.is(tok::identifier) && GetLookAheadToken(1).is(tok::ellipsis) &&
+           GetLookAheadToken(2).is(tok::l_square)) {
+    SourceLocation Start = Tok.getLocation();
+    DeclSpec DS(AttrFactory);
+    SourceLocation CCLoc;
+    SourceLocation EndLoc = ParseIndexedTypeNamePack(DS);
+    if (DS.getTypeSpecType() == DeclSpec::TST_error)
+      return false;
+    QualType Type = Actions.ActOnPackIndexingType(
+        DS.getRepAsType().get(), DS.getPackIndexingExpr(), DS.getBeginLoc(),
+        DS.getEllipsisLoc());
+    if (Type.isNull())
+      return true;
+
+    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;
+  }
+
   // Preferred type might change when parsing qualifiers, we need the original.
   auto SavedType = PreferredType;
   while (true) {
@@ -617,11 +643,39 @@ ExprResult Parser::tryParseCXXIdExpression(CXXScopeSpec &SS,
     break;
   }
 
+  // Might be a pack index expression!
+  if (getLangOpts().CPlusPlus26)
+    E = tryParseCXXPackIndexingExpression(E);
+
   if (!E.isInvalid() && !E.isUnset() && Tok.is(tok::less))
     checkPotentialAngleBracket(E);
   return E;
 }
 
+ExprResult Parser::ParseCXXPackIndexingExpression(ExprResult PackIdExpression) {
+  assert(Tok.is(tok::ellipsis) && NextToken().is(tok::l_square) &&
+         "expected ...[");
+  SourceLocation EllipsisLoc = ConsumeToken();
+  BalancedDelimiterTracker T(*this, tok::l_square);
+  T.consumeOpen();
+  ExprResult IndexExpr = ParseConstantExpression();
+  if (T.consumeClose() || IndexExpr.isInvalid())
+    return ExprError();
+  return Actions.ActOnPackIndexingExpr(getCurScope(), PackIdExpression.get(),
+                                       EllipsisLoc, T.getOpenLocation(),
+                                       IndexExpr.get(), T.getCloseLocation());
+}
+
+ExprResult
+Parser::tryParseCXXPackIndexingExpression(ExprResult PackIdExpression) {
+  ExprResult E = PackIdExpression;
+  if (!PackIdExpression.isInvalid() && !PackIdExpression.isUnset() &&
+      Tok.is(tok::ellipsis) && NextToken().is(tok::l_square)) {
+    E = ParseCXXPackIndexingExpression(E);
+  }
+  return E;
+}
+
 /// ParseCXXIdExpression - Handle id-expression.
 ///
 ///       id-expression:
@@ -1821,6 +1875,15 @@ Parser::ParseCXXPseudoDestructor(Expr *Base, SourceLocation OpLoc,
     return ExprError();
   }
 
+  // pack-index-specifier
+  if (getLangOpts().CPlusPlus26 && GetLookAheadToken(1).is(tok::ellipsis) &&
+      GetLookAheadToken(2).is(tok::l_square)) {
+    DeclSpec DS(AttrFactory);
+    ParseIndexedTypeNamePack(DS);
+    return Actions.ActOnPseudoDestructorExpr(getCurScope(), Base, OpLoc, OpKind,
+                                             TildeLoc, DS);
+  }
+
   // Parse the second type.
   UnqualifiedId SecondTypeName;
   IdentifierInfo *Name = Tok.getIdentifierInfo();
@@ -2264,7 +2327,6 @@ void Parser::ParseCXXSimpleTypeSpecifier(DeclSpec &DS) {
                        getTypeAnnotation(Tok), Policy);
     DS.SetRangeEnd(Tok.getAnnotationEndLoc());
     ConsumeAnnotationToken();
-
     DS.Finish(Actions, Policy);
     return;
   }
@@ -2375,6 +2437,10 @@ void Parser::ParseCXXSimpleTypeSpecifier(DeclSpec &DS) {
     DS.SetRangeEnd(ParseDecltypeSpecifier(DS));
     return DS.Finish(Actions, Policy);
 
+  case tok::annot_indexed_pack_type:
+    DS.SetRangeEnd(ParseIndexedTypeNamePack(DS));
+    return DS.Finish(Actions, Policy);
+
   // GNU typeof support.
   case tok::kw_typeof:
     ParseTypeofSpecifier(DS);
diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp
index 8b653b1c4f8eaf..521bed781855a1 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -1362,6 +1362,16 @@ Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename,
   };
   switch (Tok.getKind()) {
   case tok::identifier: {
+    if (GetLookAheadToken(1).is(tok::ellipsis) &&
+        GetLookAheadToken(2).is(tok::l_square)) {
+      if (TryAnnotateTypeOrScopeToken())
+        return TPResult::Error;
+      if (Tok.is(tok::identifier))
+        return TPResult::False;
+      return isCXXDeclarationSpecifier(ImplicitTypenameContext::No,
+                                       BracedCastResult, InvalidAsDeclSpec);
+    }
+
     // Check for need to substitute AltiVec __vector keyword
     // for "vector" identifier.
     if (TryAltiVecVectorToken())
@@ -1751,6 +1761,7 @@ Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename,
 
       return TPResult::True;
     }
+
     [[fallthrough]];
 
   case tok::kw_char:
@@ -1778,6 +1789,7 @@ Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename,
   case tok::kw__Accum:
   case tok::kw__Fract:
   case tok::kw__Sat:
+  case tok::annot_indexed_pack_type:
 #define GENERIC_IMAGE_TYPE(ImgType, Id) case tok::kw_##ImgType##_t:
 #include "clang/Basic/OpenCLImageTypes.def"
     if (NextToken().is(tok::l_paren))
@@ -1856,6 +1868,7 @@ bool Parser::isCXXDeclarationSpecifierAType() {
   switch (Tok.getKind()) {
     // typename-specifier
   case tok::annot_decltype:
+  case tok::annot_indexed_pack_type:
   case tok::annot_template_id:
   case tok::annot_typename:
   case tok::kw_typeof:
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 176d2149e73184..0fd25263e14599 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -1975,7 +1975,8 @@ bool Parser::TryAnnotateTypeOrScopeToken(
   assert((Tok.is(tok::identifier) || Tok.is(tok::coloncolon) ||
           Tok.is(tok::kw_typename) || Tok.is(tok::annot_cxxscope) ||
           Tok.is(tok::kw_decltype) || Tok.is(tok::annot_template_id) ||
-          Tok.is(tok::kw___super) || Tok.is(tok::kw_auto)) &&
+          Tok.is(tok::kw___super) || Tok.is(tok::kw_auto) ||
+          Tok.is(tok::annot_indexed_pack_type)) &&
          "Cannot be a type or scope token!");
 
   if (Tok.is(tok::kw_typename)) {
diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp
index 781f24cb71ae99..2a559561c810c4 100644
--- a/clang/lib/Sema/DeclSpec.cpp
+++ b/clang/lib/Sema/DeclSpec.cpp
@@ -374,6 +374,7 @@ bool Declarator::isDeclarationOfFunction() const {
     case TST_void:
     case TST_wchar:
     case TST_BFloat16:
+    case TST_indexed_typename_pack:
 #define GENERIC_IMAGE_TYPE(ImgType, Id) case TST_##ImgType##_t:
 #include "clang/Basic/OpenCLImageTypes.def"
       return false;
@@ -585,6 +586,8 @@ const char *DeclSpec::getSpecifierName(DeclSpec::TST T,
   case DeclSpec::TST_struct:      return "struct";
   case DeclSpec::TST_interface:   return "__interface";
   case DeclSpec::TST_typename:    return "type-name";
+  case DeclSpec::TST_indexed_typename_pack:
+    return "type-name-pack-indexing";
   case DeclSpec::TST_typeofType:
   case DeclSpec::TST_typeofExpr:  return "typeof";
   case DeclSpec::TST_typeof_unqualType:
@@ -775,6 +778,15 @@ bool DeclSpec::SetTypeSpecType(TST T, SourceLocation TagKwLoc,
   TSTLoc = TagKwLoc;
   TSTNameLoc = TagNameLoc;
   TypeSpecOwned = false;
+
+  if (T == TST_indexed_typename_pack) {
+    // we got there from a an annotation. Reconstruct the type
+    // Ugly...
+    QualType QT = Rep.get();
+    const PackIndexingType *LIT = cast<PackIndexingType>(QT);
+    TypeRep = ParsedType::make(LIT->getPattern());
+    PackIndexingExpr = LIT->getIndexExpr();
+  }
   return false;
 }
 
@@ -973,6 +985,15 @@ bool DeclSpec::SetBitIntType(SourceLocation KWLoc, Expr *BitsExpr,
   return false;
 }
 
+void DeclSpec::SetPackIndexingExpr(SourceLocation EllipsisLoc,
+                                   Expr *IndexingExpr) {
+  assert(TypeSpecType == TST_typename &&
+         "pack indexing can only be applied to typename");
+  TypeSpecType = TST_indexed_typename_pack;
+  PackIndexingExpr = IndexingExpr;
+  this->EllipsisLoc = EllipsisLoc;
+}
+
 bool DeclSpec::SetTypeQual(TQ T, SourceLocation Loc, const char *&PrevSpec,
                            unsigned &DiagID, const LangOptions &Lang) {
   // Duplicates are permitted in C99 onwards, but are not permitted in C89 or
diff --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp
index 44a40215b90dfb..98833e931c6dad 100644
--- a/clang/lib/Sema/SemaCXXScopeSpec.cpp
+++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp
@@ -867,6 +867,29 @@ bool Sema::ActOnCXXNestedNameSpecifierDecltype(CXXScopeSpec &SS,
   return false;
 }
 
+bool Sema::ActOnCXXNestedNameSpecifierIndexedPack(CXXScopeSpec &SS,
+                                                  const DeclSpec &DS,
+                                                  SourceLocation ColonColonLoc,
+                                                  QualType Type) {
+  if (SS.isInvalid() || DS.getTypeSpecType() == DeclSpec::TST_error)
+    return true;
+
+  assert(DS.getTypeSpecType() == DeclSpec::TST_indexed_typename_pack);
+
+  if (Type.isNull())
+    return true;
+
+  TypeLocBuilder TLB;
+  TLB.pushTrivial(getASTContext(),
+                  cast<PackIndexingType>(Type.getTypePtr())->getPattern(),
+                  DS.getBeginLoc());
+  PackIndexingTypeLoc PIT = TLB.push<PackIndexingTypeLoc>(Type);
+  PIT.setEllipsisLoc(DS.getEllipsisLoc());
+  SS.Extend(Context, SourceLocation(), TLB.getTypeLocInContext(Context, Type),
+            ColonColonLoc);
+  return false;
+}
+
 /// IsInvalidUnlessNestedName - This method is used for error recovery
 /// purposes to determine whether the specified identifier is only valid as
 /// a nested name specifier, for example a namespace name.  It is
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index a6cd0bb9ea2a82..3ecf9eb65289f5 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -158,6 +158,7 @@ bool Sema::isSimpleTypeSpecifier(tok::TokenKind Kind) const {
   case tok::kw_char32_t:
   case tok::kw_typeof:
   case tok::annot_decltype:
+  case tok::annot_indexed_pack_type:
   case tok::kw_decltype:
     return getLangOpts().CPlusPlus;
 
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 3688192e6cbe5c..a198913897b07e 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -4494,6 +4494,10 @@ Sema::BuildMemInitializer(Decl *ConstructorD,
   } else if (DS.getTypeSpecType() == TST_decltype_auto) {
     Diag(DS.getTypeSpecTypeLoc(), diag::err_decltype_auto_invalid);
     return true;
+  } else if (DS.getTypeSpecType() == TST_indexed_typename_pack) {
+    BaseType =
+        ActOnPackIndexingType(DS.getRepAsType().get(), DS.getPackIndexingExpr(),
+                              DS.getBeginLoc(), DS.getEllipsisLoc());
   } else {
     LookupResult R(*this, MemberOrBase, IdLoc, LookupOrdinaryName);
     LookupParsedName(R, S, &SS);
diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp
index 75730ea888afb4..8d58ef5ee16d52 100644
--- a/clang/lib/Sema/SemaExceptionSpec.cpp
+++ b/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -1410,6 +1410,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
   case Expr::OpaqueValueExprClass:
   case Expr::PredefinedExprClass:
   case Expr::SizeOfPackExprClass:
+  case Expr::PackIndexingExprClass:
   case Expr::StringLiteralClass:
   case Expr::SourceLocExprClass:
   case Expr::ConceptSpecializationExprClass:
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index fc39d6149c1cc6..acf627ff0363bd 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -4709,6 +4709,9 @@ static void captureVariablyModifiedType(ASTContext &Context, QualType T,
     case Type::Decltype:
       T = cast<DecltypeType>(Ty)->desugar();
       break;
+    case Type::PackIndexing:
+      T = cast<PackIndexingType>(Ty)->desugar();
+      break;
     case Type::Using:
       T = cast<UsingType>(Ty)->desugar();
       break;
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 023411c7edc946..75dc91518696f6 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -8072,20 +8072,36 @@ ExprResult Sema::ActOnPseudoDestructorExpr(Scope *S, Expr *Base,
                                            SourceLocation TildeLoc,
                                            const DeclSpec& DS) {
   QualType ObjectType;
+  QualType T;
+  TypeLocBuilder TLB;
   if (CheckArrow(*this, ObjectType, Base, OpKind, OpLoc))
     return ExprError();
 
-  if (DS.getTypeSpecType() == DeclSpec::TST_decltype_auto) {
+  switch (DS.getTypeSpecType()) {
+  case DeclSpec::TST_decltype_auto: {
     Diag(DS.getTypeSpecTypeLoc(), diag::err_decltype_auto_invalid);
     return true;
   }
-
-  QualType T = BuildDecltypeType(DS.getRepAsExpr(), /*AsUnevaluated=*/false);
-
-  TypeLocBuilder TLB;
-  DecltypeTypeLoc DecltypeTL = TLB.push<DecltypeTypeLoc>(T);
-  DecltypeTL.setDecltypeLoc(DS.getTypeSpecTypeLoc());
-  DecltypeTL.setRParenLoc(DS.getTypeofParensRange().getEnd());
+  case DeclSpec::TST_decltype: {
+    T = BuildDecltypeType(DS.getRepAsExpr(), /*AsUnevaluated=*/false);
+    DecltypeTypeLoc DecltypeTL = TLB.push<DecltypeTypeLoc>(T);
+    DecltypeTL.setDecltypeLoc(DS.getTypeSpecTypeLoc());
+    DecltypeTL.setRParenLoc(DS.getTypeofParensRange().getEnd());
+    break;
+  }
+  case DeclSpec::TST_indexed_typename_pack: {
+    T = ActOnPackIndexingType(DS.getRepAsType().get(), DS.getPackIndexingExpr(),
+                              DS.getBeginLoc(), DS.getEllipsisLoc());
+    TLB.pushTrivial(getASTContext(),
+                    cast<PackIndexingType>(T.getTypePtr())->getPattern(),
+                    DS.getBeginLoc());
+    PackIndexingTypeLoc PITL = TLB.push<PackIndexingTypeLoc>(T);
+    PITL.setEllipsisLoc(DS.getEllipsisLoc());
+    break;
+  }
+  default:
+    llvm_unreachable("Unsupported type in pseudo destructor");
+  }
   TypeSourceInfo *DestructedTypeInfo = TLB.getTypeSourceInfo(Context, T);
   PseudoDestructorTypeStorage Destructed(DestructedTypeInfo);
 
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 766ebd90fded0c..bbd4c252552d42 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -6372,6 +6372,11 @@ bool UnnamedLocalNoLinkageFinder::VisitDecltypeType(const DecltypeType*) {
   return false;
 }
 
+bool UnnamedLocalNoLinkageFinder::VisitPackIndexingType(
+    const PackIndexingType *) {
+  return false;
+}
+
 bool UnnamedLocalNoLinkageFinder::VisitUnaryTransformType(
                                                     const UnaryTransformType*) {
   return false;
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 699e0985e595b6..c2279e96be67ef 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -636,6 +636,7 @@ static bool IsPossiblyOpaquelyQualifiedTypeInternal(const Type *T) {
   case Type::TypeOf:
   case Type::DependentName:
   case Type::Decltype:
+  case Type::PackIndexing:
   case Type::UnresolvedUsing:
   case Type::TemplateTypeParm:
     return true;
@@ -2234,6 +2235,15 @@ static Sema::TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
     case Type::Pipe:
       // No template argument deduction for these types
       return Sema::TDK_Success;
+
+    case Type::PackIndexing: {
+      const PackIndexingType *PIT = P->getAs<PackIndexingType>();
+      if (PIT->hasSelectedType()) {
+        return DeduceTemplateArgumentsByTypeMatch(
+            S, TemplateParams, PIT->getSelectedType(), A, Info, Deduced, TDF);
+      }
+      return Sema::TDK_IncompletePack;
+    }
     }
 
   llvm_unreachable("Invalid Type Class!");
@@ -6380,6 +6390,12 @@ MarkUsedTemplateParameters(ASTContext &Ctx, QualType T,
                                  OnlyDeduced, Depth, Used);
     break;
 
+  case Type::PackIndexing:
+    if (!OnlyDeduced)
+      MarkUsedTemplateParameters(Ctx, cast<PackIndexingType>(T)->getPattern(),
+                                 OnlyDeduced, Depth, Used);
+    break;
+
   case Type::UnaryTransform:
     if (!OnlyDeduced)
       MarkUsedTemplateParameters(Ctx,
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index 0f4e9e7f94c81e..3b87becc156b8e 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -184,6 +184,15 @@ namespace {
     bool TraversePackExpansionTypeLoc(PackExpansionTypeLoc TL) { return true; }
     bool TraversePackExpansionExpr(PackExpansionExpr *E) { return true; }
     bool TraverseCXXFoldExpr(CXXFoldExpr *E) { return true; }
+    bool TraversePackIndexingExpr(PackIndexingExpr *E) {
+      return inherited::TraverseStmt(E->getIndexExpr());
+    }
+    bool TraversePackIndexingType(PackIndexingType *E) {
+      return inherited::TraverseStmt(E->getIndexExpr());
+    }
+    bool TraversePackIndexingTypeLoc(PackIndexingTypeLoc TL) {
+      return inherited::TraverseStmt(TL.getIndexExpr());
+    }
 
     ///@}
 
@@ -865,6 +874,7 @@ std::optional<unsigned> Sema::getNumArgumentsInExpansion(
 bool Sema::containsUnexpandedParameterPacks(Declarator &D) {
   const DeclSpec &DS = D.getDeclSpec();
   switch (DS.getTypeSpecType()) {
+  case TST_indexed_typename_pack:
   case TST_typename:
   case TST_typeof_unqualType:
   case TST_typeofType:
@@ -1050,8 +1060,7 @@ ExprResult Sema::ActOnSizeofParameterPackExpr(Scope *S,
   }
 
   if (!ParameterPack || !ParameterPack->isParameterPack()) {
-    Diag(NameLoc, diag::err_sizeof_pack_no_pack_name)
-      << &Name;
+    Diag(NameLoc, diag::err_expected_name_of_pack) << &Name;
     return ExprError();
   }
 
@@ -1061,6 +1070,59 @@ ExprResult Sema::ActOnSizeofParameterPackExpr(Scope *S,
                                 RParenLoc);
 }
 
+static bool isParameterPack(Expr *PackExpression) {
+  if (auto D = dyn_cast<DeclRefExpr>(PackExpression); D) {
+    ValueDecl *VD = D->getDecl();
+    return VD->isParameterPack();
+  }
+  return false;
+}
+
+ExprResult Sema::ActOnPackIndexingExpr(Scope *S, Expr *PackExpression,
+                                       SourceLocation EllipsisLoc,
+                                       SourceLocation LSquareLoc,
+                                       Expr *IndexExpr,
+                                       SourceLocation RSquareLoc) {
+  bool isParameterPack = ::isParameterPack(PackExpression);
+  if (!isParameterPack) {
+    CorrectDelayedTyposInExpr(IndexExpr);
+    Diag(PackExpression->getBeginLoc(), diag::err_expected_name_of_pack)
+        << PackExpression;
+    return ExprError();
+  }
+  return BuildPackIndexingExpr(PackExpression, EllipsisLoc, IndexExpr,
+                               RSquareLoc);
+}
+
+ExprResult
+Sema::BuildPackIndexingExpr(Expr *PackExpression, SourceLocation EllipsisLoc,
+                            Expr *IndexExpr, SourceLocation RSquareLoc,
+                            ArrayRef<Expr *> ExpandedExprs, bool EmptyPack) {
+
+  std::optional<int64_t> Index;
+  if (!IndexExpr->isValueDependent() && !IndexExpr->isTypeDependent()) {
+    llvm::APSInt Value(Context.getIntWidth(Context.getSizeType()));
+    // TODO: do we need a new enumerator instead of CCEK_ArrayBound?
+    ExprResult Res = CheckConvertedConstantExpression(
+        IndexExpr, Context.getSizeType(), Value, CCEK_ArrayBound);
+    if (!Res.isUsable())
+      return ExprError();
+    Index = Value.getExtValue();
+  }
+
+  if (Index && (!ExpandedExprs.empty() || EmptyPack)) {
+    if (*Index < 0 || EmptyPack || *Index >= int64_t(ExpandedExprs.size())) {
+      Diag(PackExpression->getBeginLoc(), diag::err_pack_index_out_of_bound)
+          << *Index << PackExpression << ExpandedExprs.size();
+      return ExprError();
+    }
+  }
+
+  return PackIndexingExpr::Create(getASTContext(), EllipsisLoc, RSquareLoc,
+                                  PackExpression, IndexExpr, Index,
+                                  ExpandedExprs);
+}
+
 TemplateArgumentLoc Sema::getTemplateArgumentPackExpansionPattern(
     TemplateArgumentLoc OrigLoc, SourceLocation &Ellipsis,
     std::optional<unsigned> &NumExpansions) const {
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 560feafa1857cb..425ec618476931 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -1665,6 +1665,19 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) {
     }
     break;
   }
+  case DeclSpec::TST_indexed_typename_pack: {
+    Expr *E = DS.getPackIndexingExpr();
+    assert(E && "Didn't get an expression for pack indexing");
+    QualType Pattern = S.GetTypeFromParser(DS.getRepAsType());
+    Result = S.ActOnPackIndexingType(Pattern, E, DS.getBeginLoc(),
+                                     DS.getEllipsisLoc());
+    if (Result.isNull()) {
+      declarator.setInvalidType(true);
+      Result = Context.IntTy;
+    }
+    break;
+  }
+
 #define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) case DeclSpec::TST_##Trait:
 #include "clang/Basic/TransformTypeTraits.def"
     Result = S.GetTypeFromParser(DS.getRepAsType());
@@ -6310,6 +6323,10 @@ namespace {
       TL.setDecltypeLoc(DS.getTypeSpecTypeLoc());
       TL.setRParenLoc(DS.getTypeofParensRange().getEnd());
     }
+    void VisitPackIndexingTypeLoc(PackIndexingTypeLoc TL) {
+      assert(DS.getTypeSpecType() == DeclSpec::TST_indexed_typename_pack);
+      TL.setEllipsisLoc(DS.getEllipsisLoc());
+    }
     void VisitUnaryTransformTypeLoc(UnaryTransformTypeLoc TL) {
       assert(DS.isTransformTypeTrait(DS.getTypeSpecType()));
       TL.setKWLoc(DS.getTypeSpecTypeLoc());
@@ -9587,6 +9604,9 @@ QualType Sema::getDecltypeForExpr(Expr *E) {
   if (auto *ImplCastExpr = dyn_cast<ImplicitCastExpr>(E))
     IDExpr = ImplCastExpr->getSubExpr();
 
+  if (auto *PackExpr = dyn_cast<PackIndexingExpr>(E))
+    IDExpr = PackExpr->getSelectedExpr();
+
   // C++11 [dcl.type.simple]p4:
   //   The type denoted by decltype(e) is defined as follows:
 
@@ -9658,6 +9678,46 @@ QualType Sema::BuildDecltypeType(Expr *E, bool AsUnevaluated) {
   return Context.getDecltypeType(E, getDecltypeForExpr(E));
 }
 
+QualType Sema::ActOnPackIndexingType(QualType Pattern, Expr *IndexExpr,
+                                     SourceLocation Loc,
+                                     SourceLocation EllipsisLoc) {
+  if (!Pattern->containsUnexpandedParameterPack()) {
+    Diag(EllipsisLoc, diag::err_expected_name_of_pack) << Pattern;
+    return QualType();
+  }
+  return BuildPackIndexingType(Pattern, IndexExpr, Loc, EllipsisLoc);
+}
+
+QualType Sema::BuildPackIndexingType(QualType Pattern, Expr *IndexExpr,
+                                     SourceLocation Loc,
+                                     SourceLocation EllipsisLoc,
+                                     bool FullyExpanded,
+                                     ArrayRef<QualType> Expansions) {
+
+  std::optional<int64_t> Index;
+  if (FullyExpanded && !IndexExpr->isValueDependent() &&
+      !IndexExpr->isTypeDependent()) {
+    llvm::APSInt Value(Context.getIntWidth(Context.getSizeType()));
+    // TODO: do we need a new enumerator instead of CCEK_ArrayBound?
+    ExprResult Res = CheckConvertedConstantExpression(
+        IndexExpr, Context.getSizeType(), Value, CCEK_ArrayBound);
+    if (!Res.isUsable())
+      return QualType();
+    Index = Value.getExtValue();
+  }
+
+  if (FullyExpanded && Index) {
+    if (*Index < 0 || *Index >= int64_t(Expansions.size())) {
+      Diag(IndexExpr->getBeginLoc(), diag::err_pack_index_out_of_bound)
+          << *Index << Pattern << Expansions.size();
+      return QualType();
+    }
+  }
+
+  return Context.getPackIndexingType(Pattern, IndexExpr, FullyExpanded,
+                                     Expansions, Index.value_or(-1));
+}
+
 static QualType GetEnumUnderlyingType(Sema &S, QualType BaseType,
                                       SourceLocation Loc) {
   assert(BaseType->isEnumeralType());
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index e24f710fdedd4e..f7b9d6b8f86d75 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -1043,6 +1043,12 @@ class TreeTransform {
   /// Subclasses may override this routine to provide different behavior.
   QualType RebuildDecltypeType(Expr *Underlying, SourceLocation Loc);
 
+  QualType RebuildPackIndexingType(QualType Pattern, Expr *IndexExpr,
+                                   SourceLocation Loc,
+                                   SourceLocation EllipsisLoc,
+                                   bool FullyExpanded,
+                                   ArrayRef<QualType> Expansions = {});
+
   /// Build a new C++11 auto type.
   ///
   /// By default, builds a new AutoType with the given deduced type.
@@ -3576,6 +3582,16 @@ class TreeTransform {
                                   RParenLoc, Length, PartialArgs);
   }
 
+  ExprResult RebuildPackIndexingExpr(SourceLocation EllipsisLoc,
+                                     SourceLocation RSquareLoc,
+                                     Expr *PackIdExpression, Expr *IndexExpr,
+                                     ArrayRef<Expr *> ExpandedExprs,
+                                     bool EmptyPack = false) {
+    return getSema().BuildPackIndexingExpr(PackIdExpression, EllipsisLoc,
+                                           IndexExpr, RSquareLoc, ExpandedExprs,
+                                           EmptyPack);
+  }
+
   /// Build a new expression representing a call to a source location
   ///  builtin.
   ///
@@ -6460,6 +6476,100 @@ QualType TreeTransform<Derived>::TransformDecltypeType(TypeLocBuilder &TLB,
   return Result;
 }
 
+template <typename Derived>
+QualType
+TreeTransform<Derived>::TransformPackIndexingType(TypeLocBuilder &TLB,
+                                                  PackIndexingTypeLoc TL) {
+  // Transform the index
+  ExprResult IndexExpr = getDerived().TransformExpr(TL.getIndexExpr());
+  if (IndexExpr.isInvalid())
+    return QualType();
+  QualType Pattern = TL.getPattern();
+
+  const PackIndexingType *PIT = TL.getTypePtr();
+  SmallVector<QualType, 5> ExpandedTypes;
+  llvm::ArrayRef<QualType> Types = PIT->getExpansions();
+
+  bool NotYetExpanded = Types.empty();
+  bool FullyExpanded = true;
+
+  if (Types.empty())
+    Types = llvm::ArrayRef<QualType>(&Pattern, 1);
+
+  for (const QualType &T : Types) {
+    if (!T->containsUnexpandedParameterPack()) {
+      QualType Transformed = getDerived().TransformType(T);
+      if (Transformed.isNull())
+        return QualType();
+      ExpandedTypes.push_back(Transformed);
+      continue;
+    }
+
+    SmallVector<UnexpandedParameterPack, 2> Unexpanded;
+    getSema().collectUnexpandedParameterPacks(T, Unexpanded);
+    assert(!Unexpanded.empty() && "Pack expansion without parameter packs?");
+    // Determine whether the set of unexpanded parameter packs can and should
+    // be expanded.
+    bool ShouldExpand = true;
+    bool RetainExpansion = false;
+    std::optional<unsigned> OrigNumExpansions;
+    std::optional<unsigned> NumExpansions = OrigNumExpansions;
+    if (getDerived().TryExpandParameterPacks(TL.getEllipsisLoc(), SourceRange(),
+                                             Unexpanded, ShouldExpand,
+                                             RetainExpansion, NumExpansions))
+      return QualType();
+    if (!ShouldExpand) {
+      Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), -1);
+      QualType Pack = getDerived().TransformType(T);
+      if (Pack.isNull())
+        return QualType();
+      if (NotYetExpanded) {
+        FullyExpanded = false;
+        QualType Out = getDerived().RebuildPackIndexingType(
+            Pack, IndexExpr.get(), SourceLocation(), TL.getEllipsisLoc(),
+            FullyExpanded);
+        if (Out.isNull())
+          return QualType();
+
+        PackIndexingTypeLoc Loc = TLB.push<PackIndexingTypeLoc>(Out);
+        Loc.setEllipsisLoc(TL.getEllipsisLoc());
+        return Out;
+      }
+      ExpandedTypes.push_back(Pack);
+      continue;
+    }
+    for (unsigned I = 0; I != *NumExpansions; ++I) {
+      Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), I);
+      QualType Out = getDerived().TransformType(T);
+      if (Out.isNull())
+        return QualType();
+      ExpandedTypes.push_back(Out);
+    }
+    // If we're supposed to retain a pack expansion, do so by temporarily
+    // forgetting the partially-substituted parameter pack.
+    if (RetainExpansion) {
+      FullyExpanded = false;
+      ForgetPartiallySubstitutedPackRAII Forget(getDerived());
+      QualType Out = getDerived().TransformType(T);
+      if (Out.isNull())
+        return QualType();
+      ExpandedTypes.push_back(Out);
+    }
+  }
+
+  QualType Result = getDerived().TransformType(TLB, TL.getPatternLoc());
+
+  QualType Out = getDerived().RebuildPackIndexingType(
+      Result, IndexExpr.get(), SourceLocation(), TL.getEllipsisLoc(),
+      FullyExpanded, ExpandedTypes);
+  if (Out.isNull())
+    return Out;
+
+  PackIndexingTypeLoc Loc = TLB.push<PackIndexingTypeLoc>(Out);
+  Loc.setEllipsisLoc(TL.getEllipsisLoc());
+  return Out;
+}
+
 template<typename Derived>
 QualType TreeTransform<Derived>::TransformUnaryTransformType(
                                                             TypeLocBuilder &TLB,
@@ -14080,6 +14190,87 @@ TreeTransform<Derived>::TransformSizeOfPackExpr(SizeOfPackExpr *E) {
                                             Args.size(), std::nullopt);
 }
 
+template <typename Derived>
+ExprResult
+TreeTransform<Derived>::TransformPackIndexingExpr(PackIndexingExpr *E) {
+  if (!E->isValueDependent())
+    return E;
+
+  // Transform the index
+  ExprResult IndexExpr = getDerived().TransformExpr(E->getIndexExpr());
+  if (IndexExpr.isInvalid())
+    return ExprError();
+
+  SmallVector<Expr *, 5> ExpandedExprs;
+  if (E->getExpressions().empty()) {
+    Expr *Pattern = E->getPackIdExpression();
+    SmallVector<UnexpandedParameterPack, 2> Unexpanded;
+    getSema().collectUnexpandedParameterPacks(E->getPackIdExpression(),
+                                              Unexpanded);
+    assert(!Unexpanded.empty() && "Pack expansion without parameter packs?");
+
+    // Determine whether the set of unexpanded parameter packs can and should
+    // be expanded.
+    bool ShouldExpand = true;
+    bool RetainExpansion = false;
+    std::optional<unsigned> OrigNumExpansions;
+    std::optional<unsigned> NumExpansions = OrigNumExpansions;
+    if (getDerived().TryExpandParameterPacks(
+            E->getEllipsisLoc(), Pattern->getSourceRange(), Unexpanded,
+            ShouldExpand, RetainExpansion, NumExpansions))
+      return true;
+    if (!ShouldExpand) {
+      Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), -1);
+      ExprResult Pack = getDerived().TransformExpr(Pattern);
+      if (Pack.isInvalid())
+        return ExprError();
+      return getDerived().RebuildPackIndexingExpr(
+          E->getEllipsisLoc(), E->getRSquareLoc(), Pack.get(), IndexExpr.get(),
+          std::nullopt);
+    }
+    for (unsigned I = 0; I != *NumExpansions; ++I) {
+      Sema::ArgumentPackSubstitutionIndexRAII SubstIndex(getSema(), I);
+      ExprResult Out = getDerived().TransformExpr(Pattern);
+      if (Out.isInvalid())
+        return true;
+      if (Out.get()->containsUnexpandedParameterPack()) {
+        Out = getDerived().RebuildPackExpansion(Out.get(), E->getEllipsisLoc(),
+                                                OrigNumExpansions);
+        if (Out.isInvalid())
+          return true;
+      }
+      ExpandedExprs.push_back(Out.get());
+    }
+    // If we're supposed to retain a pack expansion, do so by temporarily
+    // forgetting the partially-substituted parameter pack.
+    if (RetainExpansion) {
+      ForgetPartiallySubstitutedPackRAII Forget(getDerived());
+
+      ExprResult Out = getDerived().TransformExpr(Pattern);
+      if (Out.isInvalid())
+        return true;
+
+      Out = getDerived().RebuildPackExpansion(Out.get(), E->getEllipsisLoc(),
+                                              OrigNumExpansions);
+      if (Out.isInvalid())
+        return true;
+      ExpandedExprs.push_back(Out.get());
+    }
+  }
+
+  else {
+    if (getDerived().TransformExprs(E->getExpressions().data(),
+                                    E->getExpressions().size(), false,
+                                    ExpandedExprs))
+      return ExprError();
+  }
+
+  return getDerived().RebuildPackIndexingExpr(
+      E->getEllipsisLoc(), E->getRSquareLoc(), E->getPackIdExpression(),
+      IndexExpr.get(), ExpandedExprs,
+      /*EmptyPack=*/ExpandedExprs.size() == 0);
+}
+
 template<typename Derived>
 ExprResult
 TreeTransform<Derived>::TransformSubstNonTypeTemplateParmPackExpr(
@@ -15121,6 +15312,15 @@ QualType TreeTransform<Derived>::RebuildDecltypeType(Expr *E, SourceLocation) {
   return SemaRef.BuildDecltypeType(E);
 }
 
+template <typename Derived>
+QualType TreeTransform<Derived>::RebuildPackIndexingType(
+    QualType Pattern, Expr *IndexExpr, SourceLocation Loc,
+    SourceLocation EllipsisLoc, bool FullyExpanded,
+    ArrayRef<QualType> Expansions) {
+  return SemaRef.BuildPackIndexingType(Pattern, IndexExpr, Loc, EllipsisLoc,
+                                       FullyExpanded, Expansions);
+}
+
 template<typename Derived>
 QualType TreeTransform<Derived>::RebuildUnaryTransformType(QualType BaseType,
                                             UnaryTransformType::UTTKind UKind,
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 42b48d230af7a9..3df8b4faad8c2f 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -6923,6 +6923,10 @@ void TypeLocReader::VisitDecltypeTypeLoc(DecltypeTypeLoc TL) {
   TL.setRParenLoc(readSourceLocation());
 }
 
+void TypeLocReader::VisitPackIndexingTypeLoc(PackIndexingTypeLoc TL) {
+  TL.setEllipsisLoc(readSourceLocation());
+}
+
 void TypeLocReader::VisitUnaryTransformTypeLoc(UnaryTransformTypeLoc TL) {
   TL.setKWLoc(readSourceLocation());
   TL.setLParenLoc(readSourceLocation());
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index d7d0c0e5bb21b4..0f6d6aa03ed9df 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -2128,6 +2128,22 @@ void ASTStmtReader::VisitSizeOfPackExpr(SizeOfPackExpr *E) {
   }
 }
 
+void ASTStmtReader::VisitPackIndexingExpr(PackIndexingExpr *E) {
+  VisitExpr(E);
+  unsigned NumTransformedExprs = Record.readInt();
+  E->EllipsisLoc = readSourceLocation();
+  E->RSquareLoc = readSourceLocation();
+  E->SubExprs[0] = Record.readStmt();
+  E->SubExprs[1] = Record.readStmt();
+  E->TransformedExpressions = NumTransformedExprs;
+  bool HasIndexValue = Record.readBool();
+  if (HasIndexValue)
+    E->Index = Record.readInt();
+  auto **Exprs = E->getTrailingObjects<Expr *>();
+  for (unsigned I = 0; I < NumTransformedExprs; ++I)
+    Exprs[I] = Record.readExpr();
+}
+
 void ASTStmtReader::VisitSubstNonTypeTemplateParmExpr(
                                               SubstNonTypeTemplateParmExpr *E) {
   VisitExpr(E);
@@ -3994,6 +4010,12 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
               /*NumPartialArgs=*/Record[ASTStmtReader::NumExprFields]);
       break;
 
+    case EXPR_PACK_INDEXING:
+      S = PackIndexingExpr::CreateDeserialized(
+          Context,
+          /*TransformedExprs=*/Record[ASTStmtReader::NumExprFields]);
+      break;
+
     case EXPR_SUBST_NON_TYPE_TEMPLATE_PARM:
       S = new (Context) SubstNonTypeTemplateParmExpr(Empty);
       break;
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index 0161ad10f3f238..98eec9a9873b03 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -482,6 +482,10 @@ void ASTRecordWriter::AddConceptReference(const ConceptReference *CR) {
     AddASTTemplateArgumentListInfo(CR->getTemplateArgsAsWritten());
 }
 
+void TypeLocWriter::VisitPackIndexingTypeLoc(PackIndexingTypeLoc TL) {
+  addSourceLocation(TL.getEllipsisLoc());
+}
+
 void TypeLocWriter::VisitAutoTypeLoc(AutoTypeLoc TL) {
   addSourceLocation(TL.getNameLoc());
   auto *CR = TL.getConceptReference();
@@ -784,6 +788,7 @@ static void AddStmtsExprs(llvm::BitstreamWriter &Stream,
   RECORD(EXPR_ARRAY_TYPE_TRAIT);
   RECORD(EXPR_PACK_EXPANSION);
   RECORD(EXPR_SIZEOF_PACK);
+  RECORD(EXPR_PACK_INDEXING);
   RECORD(EXPR_SUBST_NON_TYPE_TEMPLATE_PARM);
   RECORD(EXPR_SUBST_NON_TYPE_TEMPLATE_PARM_PACK);
   RECORD(EXPR_FUNCTION_PARM_PACK);
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index 59be6828fafabf..fd95848751fc20 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -2039,6 +2039,22 @@ void ASTStmtWriter::VisitSizeOfPackExpr(SizeOfPackExpr *E) {
   Code = serialization::EXPR_SIZEOF_PACK;
 }
 
+void ASTStmtWriter::VisitPackIndexingExpr(PackIndexingExpr *E) {
+  VisitExpr(E);
+  Record.push_back(E->TransformedExpressions);
+  Record.AddSourceLocation(E->getEllipsisLoc());
+  Record.AddSourceLocation(E->getRSquareLoc());
+  Record.AddStmt(E->getPackIdExpression());
+  Record.AddStmt(E->getIndexExpr());
+  Record.push_back(E->Index.has_value());
+  if (E->Index.has_value())
+    Record.push_back(*E->Index);
+  Record.push_back(E->TransformedExpressions);
+  for (Expr *Sub : E->getExpressions())
+    Record.AddStmt(Sub);
+  Code = serialization::EXPR_PACK_INDEXING;
+}
+
 void ASTStmtWriter::VisitSubstNonTypeTemplateParmExpr(
                                               SubstNonTypeTemplateParmExpr *E) {
   VisitExpr(E);
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 24e91a22fd6884..ccc3c0f1e0c100 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1737,6 +1737,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
     case Stmt::RecoveryExprClass:
     case Stmt::CXXNoexceptExprClass:
     case Stmt::PackExpansionExprClass:
+    case Stmt::PackIndexingExprClass:
     case Stmt::SubstNonTypeTemplateParmPackExprClass:
     case Stmt::FunctionParmPackExprClass:
     case Stmt::CoroutineBodyStmtClass:
diff --git a/clang/test/PCH/pack_indexing.cpp b/clang/test/PCH/pack_indexing.cpp
new file mode 100644
index 00000000000000..cf8124617b3c68
--- /dev/null
+++ b/clang/test/PCH/pack_indexing.cpp
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -std=c++2c -x c++-header %s -emit-pch -o %t.pch
+// RUN: %clang_cc1 -std=c++2c -x c++ /dev/null -include-pch %t.pch
+
+// RUN: %clang_cc1 -std=c++2c -x c++-header %s -emit-pch -fpch-instantiate-templates -o %t.pch
+// RUN: %clang_cc1 -std=c++2c -x c++ /dev/null -include-pch %t.pch
+
+template <int I, typename... U>
+using Type = U...[I];
+
+template <int I, auto...V>
+constexpr auto Var = V...[I];
+
+void fn1() {
+  using A = Type<1, int, long, double>;
+  constexpr auto V = Var<2, 0, 1, 42>;
+}
diff --git a/clang/test/Parser/cxx2b-pack-indexing.cpp b/clang/test/Parser/cxx2b-pack-indexing.cpp
new file mode 100644
index 00000000000000..1e92705dd66c82
--- /dev/null
+++ b/clang/test/Parser/cxx2b-pack-indexing.cpp
@@ -0,0 +1,65 @@
+// RUN: %clang_cc1 -std=c++2c -verify -fsyntax-only %s
+
+template<typename... T>
+struct S {
+    T...1; // expected-error{{expected member name or ';' after declaration specifiers}}
+    T...[; // expected-error{{expected expression}} \
+           // expected-error{{expected ']'}} \
+           // expected-note {{to match this '['}}\
+           // expected-warning{{declaration does not declare anything}}
+
+    T...[1; // expected-error{{expected ']'}} \
+            // expected-note {{to match this '['}}\
+            // expected-warning{{declaration does not declare anything}}
+
+    T...[]; // expected-error{{expected expression}} \
+            // expected-warning{{declaration does not declare anything}}
+
+    void f(auto... v) {
+        decltype(v...[1]) a = v...[1];
+        decltype(v...[1]) b = v...[]; // expected-error{{expected expression}}
+
+        decltype(v...[1]) c = v...[ ;  // expected-error{{expected expression}}\
+                                      // expected-error{{expected ']'}} \
+                                      // expected-note {{to match this '['}}
+    }
+};
+
+
+template <typename...>
+struct typelist{};
+
+template <typename... T>
+requires requires(T...[0]) { {T...[0](0)}; }
+struct SS : T...[1] {
+    [[no_unique_address]] T...[1] base = {};
+    using foo = T...[1];
+    SS()
+    : T...[1]()
+    {}
+    typelist<T...[0]> a;
+    const T...[0] f(T...[0] && p) noexcept((T...[0])0) {
+        T...[0] (*test)(const volatile T...[0]**);
+        thread_local T...[0] d;
+        [[maybe_unused]] T...[0] a = p;
+        auto ptr = new T...[0](0);
+        (*ptr).~T...[0]();
+        return T...[0](0);
+        typename T...[1]::foo b = 0;
+        T...[1]::i = 0;
+        return (T...[0])(a);
+        new T...[0];
+        [[maybe_unused]] auto l = []<T...[0]>(T...[0][1]) -> T...[0]{return{};};
+        [[maybe_unused]] auto _ = l.template operator()<T...[0]{}>({0});
+    }
+    operator T...[0]() const{}
+};
+
+struct base {
+    using foo = int;
+    static inline int i = 42;
+};
+
+int main() {
+    SS<int, base>().f(0);
+}
diff --git a/clang/test/SemaCXX/cxx2b-pack-indexing.cpp b/clang/test/SemaCXX/cxx2b-pack-indexing.cpp
new file mode 100644
index 00000000000000..31138fb6730c43
--- /dev/null
+++ b/clang/test/SemaCXX/cxx2b-pack-indexing.cpp
@@ -0,0 +1,115 @@
+// RUN: %clang_cc1 -std=c++2c -verify %s
+
+struct NotAPack;
+template <typename T, auto V, template<typename> typename Tp>
+void not_pack() {
+    int i = 0;
+    i...[0]; // expected-error {{i does not refer to the name of a parameter pack}}
+    V...[0]; // expected-error {{V does not refer to the name of a parameter pack}}
+    NotAPack...[0]; // expected-error{{'NotAPack' does not refer to the name of a parameter pack}}
+    T...[0];   // expected-error{{'T' does not refer to the name of a parameter pack}}
+    Tp...[0]; // expected-error{{'Tp' does not refer to the name of a parameter pack}}
+}
+
+namespace invalid_indexes {
+
+int non_constant_index(); // expected-note 2{{declared here}}
+
+template <int idx>
+int params(auto... p) {
+    return p...[idx]; //expected-error 3{{is not a valid index for pack p of size}}
+}
+
+template <auto N, typename...T>
+int test_types() {
+    T...[N] a; // expected-error 4{{is not a valid index for pack 'T' of size}}
+}
+
+void test() {
+    params<0>();   // expected-note{{here}}
+    params<1>(0);  // expected-note{{here}}
+    params<-1>(0); // expected-note{{here}}
+
+    test_types<-1>(); //expected-note {{in instantiation}}
+    test_types<-1, int>(); //expected-note {{in instantiation}}
+    test_types<0>(); //expected-note {{in instantiation}}
+    test_types<1, int>(); //expected-note {{in instantiation}}
+}
+
+void invalid_indexes(auto... p) {
+    p...[non_constant_index()]; // expected-error {{array size is not a constant expression}}\
+                                // expected-note {{cannot be used in a constant expression}}
+
+    const char* no_index = "";
+    p...[no_index]; // expected-error {{value of type 'const char *' is not implicitly convertible to 'unsigned long'}}
+}
+
+void invalid_index_types() {
+    []<typename... T> {
+        T...[non_constant_index()] a;  // expected-error {{array size is not a constant expression}}\
+                                       // expected-note {{cannot be used in a constant expression}}
+    }(); //expected-note {{in instantiation}}
+}
+
+}
+
+template <typename T, typename U>
+constexpr bool is_same = false;
+
+template <typename T>
+constexpr bool is_same<T, T> = true;
+
+template <typename T>
+constexpr bool f(auto&&... p) {
+    return is_same<T, decltype(p...[0])>;
+}
+
+void g() {
+    int a = 0;
+    const int b = 0;
+    static_assert(f<int&&>(0));
+    static_assert(f<int&>(a));
+    static_assert(f<const int&>(b));
+}
+
+template <auto... p>
+struct check_ice {
+    enum e {
+        x = p...[0]
+    };
+};
+
+static_assert(check_ice<42>::x == 42);
+
+struct S{};
+template <auto... p>
+constexpr auto constant_initializer = p...[0];
+constexpr auto InitOk = constant_initializer<S{}>;
+
+consteval int evaluate(auto... p) {
+    return p...[0];
+}
+constexpr int x = evaluate(42, S{});
+static_assert(x == 42);
+
+
+namespace splice {
+template <auto ... Is>
+struct IL{};
+
+template <typename ... Ts>
+struct TL{};
+
+template <typename Tl, typename Il>
+struct SpliceImpl;
+
+template <typename ... Ts, auto ...Is>
+struct SpliceImpl<TL<Ts...>, IL<Is...>>{
+    using type = TL<Ts...[Is]...>;
+};
+
+template <typename Tl, typename Il>
+using Splice = typename SpliceImpl<Tl, Il>::type;
+using type = Splice<TL<char, short, long, double>, IL<1, 2>>;
+static_assert(is_same<type, TL<short, long>>);
+}
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index df630f66f0b946..dc195f2a2ffef6 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -1886,6 +1886,12 @@ bool CursorVisitor::VisitDecltypeTypeLoc(DecltypeTypeLoc TL) {
   return false;
 }
 
+bool CursorVisitor::VisitPackIndexingTypeLoc(PackIndexingTypeLoc TL) {
+  if (Visit(TL.getPatternLoc()))
+    return true;
+  return Visit(MakeCXCursor(TL.getIndexExpr(), StmtParent, TU));
+}
+
 bool CursorVisitor::VisitInjectedClassNameTypeLoc(InjectedClassNameTypeLoc TL) {
   return Visit(MakeCursorTypeRef(TL.getDecl(), TL.getNameLoc(), TU));
 }
@@ -5711,6 +5717,8 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) {
     return cxstring::createRef("PackExpansionExpr");
   case CXCursor_SizeOfPackExpr:
     return cxstring::createRef("SizeOfPackExpr");
+  case CXCursor_PackIndexingExpr:
+    return cxstring::createRef("PackIndexingExpr");
   case CXCursor_LambdaExpr:
     return cxstring::createRef("LambdaExpr");
   case CXCursor_UnexposedExpr:
diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp
index fd03c48ba1a42a..c997b5c105e21e 100644
--- a/clang/tools/libclang/CXCursor.cpp
+++ b/clang/tools/libclang/CXCursor.cpp
@@ -567,6 +567,10 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent,
     K = CXCursor_SizeOfPackExpr;
     break;
 
+  case Stmt::PackIndexingExprClass:
+    K = CXCursor_PackIndexingExpr;
+    break;
+
   case Stmt::DeclRefExprClass:
     if (const ImplicitParamDecl *IPD = dyn_cast_or_null<ImplicitParamDecl>(
             cast<DeclRefExpr>(S)->getDecl())) {
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index 621439d0bae966..814bf9e66862f2 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -156,7 +156,7 @@ <h2 id="cxx26">C++2c implementation status</h2>
  <tr>
   <td>Pack Indexing</td>
   <td><a href="https://wg21.link/P2662R3">P2662R3</a></td>
-  <td class="none" align="center">No</td>
+  <td class="unreleased" align="center">Clang 18</td>
  </tr>
  <tr>
   <td>Remove Deprecated Arithmetic Conversion on Enumerations</td>

>From 96f5e9349df8c4074780b34142efa424e3248ea6 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Wed, 10 Jan 2024 14:58:15 +0100
Subject: [PATCH 2/5] Address some comments from Erich

---
 clang/include/clang-c/Index.h                    |  2 +-
 clang/include/clang/AST/Type.h                   |  2 +-
 clang/include/clang/Basic/DiagnosticSemaKinds.td |  2 +-
 clang/include/clang/Basic/Specifiers.h           |  2 +-
 clang/include/clang/Basic/TokenKinds.def         |  2 +-
 clang/include/clang/Sema/DeclSpec.h              |  7 ++++---
 clang/lib/AST/ExprCXX.cpp                        |  2 +-
 clang/lib/AST/TypePrinter.cpp                    |  1 -
 clang/lib/Parse/ParseCXXInlineMethods.cpp        |  3 +--
 clang/lib/Parse/ParseDecl.cpp                    |  4 ++--
 clang/lib/Parse/ParseDeclCXX.cpp                 | 14 +++++++-------
 clang/lib/Parse/ParseExpr.cpp                    |  4 ++--
 clang/lib/Parse/ParseExprCXX.cpp                 |  2 +-
 clang/lib/Parse/ParseTentative.cpp               |  4 ++--
 clang/lib/Parse/Parser.cpp                       |  2 +-
 clang/lib/Sema/DeclSpec.cpp                      |  8 ++++----
 clang/lib/Sema/SemaCXXScopeSpec.cpp              |  2 +-
 clang/lib/Sema/SemaDecl.cpp                      |  2 +-
 clang/lib/Sema/SemaDeclCXX.cpp                   |  2 +-
 clang/lib/Sema/SemaExprCXX.cpp                   |  2 +-
 clang/lib/Sema/SemaTemplateDeduction.cpp         |  5 ++++-
 clang/lib/Sema/SemaTemplateVariadic.cpp          |  6 +++---
 clang/lib/Sema/SemaType.cpp                      |  4 ++--
 clang/lib/Serialization/ASTReaderStmt.cpp        |  5 ++---
 clang/test/SemaCXX/cxx2b-pack-indexing.cpp       |  4 ++--
 25 files changed, 47 insertions(+), 46 deletions(-)

diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h
index 2c0b89a0d12b21..b667d406dfa5e5 100644
--- a/clang/include/clang-c/Index.h
+++ b/clang/include/clang-c/Index.h
@@ -1686,7 +1686,7 @@ enum CXCursorKind {
   CXCursor_CXXParenListInitExpr = 155,
 
   /**
-   *  Represents a C++26 pack indexing expression
+   *  Represents a C++26 pack indexing expression.
    */
   CXCursor_PackIndexingExpr = 156,
 
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index a710766d1cf92f..0904143b0c8c62 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -4920,7 +4920,7 @@ class PackIndexingType final
     return *(getExpansionsPtr() + Index);
   }
 
-  bool hasSelectedType() const { return Index != -1 && !isDependentType(); }
+  bool hasSelectedType() const { return !isDependentType(); }
 
   ArrayRef<QualType> getExpansions() const {
     return {getExpansionsPtr(), Size};
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 656dcbc42aae59..fcdd8a8df04170 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5685,7 +5685,7 @@ def err_expected_name_of_pack : Error<
   "%0 does not refer to the name of a parameter pack">;
 
 def err_pack_index_out_of_bound : Error<
-  "%0 is not a valid index for pack %1 of size %2">;
+  "invalid index %0 for pack %1 of size %2">;
 
 def err_fold_expression_packs_both_sides : Error<
   "binary fold expression has unexpanded parameter packs in both operands">;
diff --git a/clang/include/clang/Basic/Specifiers.h b/clang/include/clang/Basic/Specifiers.h
index bff4c1616c1d02..c0c5e4b6806f82 100644
--- a/clang/include/clang/Basic/Specifiers.h
+++ b/clang/include/clang/Basic/Specifiers.h
@@ -94,7 +94,7 @@ namespace clang {
     TST_auto_type,       // __auto_type extension
     TST_unknown_anytype, // __unknown_anytype extension
     TST_atomic,          // C11 _Atomic
-    TST_indexed_typename_pack,
+    TST_typename_pack_indexing,
 #define GENERIC_IMAGE_TYPE(ImgType, Id) TST_##ImgType##_t, // OpenCL image types
 #include "clang/Basic/OpenCLImageTypes.def"
     TST_error // erroneous type
diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index 6b5f8210fa6d0e..6361a2616eef56 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -836,7 +836,7 @@ ANNOTATION(primary_expr) // annotation for a primary expression, used when
                          // message send
 ANNOTATION(decltype)     // annotation for a decltype expression,
                          // e.g., "decltype(foo.bar())"
-ANNOTATION(indexed_pack_type) // annotation for an indexed pack of type,
+ANNOTATION(pack_indexing_type) // annotation for an indexed pack of type,
                               // e.g., "T...[expr]"
 
 // Annotation for #pragma unused(...)
diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h
index 4726cbbd1af50c..77638def60063d 100644
--- a/clang/include/clang/Sema/DeclSpec.h
+++ b/clang/include/clang/Sema/DeclSpec.h
@@ -309,7 +309,8 @@ class DeclSpec {
   static const TST TST_typeof_unqualExpr = clang::TST_typeof_unqualExpr;
   static const TST TST_decltype = clang::TST_decltype;
   static const TST TST_decltype_auto = clang::TST_decltype_auto;
-  static const TST TST_indexed_typename_pack = clang::TST_indexed_typename_pack;
+  static const TST TST_typename_pack_indexing =
+      clang::TST_typename_pack_indexing;
 #define TRANSFORM_TYPE_TRAIT_DEF(_, Trait)                                     \
   static const TST TST_##Trait = clang::TST_##Trait;
 #include "clang/Basic/TransformTypeTraits.def"
@@ -430,7 +431,7 @@ class DeclSpec {
   static bool isTypeRep(TST T) {
     return T == TST_atomic || T == TST_typename || T == TST_typeofType ||
            T == TST_typeof_unqualType || isTransformTypeTrait(T) ||
-           T == TST_indexed_typename_pack;
+           T == TST_typename_pack_indexing;
   }
   static bool isExprRep(TST T) {
     return T == TST_typeofExpr || T == TST_typeof_unqualExpr ||
@@ -535,7 +536,7 @@ class DeclSpec {
   }
 
   Expr *getPackIndexingExpr() const {
-    assert(TypeSpecType == TST_indexed_typename_pack &&
+    assert(TypeSpecType == TST_typename_pack_indexing &&
            "DeclSpec is not a pack indexing expr");
     return PackIndexingExpr;
   }
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index df76a5796d25bd..660d0656b3e71c 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -1690,7 +1690,7 @@ NamedDecl *PackIndexingExpr::getPackDecl() const {
     assert(ND && "exected a named decl");
     return ND;
   }
-  assert(false && "Non variables packs not supported");
+  assert(false && "invalid declaration kind in pack indexing expression");
   return nullptr;
 }
 
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index 673032fced19d5..897cfb65af06d6 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -284,7 +284,6 @@ bool TypePrinter::canPrefixQualifiers(const Type *T,
     case Type::FunctionNoProto:
     case Type::Paren:
     case Type::PackExpansion:
-
     case Type::SubstTemplateTypeParm:
     case Type::MacroQualified:
       CanPrefixQualifiers = false;
diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp
index 518a168ee246fb..d790060c17c049 100644
--- a/clang/lib/Parse/ParseCXXInlineMethods.cpp
+++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp
@@ -979,8 +979,7 @@ bool Parser::ConsumeAndStoreFunctionPrologue(CachedTokens &Toks) {
         break;
       }
       // Pack indexing
-      if (getLangOpts().CPlusPlus26 && Tok.is(tok::ellipsis) &&
-          NextToken().is(tok::l_square)) {
+      if (Tok.is(tok::ellipsis) && NextToken().is(tok::l_square)) {
         Toks.push_back(Tok);
         SourceLocation OpenLoc = ConsumeToken();
         Toks.push_back(Tok);
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index 37c8be5d8a50cd..b4fbca9f9bd09a 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -4442,7 +4442,7 @@ void Parser::ParseDeclarationSpecifiers(
       ParseDecltypeSpecifier(DS);
       continue;
 
-    case tok::annot_indexed_pack_type:
+    case tok::annot_pack_indexing_type:
       ParseIndexedTypeNamePack(DS);
       continue;
 
@@ -5741,7 +5741,7 @@ bool Parser::isDeclarationSpecifier(
 
     // C++11 decltype and constexpr.
   case tok::annot_decltype:
-  case tok::annot_indexed_pack_type:
+  case tok::annot_pack_indexing_type:
   case tok::kw_constexpr:
 
     // C++20 consteval and constinit.
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 1987ae6bb0dc94..33768442a22cb8 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -1197,7 +1197,7 @@ void Parser::AnnotateExistingDecltypeSpecifier(const DeclSpec &DS,
 }
 
 SourceLocation Parser::ParseIndexedTypeNamePack(DeclSpec &DS) {
-  assert(Tok.isOneOf(tok::annot_indexed_pack_type, tok::identifier) &&
+  assert(Tok.isOneOf(tok::annot_pack_indexing_type, tok::identifier) &&
          "Expected an identifier");
 
   TypeResult Type;
@@ -1207,7 +1207,7 @@ SourceLocation Parser::ParseIndexedTypeNamePack(DeclSpec &DS) {
   unsigned DiagID;
   const PrintingPolicy &Policy = Actions.getASTContext().getPrintingPolicy();
 
-  if (Tok.is(tok::annot_indexed_pack_type)) {
+  if (Tok.is(tok::annot_pack_indexing_type)) {
     StartLoc = Tok.getLocation();
     SourceLocation EndLoc;
     Type = getTypeAnnotation(Tok);
@@ -1220,7 +1220,7 @@ SourceLocation Parser::ParseIndexedTypeNamePack(DeclSpec &DS) {
       DS.SetTypeSpecError();
       return EndLoc;
     }
-    DS.SetTypeSpecType(DeclSpec::TST_indexed_typename_pack, StartLoc, PrevSpec,
+    DS.SetTypeSpecType(DeclSpec::TST_typename_pack_indexing, StartLoc, PrevSpec,
                        DiagID, Type, Policy);
     return EndLoc;
   } else {
@@ -1278,7 +1278,7 @@ void Parser::AnnotateExistingIndexedTypeNamePack(ParsedType T,
   } else
     PP.EnterToken(Tok, /*IsReinject*/ true);
 
-  Tok.setKind(tok::annot_indexed_pack_type);
+  Tok.setKind(tok::annot_pack_indexing_type);
   setTypeAnnotation(Tok, T);
   Tok.setAnnotationEndLoc(EndLoc);
   Tok.setLocation(StartLoc);
@@ -1382,7 +1382,7 @@ TypeResult Parser::ParseBaseTypeSpecifier(SourceLocation &BaseLoc,
     return Actions.ActOnTypeName(getCurScope(), DeclaratorInfo);
   }
 
-  if (Tok.is(tok::annot_indexed_pack_type)) {
+  if (Tok.is(tok::annot_pack_indexing_type)) {
     DeclSpec DS(AttrFactory);
     ParseIndexedTypeNamePack(DS);
     Declarator DeclaratorInfo(DS, ParsedAttributesView::none(),
@@ -3923,9 +3923,9 @@ MemInitResult Parser::ParseMemInitializer(Decl *ConstructorDecl) {
     // ParseOptionalCXXScopeSpecifier at this point.
     // FIXME: Can we get here with a scope specifier?
     ParseDecltypeSpecifier(DS);
-  } else if (Tok.is(tok::annot_indexed_pack_type)) {
+  } else if (Tok.is(tok::annot_pack_indexing_type)) {
     // Uses of T...[N] will already have been converted to
-    // annot_indexed_pack_type by ParseOptionalCXXScopeSpecifier at this point.
+    // annot_pack_indexing_type by ParseOptionalCXXScopeSpecifier at this point.
     ParseIndexedTypeNamePack(DS);
   } else {
     TemplateIdAnnotation *TemplateId = Tok.is(tok::annot_template_id)
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index aa5c83f3a64193..a6f41b395365d9 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -1073,7 +1073,7 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
         // If the token is not annotated, then it might be an expression pack
         // indexing
         if (!TryAnnotateTypeOrScopeToken() &&
-            Tok.is(tok::annot_indexed_pack_type))
+            Tok.is(tok::annot_pack_indexing_type))
           return ParseCastExpression(ParseKind, isAddressOfOperand, isTypeCast,
                                      isVectorLiteral, NotPrimaryExpression);
       }
@@ -1562,7 +1562,7 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
     [[fallthrough]];
 
   case tok::annot_decltype:
-  case tok::annot_indexed_pack_type:
+  case tok::annot_pack_indexing_type:
   case tok::kw_char:
   case tok::kw_wchar_t:
   case tok::kw_char8_t:
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 236418291d549a..56abdd12fb5a80 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -2437,7 +2437,7 @@ void Parser::ParseCXXSimpleTypeSpecifier(DeclSpec &DS) {
     DS.SetRangeEnd(ParseDecltypeSpecifier(DS));
     return DS.Finish(Actions, Policy);
 
-  case tok::annot_indexed_pack_type:
+  case tok::annot_pack_indexing_type:
     DS.SetRangeEnd(ParseIndexedTypeNamePack(DS));
     return DS.Finish(Actions, Policy);
 
diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp
index 521bed781855a1..4fbab1dd5d9482 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -1789,7 +1789,7 @@ Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename,
   case tok::kw__Accum:
   case tok::kw__Fract:
   case tok::kw__Sat:
-  case tok::annot_indexed_pack_type:
+  case tok::annot_pack_indexing_type:
 #define GENERIC_IMAGE_TYPE(ImgType, Id) case tok::kw_##ImgType##_t:
 #include "clang/Basic/OpenCLImageTypes.def"
     if (NextToken().is(tok::l_paren))
@@ -1868,7 +1868,7 @@ bool Parser::isCXXDeclarationSpecifierAType() {
   switch (Tok.getKind()) {
     // typename-specifier
   case tok::annot_decltype:
-  case tok::annot_indexed_pack_type:
+  case tok::annot_pack_indexing_type:
   case tok::annot_template_id:
   case tok::annot_typename:
   case tok::kw_typeof:
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 0fd25263e14599..64e9b14173a7a8 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -1976,7 +1976,7 @@ bool Parser::TryAnnotateTypeOrScopeToken(
           Tok.is(tok::kw_typename) || Tok.is(tok::annot_cxxscope) ||
           Tok.is(tok::kw_decltype) || Tok.is(tok::annot_template_id) ||
           Tok.is(tok::kw___super) || Tok.is(tok::kw_auto) ||
-          Tok.is(tok::annot_indexed_pack_type)) &&
+          Tok.is(tok::annot_pack_indexing_type)) &&
          "Cannot be a type or scope token!");
 
   if (Tok.is(tok::kw_typename)) {
diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp
index 2a559561c810c4..313f073445e8f2 100644
--- a/clang/lib/Sema/DeclSpec.cpp
+++ b/clang/lib/Sema/DeclSpec.cpp
@@ -374,7 +374,7 @@ bool Declarator::isDeclarationOfFunction() const {
     case TST_void:
     case TST_wchar:
     case TST_BFloat16:
-    case TST_indexed_typename_pack:
+    case TST_typename_pack_indexing:
 #define GENERIC_IMAGE_TYPE(ImgType, Id) case TST_##ImgType##_t:
 #include "clang/Basic/OpenCLImageTypes.def"
       return false;
@@ -586,7 +586,7 @@ const char *DeclSpec::getSpecifierName(DeclSpec::TST T,
   case DeclSpec::TST_struct:      return "struct";
   case DeclSpec::TST_interface:   return "__interface";
   case DeclSpec::TST_typename:    return "type-name";
-  case DeclSpec::TST_indexed_typename_pack:
+  case DeclSpec::TST_typename_pack_indexing:
     return "type-name-pack-indexing";
   case DeclSpec::TST_typeofType:
   case DeclSpec::TST_typeofExpr:  return "typeof";
@@ -779,7 +779,7 @@ bool DeclSpec::SetTypeSpecType(TST T, SourceLocation TagKwLoc,
   TSTNameLoc = TagNameLoc;
   TypeSpecOwned = false;
 
-  if (T == TST_indexed_typename_pack) {
+  if (T == TST_typename_pack_indexing) {
     // we got there from a an annotation. Reconstruct the type
     // Ugly...
     QualType QT = Rep.get();
@@ -989,7 +989,7 @@ void DeclSpec::SetPackIndexingExpr(SourceLocation EllipsisLoc,
                                    Expr *IndexingExpr) {
   assert(TypeSpecType == TST_typename &&
          "pack indexing can only be applied to typename");
-  TypeSpecType = TST_indexed_typename_pack;
+  TypeSpecType = TST_typename_pack_indexing;
   PackIndexingExpr = IndexingExpr;
   this->EllipsisLoc = EllipsisLoc;
 }
diff --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp
index 98833e931c6dad..fca5bd131bbc0f 100644
--- a/clang/lib/Sema/SemaCXXScopeSpec.cpp
+++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp
@@ -874,7 +874,7 @@ bool Sema::ActOnCXXNestedNameSpecifierIndexedPack(CXXScopeSpec &SS,
   if (SS.isInvalid() || DS.getTypeSpecType() == DeclSpec::TST_error)
     return true;
 
-  assert(DS.getTypeSpecType() == DeclSpec::TST_indexed_typename_pack);
+  assert(DS.getTypeSpecType() == DeclSpec::TST_typename_pack_indexing);
 
   if (Type.isNull())
     return true;
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 3ecf9eb65289f5..f391ffc023182e 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -158,7 +158,7 @@ bool Sema::isSimpleTypeSpecifier(tok::TokenKind Kind) const {
   case tok::kw_char32_t:
   case tok::kw_typeof:
   case tok::annot_decltype:
-  case tok::annot_indexed_pack_type:
+  case tok::annot_pack_indexing_type:
   case tok::kw_decltype:
     return getLangOpts().CPlusPlus;
 
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index a198913897b07e..5c753717430f72 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -4494,7 +4494,7 @@ Sema::BuildMemInitializer(Decl *ConstructorD,
   } else if (DS.getTypeSpecType() == TST_decltype_auto) {
     Diag(DS.getTypeSpecTypeLoc(), diag::err_decltype_auto_invalid);
     return true;
-  } else if (DS.getTypeSpecType() == TST_indexed_typename_pack) {
+  } else if (DS.getTypeSpecType() == TST_typename_pack_indexing) {
     BaseType =
         ActOnPackIndexingType(DS.getRepAsType().get(), DS.getPackIndexingExpr(),
                               DS.getBeginLoc(), DS.getEllipsisLoc());
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 75dc91518696f6..f2cc3661306c0c 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -8089,7 +8089,7 @@ ExprResult Sema::ActOnPseudoDestructorExpr(Scope *S, Expr *Base,
     DecltypeTL.setRParenLoc(DS.getTypeofParensRange().getEnd());
     break;
   }
-  case DeclSpec::TST_indexed_typename_pack: {
+  case DeclSpec::TST_typename_pack_indexing: {
     T = ActOnPackIndexingType(DS.getRepAsType().get(), DS.getPackIndexingExpr(),
                               DS.getBeginLoc(), DS.getEllipsisLoc());
     TLB.pushTrivial(getASTContext(),
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index c2279e96be67ef..dae2592da4024c 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -6391,9 +6391,12 @@ MarkUsedTemplateParameters(ASTContext &Ctx, QualType T,
     break;
 
   case Type::PackIndexing:
-    if (!OnlyDeduced)
+    if (!OnlyDeduced) {
       MarkUsedTemplateParameters(Ctx, cast<PackIndexingType>(T)->getPattern(),
                                  OnlyDeduced, Depth, Used);
+      MarkUsedTemplateParameters(Ctx, cast<PackIndexingType>(T)->getIndexExpr(),
+                                 OnlyDeduced, Depth, Used);
+    }
     break;
 
   case Type::UnaryTransform:
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index 3b87becc156b8e..f34ffd3e456e69 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -874,7 +874,7 @@ std::optional<unsigned> Sema::getNumArgumentsInExpansion(
 bool Sema::containsUnexpandedParameterPacks(Declarator &D) {
   const DeclSpec &DS = D.getDeclSpec();
   switch (DS.getTypeSpecType()) {
-  case TST_indexed_typename_pack:
+  case TST_typename_pack_indexing:
   case TST_typename:
   case TST_typeof_unqualType:
   case TST_typeofType:
@@ -1071,7 +1071,7 @@ ExprResult Sema::ActOnSizeofParameterPackExpr(Scope *S,
 }
 
 static bool isParameterPack(Expr *PackExpression) {
-  if (auto D = dyn_cast<DeclRefExpr>(PackExpression); D) {
+  if (auto *D = dyn_cast<DeclRefExpr>(PackExpression); D) {
     ValueDecl *VD = D->getDecl();
     return VD->isParameterPack();
   }
@@ -1100,7 +1100,7 @@ Sema::BuildPackIndexingExpr(Expr *PackExpression, SourceLocation EllipsisLoc,
                             ArrayRef<Expr *> ExpandedExprs, bool EmptyPack) {
 
   std::optional<int64_t> Index;
-  if (!IndexExpr->isValueDependent() && !IndexExpr->isTypeDependent()) {
+  if (!IndexExpr->isInstantiationDependent()) {
     llvm::APSInt Value(Context.getIntWidth(Context.getSizeType()));
     // TODO: do we need a new enumerator instead of CCEK_ArrayBound?
     ExprResult Res = CheckConvertedConstantExpression(
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 425ec618476931..a6c7546bd6707c 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -1665,7 +1665,7 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) {
     }
     break;
   }
-  case DeclSpec::TST_indexed_typename_pack: {
+  case DeclSpec::TST_typename_pack_indexing: {
     Expr *E = DS.getPackIndexingExpr();
     assert(E && "Didn't get an expression for pack indexing");
     QualType Pattern = S.GetTypeFromParser(DS.getRepAsType());
@@ -6324,7 +6324,7 @@ namespace {
       TL.setRParenLoc(DS.getTypeofParensRange().getEnd());
     }
     void VisitPackIndexingTypeLoc(PackIndexingTypeLoc TL) {
-      assert(DS.getTypeSpecType() == DeclSpec::TST_indexed_typename_pack);
+      assert(DS.getTypeSpecType() == DeclSpec::TST_typename_pack_indexing);
       TL.setEllipsisLoc(DS.getEllipsisLoc());
     }
     void VisitUnaryTransformTypeLoc(UnaryTransformTypeLoc TL) {
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 0f6d6aa03ed9df..25c09145ce2796 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -2130,17 +2130,16 @@ void ASTStmtReader::VisitSizeOfPackExpr(SizeOfPackExpr *E) {
 
 void ASTStmtReader::VisitPackIndexingExpr(PackIndexingExpr *E) {
   VisitExpr(E);
-  unsigned NumTransformedExprs = Record.readInt();
+  E->TransformedExpressions = Record.readInt();
   E->EllipsisLoc = readSourceLocation();
   E->RSquareLoc = readSourceLocation();
   E->SubExprs[0] = Record.readStmt();
   E->SubExprs[1] = Record.readStmt();
-  E->TransformedExpressions = NumTransformedExprs;
   bool HasIndexValue = Record.readBool();
   if (HasIndexValue)
     E->Index = Record.readInt();
   auto **Exprs = E->getTrailingObjects<Expr *>();
-  for (unsigned I = 0; I < NumTransformedExprs; ++I)
+  for (unsigned I = 0; I < E->TransformedExpressions; ++I)
     Exprs[I] = Record.readExpr();
 }
 
diff --git a/clang/test/SemaCXX/cxx2b-pack-indexing.cpp b/clang/test/SemaCXX/cxx2b-pack-indexing.cpp
index 31138fb6730c43..352a735c4f3215 100644
--- a/clang/test/SemaCXX/cxx2b-pack-indexing.cpp
+++ b/clang/test/SemaCXX/cxx2b-pack-indexing.cpp
@@ -17,12 +17,12 @@ int non_constant_index(); // expected-note 2{{declared here}}
 
 template <int idx>
 int params(auto... p) {
-    return p...[idx]; //expected-error 3{{is not a valid index for pack p of size}}
+    return p...[idx]; //expected-error 3{{invalid index 0 for pack p of size 0}}
 }
 
 template <auto N, typename...T>
 int test_types() {
-    T...[N] a; // expected-error 4{{is not a valid index for pack 'T' of size}}
+    T...[N] a; // expected-error 4{{invalid index -1 for pack 'T' of size 0}}
 }
 
 void test() {

>From 89724f95852db9ae0c06f6bc37ee48db7242e7d9 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Wed, 10 Jan 2024 17:38:45 +0100
Subject: [PATCH 3/5] Address more of Erich's feedback  - Add a
 computeDependence function  - Remove the Index fron the type / expression: we
 do keep it around in some of the functions to avoid haviung to recompute the
 index in multiple places in short sucession.

---
 clang/include/clang/AST/ASTContext.h        |  2 +-
 clang/include/clang/AST/ComputeDependence.h |  2 ++
 clang/include/clang/AST/ExprCXX.h           | 40 ++++++++++-----------
 clang/include/clang/AST/Type.h              |  9 ++---
 clang/include/clang/Sema/Sema.h             |  2 +-
 clang/lib/AST/ASTContext.cpp                |  8 ++---
 clang/lib/AST/ComputeDependence.cpp         | 17 +++++++++
 clang/lib/AST/ExprCXX.cpp                   |  2 +-
 clang/lib/AST/Type.cpp                      | 13 +++++--
 clang/lib/Sema/SemaTemplateVariadic.cpp     |  1 +
 clang/lib/Sema/SemaType.cpp                 |  9 ++---
 clang/lib/Sema/TreeTransform.h              | 29 ++++++++-------
 clang/lib/Serialization/ASTReaderStmt.cpp   |  3 --
 clang/lib/Serialization/ASTWriterStmt.cpp   |  3 --
 clang/test/SemaCXX/cxx2b-pack-indexing.cpp  | 27 ++++++++------
 15 files changed, 97 insertions(+), 70 deletions(-)

diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 9e1c44eb19b805..668462ef546064 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -1717,7 +1717,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
   QualType getDecltypeType(Expr *e, QualType UnderlyingType) const;
 
   QualType getPackIndexingType(QualType Pattern, Expr *IndexExpr,
-                               bool FullyExpanded = false,
+                               bool FullySubstituted = false,
                                ArrayRef<QualType> Expansions = {},
                                int Index = -1) const;
 
diff --git a/clang/include/clang/AST/ComputeDependence.h b/clang/include/clang/AST/ComputeDependence.h
index f62611cb4c3cf7..7abf9141237dc8 100644
--- a/clang/include/clang/AST/ComputeDependence.h
+++ b/clang/include/clang/AST/ComputeDependence.h
@@ -63,6 +63,7 @@ class ArrayTypeTraitExpr;
 class ExpressionTraitExpr;
 class CXXNoexceptExpr;
 class PackExpansionExpr;
+class PackIndexingExpr;
 class SubstNonTypeTemplateParmExpr;
 class CoroutineSuspendExpr;
 class DependentCoawaitExpr;
@@ -150,6 +151,7 @@ ExprDependence computeDependence(ArrayTypeTraitExpr *E);
 ExprDependence computeDependence(ExpressionTraitExpr *E);
 ExprDependence computeDependence(CXXNoexceptExpr *E, CanThrowResult CT);
 ExprDependence computeDependence(PackExpansionExpr *E);
+ExprDependence computeDependence(PackIndexingExpr *E);
 ExprDependence computeDependence(SubstNonTypeTemplateParmExpr *E);
 ExprDependence computeDependence(CoroutineSuspendExpr *E);
 ExprDependence computeDependence(DependentCoawaitExpr *E);
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index efd6558326099e..34d7fb03eae235 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -4359,36 +4359,23 @@ class PackIndexingExpr final
   // The pack being indexed, followed by the index
   Stmt *SubExprs[2];
 
-  // The evaluated index
-  std::optional<int64_t> Index;
-
   size_t TransformedExpressions;
 
   PackIndexingExpr(QualType Type, SourceLocation EllipsisLoc,
                    SourceLocation RSquareLoc, Expr *PackIdExpr, Expr *IndexExpr,
-                   std::optional<int64_t> Index = std::nullopt,
                    ArrayRef<Expr *> SubstitutedExprs = {})
       : Expr(PackIndexingExprClass, Type, VK_LValue, OK_Ordinary),
         EllipsisLoc(EllipsisLoc), RSquareLoc(RSquareLoc),
-        SubExprs{PackIdExpr, IndexExpr}, Index(Index),
+        SubExprs{PackIdExpr, IndexExpr},
         TransformedExpressions(SubstitutedExprs.size()) {
 
     auto *Exprs = getTrailingObjects<Expr *>();
     std::uninitialized_copy(SubstitutedExprs.begin(), SubstitutedExprs.end(),
                             Exprs);
 
-    ExprDependence D = IndexExpr->getDependence();
-    if (SubstitutedExprs.empty())
-      D |= (PackIdExpr->getDependence() |
-            ExprDependence::TypeValueInstantiation) &
-           ~ExprDependence::UnexpandedPack;
-    else if (!IndexExpr->isValueDependent()) {
-      assert(Index && *Index < int64_t(SubstitutedExprs.size()) &&
-             "pack index out of bound");
-      D |= SubstitutedExprs[*Index]->getDependence();
-      setValueKind(SubstitutedExprs[*Index]->getValueKind());
-    }
-    setDependence(D);
+    setDependence(computeDependence(this));
+    if(!isInstantiationDependent())
+      setValueKind(getSelectedExpr()->getValueKind());
   }
 
   /// Create an empty expression.
@@ -4402,8 +4389,7 @@ class PackIndexingExpr final
   static PackIndexingExpr *Create(ASTContext &Context,
                                   SourceLocation EllipsisLoc,
                                   SourceLocation RSquareLoc, Expr *PackIdExpr,
-                                  Expr *IndexExpr,
-                                  std::optional<int64_t> Index = std::nullopt,
+                                  Expr *IndexExpr, std::optional<int64_t> Index,
                                   ArrayRef<Expr *> SubstitutedExprs = {});
   static PackIndexingExpr *CreateDeserialized(ASTContext &Context,
                                               unsigned NumTransformedExprs);
@@ -4426,13 +4412,23 @@ class PackIndexingExpr final
 
   Expr *getIndexExpr() const { return cast<Expr>(SubExprs[1]); }
 
+
+  std::optional<unsigned> getSelectedIndex() const {
+    if (isInstantiationDependent())
+      return std::nullopt;
+    ConstantExpr* CE = cast<ConstantExpr>(getIndexExpr());
+    auto Index = CE->getResultAsAPSInt();
+    assert(Index.isNonNegative() && "Invalid index");
+    return static_cast<unsigned>(Index.getExtValue());
+  }
+
   Expr *getSelectedExpr() const {
-    assert(Index && !isInstantiationDependent() &&
-           "extracting the indexed expression of a dependant pack");
+    std::optional<unsigned> Index = getSelectedIndex();
+    assert(Index && "extracting the indexed expression of a dependant pack");
     return getTrailingObjects<Expr *>()[*Index];
   }
 
-  llvm::ArrayRef<Expr *> getExpressions() const {
+  ArrayRef<Expr *> getExpressions() const {
     if (TransformedExpressions == 0)
       return {};
     return {getTrailingObjects<Expr *>(), TransformedExpressions};
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index 0904143b0c8c62..14beeccc1342cd 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -4895,13 +4895,12 @@ class PackIndexingType final
   Expr *IndexExpr;
 
   unsigned Size;
-  int Index = -1;
 
 protected:
   friend class ASTContext; // ASTContext creates these.
   PackIndexingType(const ASTContext &Context, QualType Canonical,
                    QualType Pattern, Expr *IndexExpr,
-                   ArrayRef<QualType> Expansions = {}, int Index = -1);
+                   ArrayRef<QualType> Expansions = {});
 
 public:
   Expr *getIndexExpr() const { return IndexExpr; }
@@ -4917,10 +4916,12 @@ class PackIndexingType final
 
   QualType getSelectedType() const {
     assert(hasSelectedType() && "Type is dependant");
-    return *(getExpansionsPtr() + Index);
+    return *(getExpansionsPtr() + *getSelectedIndex());
   }
 
-  bool hasSelectedType() const { return !isDependentType(); }
+  std::optional<unsigned> getSelectedIndex() const;
+
+  bool hasSelectedType() const { return getSelectedIndex() != std::nullopt; }
 
   ArrayRef<QualType> getExpansions() const {
     return {getExpansionsPtr(), Size};
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index cbebb79b91b52a..f095ebb38ac7f5 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2594,7 +2594,7 @@ class Sema final {
                                  SourceLocation EllipsisLoc);
   QualType BuildPackIndexingType(QualType Pattern, Expr *IndexExpr,
                                  SourceLocation Loc, SourceLocation EllipsisLoc,
-                                 bool FullyExpanded = false,
+                                 bool FullySubstituted = false,
                                  ArrayRef<QualType> Expansions = {});
 
   using UTTKind = UnaryTransformType::UTTKind;
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index da6265a79316ff..89776b32ec0ea0 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -5699,11 +5699,11 @@ QualType ASTContext::getDecltypeType(Expr *e, QualType UnderlyingType) const {
 }
 
 QualType ASTContext::getPackIndexingType(QualType Pattern, Expr *IndexExpr,
-                                         bool FullyExpanded,
+                                         bool FullySubstituted,
                                          ArrayRef<QualType> Expansions,
                                          int Index) const {
   QualType Canonical;
-  if (FullyExpanded && Index != -1) {
+  if (FullySubstituted && Index != -1) {
     Canonical = getCanonicalType(Expansions[Index]);
   } else {
     llvm::FoldingSetNodeID ID;
@@ -5716,7 +5716,7 @@ QualType ASTContext::getPackIndexingType(QualType Pattern, Expr *IndexExpr,
           PackIndexingType::totalSizeToAlloc<QualType>(Expansions.size()),
           TypeAlignment);
       Canon = new (Mem) PackIndexingType(*this, QualType(), Pattern, IndexExpr,
-                                         Expansions, Index);
+                                         Expansions);
       DependentPackIndexingTypes.InsertNode(Canon, InsertPos);
     }
     Canonical = QualType(Canon, 0);
@@ -5726,7 +5726,7 @@ QualType ASTContext::getPackIndexingType(QualType Pattern, Expr *IndexExpr,
       Allocate(PackIndexingType::totalSizeToAlloc<QualType>(Expansions.size()),
                TypeAlignment);
   auto *T = new (Mem)
-      PackIndexingType(*this, Canonical, Pattern, IndexExpr, Expansions, Index);
+      PackIndexingType(*this, Canonical, Pattern, IndexExpr, Expansions);
   Types.push_back(T);
   return QualType(T, 0);
 }
diff --git a/clang/lib/AST/ComputeDependence.cpp b/clang/lib/AST/ComputeDependence.cpp
index 097753fd3267b5..d7b9cfde5c2745 100644
--- a/clang/lib/AST/ComputeDependence.cpp
+++ b/clang/lib/AST/ComputeDependence.cpp
@@ -364,6 +364,23 @@ ExprDependence clang::computeDependence(PackExpansionExpr *E) {
          ExprDependence::TypeValueInstantiation;
 }
 
+ExprDependence clang::computeDependence(PackIndexingExpr *E) {
+  ExprDependence D = E->getIndexExpr()->getDependence();
+  ArrayRef<Expr *> Exprs = E->getExpressions();
+  if (Exprs.empty())
+    D |= (E->getPackIdExpression()->getDependence() |
+          ExprDependence::TypeValueInstantiation) &
+         ~ExprDependence::UnexpandedPack;
+  else if (!E->getIndexExpr()->isInstantiationDependent()) {
+    std::optional<unsigned> Index = E->getSelectedIndex();
+    assert(Index && *Index < Exprs.size() &&
+           "pack index out of bound");
+    D |= Exprs[*Index]->getDependence();
+  }
+  return D;
+}
+
+
 ExprDependence clang::computeDependence(SubstNonTypeTemplateParmExpr *E) {
   return E->getReplacement()->getDependence();
 }
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index 660d0656b3e71c..a6902cde716ea7 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -1681,7 +1681,7 @@ PackIndexingExpr *PackIndexingExpr::Create(ASTContext &Context,
       Context.Allocate(totalSizeToAlloc<Expr *>(SubstitutedExprs.size()));
   return new (Storage)
       PackIndexingExpr(Type, EllipsisLoc, RSquareLoc, PackIdExpr, IndexExpr,
-                       Index, SubstitutedExprs);
+                       SubstitutedExprs);
 }
 
 NamedDecl *PackIndexingExpr::getPackDecl() const {
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index ab83e833f86939..76b38ab0b5725e 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -3770,16 +3770,25 @@ void DependentDecltypeType::Profile(llvm::FoldingSetNodeID &ID,
 PackIndexingType::PackIndexingType(const ASTContext &Context,
                                    QualType Canonical, QualType Pattern,
                                    Expr *IndexExpr,
-                                   ArrayRef<QualType> Expansions, int Index)
+                                   ArrayRef<QualType> Expansions)
     : Type(PackIndexing, Canonical,
            computeDependence(Pattern, IndexExpr, Expansions)),
       Context(Context), Pattern(Pattern), IndexExpr(IndexExpr),
-      Size(Expansions.size()), Index(Index) {
+      Size(Expansions.size()) {
 
   std::uninitialized_copy(Expansions.begin(), Expansions.end(),
                           getTrailingObjects<QualType>());
 }
 
+std::optional<unsigned> PackIndexingType::getSelectedIndex() const {
+  if(isDependentType())
+    return std::nullopt;
+  ConstantExpr* CE = cast<ConstantExpr>(getIndexExpr());
+  auto Index = CE->getResultAsAPSInt();
+  assert(Index.isNonNegative() && "Invalid index");
+  return static_cast<unsigned>(Index.getExtValue());
+}
+
 TypeDependence
 PackIndexingType::computeDependence(QualType Pattern, Expr *IndexExpr,
                                     ArrayRef<QualType> Expansions) {
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index f34ffd3e456e69..e85b49af21f984 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -1108,6 +1108,7 @@ Sema::BuildPackIndexingExpr(Expr *PackExpression, SourceLocation EllipsisLoc,
     if (!Res.isUsable())
       return ExprError();
     Index = Value.getExtValue();
+    IndexExpr = Res.get();
   }
 
   if (Index && (!ExpandedExprs.empty() || EmptyPack)) {
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index a6c7546bd6707c..18c6afdd4c6290 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -9691,11 +9691,11 @@ QualType Sema::ActOnPackIndexingType(QualType Pattern, Expr *IndexExpr,
 QualType Sema::BuildPackIndexingType(QualType Pattern, Expr *IndexExpr,
                                      SourceLocation Loc,
                                      SourceLocation EllipsisLoc,
-                                     bool FullyExpanded,
+                                     bool FullySubstituted,
                                      ArrayRef<QualType> Expansions) {
 
   std::optional<int64_t> Index;
-  if (FullyExpanded && !IndexExpr->isValueDependent() &&
+  if (FullySubstituted && !IndexExpr->isValueDependent() &&
       !IndexExpr->isTypeDependent()) {
     llvm::APSInt Value(Context.getIntWidth(Context.getSizeType()));
     // TODO: do we need a new enumerator instead of CCEK_ArrayBound?
@@ -9704,9 +9704,10 @@ QualType Sema::BuildPackIndexingType(QualType Pattern, Expr *IndexExpr,
     if (!Res.isUsable())
       return QualType();
     Index = Value.getExtValue();
+    IndexExpr = Res.get();
   }
 
-  if (FullyExpanded && Index) {
+  if (FullySubstituted && Index) {
     if (*Index < 0 || *Index >= int64_t(Expansions.size())) {
       Diag(IndexExpr->getBeginLoc(), diag::err_pack_index_out_of_bound)
           << *Index << Pattern << Expansions.size();
@@ -9714,7 +9715,7 @@ QualType Sema::BuildPackIndexingType(QualType Pattern, Expr *IndexExpr,
     }
   }
 
-  return Context.getPackIndexingType(Pattern, IndexExpr, FullyExpanded,
+  return Context.getPackIndexingType(Pattern, IndexExpr, FullySubstituted,
                                      Expansions, Index.value_or(-1));
 }
 
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index f7b9d6b8f86d75..91254fc3c3e2e6 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -1046,7 +1046,7 @@ class TreeTransform {
   QualType RebuildPackIndexingType(QualType Pattern, Expr *IndexExpr,
                                    SourceLocation Loc,
                                    SourceLocation EllipsisLoc,
-                                   bool FullyExpanded,
+                                   bool FullySubstituted,
                                    ArrayRef<QualType> Expansions = {});
 
   /// Build a new C++11 auto type.
@@ -6487,11 +6487,11 @@ TreeTransform<Derived>::TransformPackIndexingType(TypeLocBuilder &TLB,
   QualType Pattern = TL.getPattern();
 
   const PackIndexingType *PIT = TL.getTypePtr();
-  SmallVector<QualType, 5> ExpandedTypes;
+  SmallVector<QualType, 5> SubtitutedTypes;
   llvm::ArrayRef<QualType> Types = PIT->getExpansions();
 
   bool NotYetExpanded = Types.empty();
-  bool FullyExpanded = true;
+  bool FullySubstituted = true;
 
   if (Types.empty())
     Types = llvm::ArrayRef<QualType>(&Pattern, 1);
@@ -6501,7 +6501,7 @@ TreeTransform<Derived>::TransformPackIndexingType(TypeLocBuilder &TLB,
       QualType Transformed = getDerived().TransformType(T);
       if (Transformed.isNull())
         return QualType();
-      ExpandedTypes.push_back(Transformed);
+      SubtitutedTypes.push_back(Transformed);
       continue;
     }
 
@@ -6524,10 +6524,10 @@ TreeTransform<Derived>::TransformPackIndexingType(TypeLocBuilder &TLB,
       if (Pack.isNull())
         return QualType();
       if (NotYetExpanded) {
-        FullyExpanded = false;
+        FullySubstituted = false;
         QualType Out = getDerived().RebuildPackIndexingType(
             Pack, IndexExpr.get(), SourceLocation(), TL.getEllipsisLoc(),
-            FullyExpanded);
+            FullySubstituted);
         if (Out.isNull())
           return QualType();
 
@@ -6535,7 +6535,7 @@ TreeTransform<Derived>::TransformPackIndexingType(TypeLocBuilder &TLB,
         Loc.setEllipsisLoc(TL.getEllipsisLoc());
         return Out;
       }
-      ExpandedTypes.push_back(Pack);
+      SubtitutedTypes.push_back(Pack);
       continue;
     }
     for (unsigned I = 0; I != *NumExpansions; ++I) {
@@ -6543,17 +6543,17 @@ TreeTransform<Derived>::TransformPackIndexingType(TypeLocBuilder &TLB,
       QualType Out = getDerived().TransformType(T);
       if (Out.isNull())
         return QualType();
-      ExpandedTypes.push_back(Out);
+      SubtitutedTypes.push_back(Out);
     }
     // If we're supposed to retain a pack expansion, do so by temporarily
     // forgetting the partially-substituted parameter pack.
     if (RetainExpansion) {
-      FullyExpanded = false;
+      FullySubstituted = false;
       ForgetPartiallySubstitutedPackRAII Forget(getDerived());
       QualType Out = getDerived().TransformType(T);
       if (Out.isNull())
         return QualType();
-      ExpandedTypes.push_back(Out);
+      SubtitutedTypes.push_back(Out);
     }
   }
 
@@ -6561,7 +6561,7 @@ TreeTransform<Derived>::TransformPackIndexingType(TypeLocBuilder &TLB,
 
   QualType Out = getDerived().RebuildPackIndexingType(
       Result, IndexExpr.get(), SourceLocation(), TL.getEllipsisLoc(),
-      FullyExpanded, ExpandedTypes);
+      FullySubstituted, SubtitutedTypes);
   if (Out.isNull())
     return Out;
 
@@ -15313,12 +15313,11 @@ QualType TreeTransform<Derived>::RebuildDecltypeType(Expr *E, SourceLocation) {
 }
 
 template <typename Derived>
-QualType TreeTransform<Derived>::RebuildPackIndexingType(
-    QualType Pattern, Expr *IndexExpr, SourceLocation Loc,
-    SourceLocation EllipsisLoc, bool FullyExpanded,
+QualType TreeTransform<Derived>::RebuildPackIndexingType(QualType Pattern, Expr *IndexExpr, SourceLocation Loc,
+    SourceLocation EllipsisLoc, bool FullySubstituted,
     ArrayRef<QualType> Expansions) {
   return SemaRef.BuildPackIndexingType(Pattern, IndexExpr, Loc, EllipsisLoc,
-                                       FullyExpanded, Expansions);
+                                       FullySubstituted, Expansions);
 }
 
 template<typename Derived>
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 25c09145ce2796..46fa62b2aae412 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -2135,9 +2135,6 @@ void ASTStmtReader::VisitPackIndexingExpr(PackIndexingExpr *E) {
   E->RSquareLoc = readSourceLocation();
   E->SubExprs[0] = Record.readStmt();
   E->SubExprs[1] = Record.readStmt();
-  bool HasIndexValue = Record.readBool();
-  if (HasIndexValue)
-    E->Index = Record.readInt();
   auto **Exprs = E->getTrailingObjects<Expr *>();
   for (unsigned I = 0; I < E->TransformedExpressions; ++I)
     Exprs[I] = Record.readExpr();
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index fd95848751fc20..db4bbdc4162798 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -2046,9 +2046,6 @@ void ASTStmtWriter::VisitPackIndexingExpr(PackIndexingExpr *E) {
   Record.AddSourceLocation(E->getRSquareLoc());
   Record.AddStmt(E->getPackIdExpression());
   Record.AddStmt(E->getIndexExpr());
-  Record.push_back(E->Index.has_value());
-  if (E->Index.has_value())
-    Record.push_back(*E->Index);
   Record.push_back(E->TransformedExpressions);
   for (Expr *Sub : E->getExpressions())
     Record.AddStmt(Sub);
diff --git a/clang/test/SemaCXX/cxx2b-pack-indexing.cpp b/clang/test/SemaCXX/cxx2b-pack-indexing.cpp
index 352a735c4f3215..fbbb50f7f665c8 100644
--- a/clang/test/SemaCXX/cxx2b-pack-indexing.cpp
+++ b/clang/test/SemaCXX/cxx2b-pack-indexing.cpp
@@ -17,23 +17,30 @@ int non_constant_index(); // expected-note 2{{declared here}}
 
 template <int idx>
 int params(auto... p) {
-    return p...[idx]; //expected-error 3{{invalid index 0 for pack p of size 0}}
+    return p...[idx]; // #error-param-size
 }
 
 template <auto N, typename...T>
 int test_types() {
-    T...[N] a; // expected-error 4{{invalid index -1 for pack 'T' of size 0}}
+    T...[N] a; // #error-type-size
 }
 
 void test() {
-    params<0>();   // expected-note{{here}}
-    params<1>(0);  // expected-note{{here}}
-    params<-1>(0); // expected-note{{here}}
-
-    test_types<-1>(); //expected-note {{in instantiation}}
-    test_types<-1, int>(); //expected-note {{in instantiation}}
-    test_types<0>(); //expected-note {{in instantiation}}
-    test_types<1, int>(); //expected-note {{in instantiation}}
+    params<0>();   // expected-note{{here}} \
+                   // expected-error@#error-param-size {{invalid index 0 for pack p of size 0}}
+    params<1>(0);  // expected-note{{here}} \
+                   // expected-error@#error-param-size {{invalid index 1 for pack p of size 1}}
+    params<-1>(0); // expected-note{{here}} \
+                   // expected-error@#error-param-size {{invalid index -1 for pack p of size 1}}
+
+    test_types<-1>(); //expected-note {{in instantiation}} \
+                      // expected-error@#error-type-size {{invalid index -1 for pack 'T' of size 0}}
+    test_types<-1, int>(); //expected-note {{in instantiation}} \
+                      // expected-error@#error-type-size {{invalid index -1 for pack 'T' of size 1}}
+    test_types<0>(); //expected-note {{in instantiation}} \
+                    // expected-error@#error-type-size {{invalid index 0 for pack 'T' of size 0}}
+    test_types<1, int>(); //expected-note {{in instantiation}}  \
+                         // expected-error@#error-type-size {{invalid index 1 for pack 'T' of size 1}}
 }
 
 void invalid_indexes(auto... p) {

>From f0a11e49a780c9bb5e0bdb46e99e3855d9f63f8f Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Thu, 11 Jan 2024 18:25:11 +0100
Subject: [PATCH 4/5] Allow pack indexing as an extension (wip)

---
 clang/include/clang/Basic/DiagnosticSemaKinds.td |  6 ++++++
 clang/lib/Parse/ParseDeclCXX.cpp                 |  7 +++++--
 clang/lib/Parse/ParseExprCXX.cpp                 | 10 +++++-----
 clang/lib/Sema/SemaTemplateVariadic.cpp          |  6 +++++-
 clang/lib/Sema/SemaType.cpp                      |  5 ++++-
 5 files changed, 25 insertions(+), 9 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index fcdd8a8df04170..5d0dc57ba9fd90 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5687,6 +5687,12 @@ def err_expected_name_of_pack : Error<
 def err_pack_index_out_of_bound : Error<
   "invalid index %0 for pack %1 of size %2">;
 
+def ext_pack_indexing : ExtWarn<
+  "pack indexing is a C++2c extension">, InGroup<CXX26>;
+def warn_cxx23_pack_indexing : Warning<
+  "pack indexing is incompatible with C++ standards before C++2c">,
+  DefaultIgnore, InGroup<CXXPre26Compat>;
+
 def err_fold_expression_packs_both_sides : Error<
   "binary fold expression has unexpanded parameter packs in both operands">;
 def err_fold_expression_empty : Error<
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 33768442a22cb8..51c1e8f973df7f 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -1247,12 +1247,15 @@ SourceLocation Parser::ParseIndexedTypeNamePack(DeclSpec &DS) {
       DS.SetTypeSpecError();
       return IndexExpr.isInvalid() ? StartLoc : IndexExpr.get()->getEndLoc();
     }
+
+    DS.SetRangeStart(StartLoc);
+    DS.SetRangeEnd(T.getCloseLocation());
+
     if (IndexExpr.isInvalid()) {
       DS.SetTypeSpecError();
       return T.getCloseLocation();
     }
-    DS.SetRangeStart(StartLoc);
-    DS.SetRangeEnd(T.getCloseLocation());
+
     DS.SetTypeSpecType(DeclSpec::TST_typename, StartLoc, PrevSpec, DiagID, Type,
                        Policy);
     DS.SetPackIndexingExpr(EllipsisLoc, IndexExpr.get());
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 56abdd12fb5a80..3984cf28e172d9 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -233,7 +233,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
     HasScopeSpecifier = true;
   }
 
-  else if (getLangOpts().CPlusPlus26 && !HasScopeSpecifier &&
+  else if (!HasScopeSpecifier &&
            Tok.is(tok::identifier) && GetLookAheadToken(1).is(tok::ellipsis) &&
            GetLookAheadToken(2).is(tok::l_square)) {
     SourceLocation Start = Tok.getLocation();
@@ -241,7 +241,8 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
     SourceLocation CCLoc;
     SourceLocation EndLoc = ParseIndexedTypeNamePack(DS);
     if (DS.getTypeSpecType() == DeclSpec::TST_error)
-      return false;
+      return true;
+
     QualType Type = Actions.ActOnPackIndexingType(
         DS.getRepAsType().get(), DS.getPackIndexingExpr(), DS.getBeginLoc(),
         DS.getEllipsisLoc());
@@ -644,8 +645,7 @@ ExprResult Parser::tryParseCXXIdExpression(CXXScopeSpec &SS,
   }
 
   // Might be a pack index expression!
-  if (getLangOpts().CPlusPlus26)
-    E = tryParseCXXPackIndexingExpression(E);
+  E = tryParseCXXPackIndexingExpression(E);
 
   if (!E.isInvalid() && !E.isUnset() && Tok.is(tok::less))
     checkPotentialAngleBracket(E);
@@ -1876,7 +1876,7 @@ Parser::ParseCXXPseudoDestructor(Expr *Base, SourceLocation OpLoc,
   }
 
   // pack-index-specifier
-  if (getLangOpts().CPlusPlus26 && GetLookAheadToken(1).is(tok::ellipsis) &&
+  if (GetLookAheadToken(1).is(tok::ellipsis) &&
       GetLookAheadToken(2).is(tok::l_square)) {
     DeclSpec DS(AttrFactory);
     ParseIndexedTypeNamePack(DS);
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index e85b49af21f984..ef6d1e0d479b71 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -1090,8 +1090,12 @@ ExprResult Sema::ActOnPackIndexingExpr(Scope *S, Expr *PackExpression,
         << PackExpression;
     return ExprError();
   }
-  return BuildPackIndexingExpr(PackExpression, EllipsisLoc, IndexExpr,
+  ExprResult Res = BuildPackIndexingExpr(PackExpression, EllipsisLoc, IndexExpr,
                                RSquareLoc);
+  if(!Res.isInvalid())
+    Diag(Res.get()->getBeginLoc(), getLangOpts().CPlusPlus26?
+                                       diag::ext_pack_indexing : diag::warn_cxx23_pack_indexing);
+  return Res;
 }
 
 ExprResult
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 18c6afdd4c6290..94256c60608e07 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -9685,7 +9685,10 @@ QualType Sema::ActOnPackIndexingType(QualType Pattern, Expr *IndexExpr,
     Diag(EllipsisLoc, diag::err_expected_name_of_pack) << Pattern;
     return QualType();
   }
-  return BuildPackIndexingType(Pattern, IndexExpr, Loc, EllipsisLoc);
+  QualType Type = BuildPackIndexingType(Pattern, IndexExpr, Loc, EllipsisLoc);
+  if(!Type.isNull())
+      Diag(Loc, getLangOpts().CPlusPlus26? diag::ext_pack_indexing : diag::warn_cxx23_pack_indexing);
+  return Type;
 }
 
 QualType Sema::BuildPackIndexingType(QualType Pattern, Expr *IndexExpr,

>From 4c16c05e0021e2738a89bd59fc1a8780949b5959 Mon Sep 17 00:00:00 2001
From: Corentin Jabot <corentinjabot at gmail.com>
Date: Fri, 12 Jan 2024 19:04:25 +0100
Subject: [PATCH 5/5]  * Support pack indexing in older language modes  *
 improve error recovery for ill-formed pack indexing specifiers

This is important because we need to form a correct type for
an invalid pack indexing that we know is a type pack indexing so
that it is recognized as being part of a declaration.
---
 clang/lib/AST/Type.cpp                        |  7 +-
 clang/lib/Parse/ParseDeclCXX.cpp              | 65 +++++++++----------
 clang/lib/Parse/ParseExprCXX.cpp              |  9 +--
 clang/lib/Parse/ParseTentative.cpp            |  1 +
 clang/lib/Sema/SemaDeclCXX.cpp                |  2 +-
 clang/lib/Sema/SemaExprCXX.cpp                |  2 +-
 clang/lib/Sema/SemaTemplateVariadic.cpp       | 11 ++--
 clang/lib/Sema/SemaType.cpp                   | 16 +++--
 clang/test/AST/ast-dump-templates.cpp         |  4 +-
 .../CXX/dcl.decl/dcl.meaning/dcl.fct/p13.cpp  |  3 +-
 clang/test/Parser/cxx0x-ambig.cpp             |  3 +-
 clang/test/Parser/cxx0x-decl.cpp              |  8 ++-
 clang/test/Parser/cxx2b-pack-indexing.cpp     | 10 +--
 clang/test/SemaCXX/cxx2b-pack-indexing.cpp    |  8 +--
 14 files changed, 79 insertions(+), 70 deletions(-)

diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 76b38ab0b5725e..f531992ac12f05 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -3781,9 +3781,12 @@ PackIndexingType::PackIndexingType(const ASTContext &Context,
 }
 
 std::optional<unsigned> PackIndexingType::getSelectedIndex() const {
-  if(isDependentType())
+  if (isInstantiationDependentType())
+    return std::nullopt;
+  // Should only be not a constant for error recovery.
+  ConstantExpr *CE = dyn_cast<ConstantExpr>(getIndexExpr());
+  if (!CE)
     return std::nullopt;
-  ConstantExpr* CE = cast<ConstantExpr>(getIndexExpr());
   auto Index = CE->getResultAsAPSInt();
   assert(Index.isNonNegative() && "Invalid index");
   return static_cast<unsigned>(Index.getExtValue());
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 51c1e8f973df7f..30ba27ee3401ce 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -1223,46 +1223,41 @@ SourceLocation Parser::ParseIndexedTypeNamePack(DeclSpec &DS) {
     DS.SetTypeSpecType(DeclSpec::TST_typename_pack_indexing, StartLoc, PrevSpec,
                        DiagID, Type, Policy);
     return EndLoc;
-  } else {
-    if (!NextToken().is(tok::ellipsis) ||
-        !GetLookAheadToken(2).is(tok::l_square)) {
-      DS.SetTypeSpecError();
-      return Tok.getEndLoc();
-    }
-
-    ParsedType Ty = Actions.getTypeName(*Tok.getIdentifierInfo(),
-                                        Tok.getLocation(), getCurScope());
-    if (!Ty) {
-      DS.SetTypeSpecError();
-      return Tok.getEndLoc();
-    }
-    Type = Ty;
+  }
+  if (!NextToken().is(tok::ellipsis) ||
+      !GetLookAheadToken(2).is(tok::l_square)) {
+    DS.SetTypeSpecError();
+    return Tok.getEndLoc();
+  }
 
-    StartLoc = ConsumeToken();
-    EllipsisLoc = ConsumeToken();
-    BalancedDelimiterTracker T(*this, tok::l_square);
-    T.consumeOpen();
-    ExprResult IndexExpr = ParseConstantExpression();
-    if (T.consumeClose()) {
-      DS.SetTypeSpecError();
-      return IndexExpr.isInvalid() ? StartLoc : IndexExpr.get()->getEndLoc();
-    }
+  ParsedType Ty = Actions.getTypeName(*Tok.getIdentifierInfo(),
+                                      Tok.getLocation(), getCurScope());
+  if (!Ty) {
+    DS.SetTypeSpecError();
+    return Tok.getEndLoc();
+  }
+  Type = Ty;
 
-    DS.SetRangeStart(StartLoc);
-    DS.SetRangeEnd(T.getCloseLocation());
+  StartLoc = ConsumeToken();
+  EllipsisLoc = ConsumeToken();
+  BalancedDelimiterTracker T(*this, tok::l_square);
+  T.consumeOpen();
+  ExprResult IndexExpr = ParseConstantExpression();
+  T.consumeClose();
 
-    if (IndexExpr.isInvalid()) {
-      DS.SetTypeSpecError();
-      return T.getCloseLocation();
-    }
+  DS.SetRangeStart(StartLoc);
+  DS.SetRangeEnd(T.getCloseLocation());
 
-    DS.SetTypeSpecType(DeclSpec::TST_typename, StartLoc, PrevSpec, DiagID, Type,
-                       Policy);
-    DS.SetPackIndexingExpr(EllipsisLoc, IndexExpr.get());
-    return T.getCloseLocation();
-    ;
+  if (!IndexExpr.isUsable()) {
+    ASTContext &C = Actions.getASTContext();
+    IndexExpr = IntegerLiteral::Create(C, C.MakeIntValue(0, C.getSizeType()),
+                                       C.getSizeType(), SourceLocation());
   }
-  return SourceLocation();
+
+  DS.SetTypeSpecType(DeclSpec::TST_typename, StartLoc, PrevSpec, DiagID, Type,
+                     Policy);
+  DS.SetPackIndexingExpr(EllipsisLoc, IndexExpr.get());
+  return T.getCloseLocation();
 }
 
 void Parser::AnnotateExistingIndexedTypeNamePack(ParsedType T,
diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp
index 3984cf28e172d9..819ceb68530a31 100644
--- a/clang/lib/Parse/ParseExprCXX.cpp
+++ b/clang/lib/Parse/ParseExprCXX.cpp
@@ -233,21 +233,22 @@ bool Parser::ParseOptionalCXXScopeSpecifier(
     HasScopeSpecifier = true;
   }
 
-  else if (!HasScopeSpecifier &&
-           Tok.is(tok::identifier) && GetLookAheadToken(1).is(tok::ellipsis) &&
+  else if (!HasScopeSpecifier && Tok.is(tok::identifier) &&
+           GetLookAheadToken(1).is(tok::ellipsis) &&
            GetLookAheadToken(2).is(tok::l_square)) {
     SourceLocation Start = Tok.getLocation();
     DeclSpec DS(AttrFactory);
     SourceLocation CCLoc;
     SourceLocation EndLoc = ParseIndexedTypeNamePack(DS);
     if (DS.getTypeSpecType() == DeclSpec::TST_error)
-      return true;
+      return false;
 
     QualType Type = Actions.ActOnPackIndexingType(
         DS.getRepAsType().get(), DS.getPackIndexingExpr(), DS.getBeginLoc(),
         DS.getEllipsisLoc());
+
     if (Type.isNull())
-      return true;
+      return false;
 
     if (!TryConsumeToken(tok::coloncolon, CCLoc)) {
       AnnotateExistingIndexedTypeNamePack(ParsedType::make(Type), Start,
diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp
index 4fbab1dd5d9482..b68b6d33617987 100644
--- a/clang/lib/Parse/ParseTentative.cpp
+++ b/clang/lib/Parse/ParseTentative.cpp
@@ -1364,6 +1364,7 @@ Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename,
   case tok::identifier: {
     if (GetLookAheadToken(1).is(tok::ellipsis) &&
         GetLookAheadToken(2).is(tok::l_square)) {
+
       if (TryAnnotateTypeOrScopeToken())
         return TPResult::Error;
       if (Tok.is(tok::identifier))
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 5c753717430f72..3090ef15d0eaba 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -4496,7 +4496,7 @@ Sema::BuildMemInitializer(Decl *ConstructorD,
     return true;
   } else if (DS.getTypeSpecType() == TST_typename_pack_indexing) {
     BaseType =
-        ActOnPackIndexingType(DS.getRepAsType().get(), DS.getPackIndexingExpr(),
+        BuildPackIndexingType(DS.getRepAsType().get(), DS.getPackIndexingExpr(),
                               DS.getBeginLoc(), DS.getEllipsisLoc());
   } else {
     LookupResult R(*this, MemberOrBase, IdLoc, LookupOrdinaryName);
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index f2cc3661306c0c..cd3815b4f40c99 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -8090,7 +8090,7 @@ ExprResult Sema::ActOnPseudoDestructorExpr(Scope *S, Expr *Base,
     break;
   }
   case DeclSpec::TST_typename_pack_indexing: {
-    T = ActOnPackIndexingType(DS.getRepAsType().get(), DS.getPackIndexingExpr(),
+    T = BuildPackIndexingType(DS.getRepAsType().get(), DS.getPackIndexingExpr(),
                               DS.getBeginLoc(), DS.getEllipsisLoc());
     TLB.pushTrivial(getASTContext(),
                     cast<PackIndexingType>(T.getTypePtr())->getPattern(),
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index ef6d1e0d479b71..9fb83361a12475 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -1090,11 +1090,12 @@ ExprResult Sema::ActOnPackIndexingExpr(Scope *S, Expr *PackExpression,
         << PackExpression;
     return ExprError();
   }
-  ExprResult Res = BuildPackIndexingExpr(PackExpression, EllipsisLoc, IndexExpr,
-                               RSquareLoc);
-  if(!Res.isInvalid())
-    Diag(Res.get()->getBeginLoc(), getLangOpts().CPlusPlus26?
-                                       diag::ext_pack_indexing : diag::warn_cxx23_pack_indexing);
+  ExprResult Res =
+      BuildPackIndexingExpr(PackExpression, EllipsisLoc, IndexExpr, RSquareLoc);
+  if (!Res.isInvalid())
+    Diag(Res.get()->getBeginLoc(), getLangOpts().CPlusPlus26
+                                       ? diag::ext_pack_indexing
+                                       : diag::warn_cxx23_pack_indexing);
   return Res;
 }
 
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 94256c60608e07..ac7df98b5403dd 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -1669,7 +1669,7 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) {
     Expr *E = DS.getPackIndexingExpr();
     assert(E && "Didn't get an expression for pack indexing");
     QualType Pattern = S.GetTypeFromParser(DS.getRepAsType());
-    Result = S.ActOnPackIndexingType(Pattern, E, DS.getBeginLoc(),
+    Result = S.BuildPackIndexingType(Pattern, E, DS.getBeginLoc(),
                                      DS.getEllipsisLoc());
     if (Result.isNull()) {
       declarator.setInvalidType(true);
@@ -9681,13 +9681,17 @@ QualType Sema::BuildDecltypeType(Expr *E, bool AsUnevaluated) {
 QualType Sema::ActOnPackIndexingType(QualType Pattern, Expr *IndexExpr,
                                      SourceLocation Loc,
                                      SourceLocation EllipsisLoc) {
-  if (!Pattern->containsUnexpandedParameterPack()) {
-    Diag(EllipsisLoc, diag::err_expected_name_of_pack) << Pattern;
+  if (!IndexExpr)
     return QualType();
-  }
+
+  // Diagnose unexpanded packs but continue to improve recovery.
+  if (!Pattern->containsUnexpandedParameterPack())
+    Diag(Loc, diag::err_expected_name_of_pack) << Pattern;
+
   QualType Type = BuildPackIndexingType(Pattern, IndexExpr, Loc, EllipsisLoc);
-  if(!Type.isNull())
-      Diag(Loc, getLangOpts().CPlusPlus26? diag::ext_pack_indexing : diag::warn_cxx23_pack_indexing);
+  if (!Type.isNull())
+    Diag(Loc, getLangOpts().CPlusPlus26 ? diag::ext_pack_indexing
+                                        : diag::warn_cxx23_pack_indexing);
   return Type;
 }
 
diff --git a/clang/test/AST/ast-dump-templates.cpp b/clang/test/AST/ast-dump-templates.cpp
index 3d26eb917c1202..d25ef36dd4d32c 100644
--- a/clang/test/AST/ast-dump-templates.cpp
+++ b/clang/test/AST/ast-dump-templates.cpp
@@ -45,9 +45,9 @@ template <typename ...T> struct A {
   template <T ...x[3]> struct B {};
 };
 
-// CHECK1-LABEL: template <typename ...T> void f(T ...[3]) {
+// CHECK1-LABEL: template <typename ...T> void f() {
 // CHECK1-NEXT:    A<T[3]...> a;
-template <typename ...T> void f(T ...[3]) {
+template <typename ...T> void f() {
   A<T[3]...> a;
 }
 
diff --git a/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p13.cpp b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p13.cpp
index 9cc13b4efb80f1..a0d5e719731908 100644
--- a/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p13.cpp
+++ b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p13.cpp
@@ -57,7 +57,8 @@ template<typename T>
 void b(T[] ...);
 
 template<typename T>
-void c(T ... []); // expected-error{{type 'T[]' of function parameter pack does not contain any unexpanded parameter packs}}
+void c(T ... []); // expected-error {{expected expression}} \
+                  // expected-error {{'T' does not refer to the name of a parameter pack}}
 
 template<typename T>
 void d(T ... x[]); // expected-error{{type 'T[]' of function parameter pack does not contain any unexpanded parameter packs}}
diff --git a/clang/test/Parser/cxx0x-ambig.cpp b/clang/test/Parser/cxx0x-ambig.cpp
index 7f3398ad1386c2..9a266aac88512f 100644
--- a/clang/test/Parser/cxx0x-ambig.cpp
+++ b/clang/test/Parser/cxx0x-ambig.cpp
@@ -122,9 +122,8 @@ namespace ellipsis {
     void f(S(...args[sizeof(T)])); // expected-note {{here}} expected-note {{here}}
     void f(S(...args)[sizeof(T)]); // expected-error {{redeclared}}
     void f(S ...args[sizeof(T)]); // expected-error {{redeclared}}
-    void g(S(...[sizeof(T)])); // expected-note {{here}} expected-warning {{ISO C++11 requires a parenthesized pack declaration to have a name}}
+    void g(S(...[sizeof(T)])); // expected-warning {{ISO C++11 requires a parenthesized pack declaration to have a name}}
     void g(S(...)[sizeof(T)]); // expected-error {{function cannot return array type}}
-    void g(S ...[sizeof(T)]); // expected-error {{redeclared}}
     void h(T(...)); // function type, expected-error {{unexpanded parameter pack}}
     void h(T...); // pack expansion, ok
     void i(int(T...)); // expected-note {{here}}
diff --git a/clang/test/Parser/cxx0x-decl.cpp b/clang/test/Parser/cxx0x-decl.cpp
index 8be98d6ef29989..499d1b6c0c5361 100644
--- a/clang/test/Parser/cxx0x-decl.cpp
+++ b/clang/test/Parser/cxx0x-decl.cpp
@@ -213,8 +213,12 @@ struct MemberComponentOrder : Base {
 
 void NoMissingSemicolonHere(struct S
                             [3]);
-template<int ...N> void NoMissingSemicolonHereEither(struct S
-                                                     ... [N]);
+template<int ...N> void NoMissingSemicolonHereEither(struct S... [N]);
+// expected-error at -1 {{'S' does not refer to the name of a parameter pack}} \
+// expected-error at -1 {{declaration of anonymous struct must be a definition}} \
+// expected-error at -1 {{expected parameter declarator}}
+
+
 
 // This must be at the end of the file; we used to look ahead past the EOF token here.
 // expected-error at +1 {{expected unqualified-id}} expected-error at +1{{expected ';'}}
diff --git a/clang/test/Parser/cxx2b-pack-indexing.cpp b/clang/test/Parser/cxx2b-pack-indexing.cpp
index 1e92705dd66c82..40cde13dccceb2 100644
--- a/clang/test/Parser/cxx2b-pack-indexing.cpp
+++ b/clang/test/Parser/cxx2b-pack-indexing.cpp
@@ -1,19 +1,19 @@
-// RUN: %clang_cc1 -std=c++2c -verify -fsyntax-only %s
+// RUN: %clang_cc1 -std=c++2c -verify -fsyntax-only -Wno-c++26-extensions %s
 
 template<typename... T>
 struct S {
     T...1; // expected-error{{expected member name or ';' after declaration specifiers}}
     T...[; // expected-error{{expected expression}} \
            // expected-error{{expected ']'}} \
-           // expected-note {{to match this '['}}\
+           // expected-note {{to match this '['}} \
            // expected-warning{{declaration does not declare anything}}
 
     T...[1; // expected-error{{expected ']'}} \
-            // expected-note {{to match this '['}}\
-            // expected-warning{{declaration does not declare anything}}
+            // expected-note {{to match this '['}} \
+           // expected-warning{{declaration does not declare anything}}
 
     T...[]; // expected-error{{expected expression}} \
-            // expected-warning{{declaration does not declare anything}}
+           // expected-warning{{declaration does not declare anything}}
 
     void f(auto... v) {
         decltype(v...[1]) a = v...[1];
diff --git a/clang/test/SemaCXX/cxx2b-pack-indexing.cpp b/clang/test/SemaCXX/cxx2b-pack-indexing.cpp
index fbbb50f7f665c8..0d81f2798920f0 100644
--- a/clang/test/SemaCXX/cxx2b-pack-indexing.cpp
+++ b/clang/test/SemaCXX/cxx2b-pack-indexing.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++2c -verify %s
+// RUN: %clang_cc1 -std=c++2c -verify -Wno-c++26-extensions %s
 
 struct NotAPack;
 template <typename T, auto V, template<typename> typename Tp>
@@ -6,9 +6,9 @@ void not_pack() {
     int i = 0;
     i...[0]; // expected-error {{i does not refer to the name of a parameter pack}}
     V...[0]; // expected-error {{V does not refer to the name of a parameter pack}}
-    NotAPack...[0]; // expected-error{{'NotAPack' does not refer to the name of a parameter pack}}
-    T...[0];   // expected-error{{'T' does not refer to the name of a parameter pack}}
-    Tp...[0]; // expected-error{{'Tp' does not refer to the name of a parameter pack}}
+    NotAPack...[0] a; // expected-error{{'NotAPack' does not refer to the name of a parameter pack}}
+    T...[0] b;   // expected-error{{'T' does not refer to the name of a parameter pack}}
+    Tp...[0] c; // expected-error{{'Tp' does not refer to the name of a parameter pack}}
 }
 
 namespace invalid_indexes {



More information about the cfe-commits mailing list