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 <corentinja...@gmail.com> Date: Tue, 13 Sep 2022 18:29:34 +0200 Subject: [PATCH] [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 ed1a978b5382d71..dab670409077678 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 64ab3378957c702..2c0b89a0d12b21e 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 3e46a5da3fc043f..9e1c44eb19b805c 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 cc8dab97f8b010f..950da2b8d2c4560 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 24278016431837b..efd6558326099eb 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 53bc15e1b19f668..1c26f0512fa1342 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 6c147eb8f640623..a710766d1cf92f9 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 471deb14aba51fc..38e40ece425fb8f 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 682c869b0c58479..0ba172a4035fdb4 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 a9dde041bc22a6d..656dcbc42aae594 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 87f29c8ae10bd9a..bff4c1616c1d027 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 cec301dfca2817b..9d03800840fcd0b 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 82a503d01068d53..6b5f8210fa6d0e3 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 649b071cebb9404..6419762417371b9 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 30e0352c868637b..c7bbc3d6384306f 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 4561cca929c0d0b..4726cbbd1af50c9 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 501dc01200a1c34..cbebb79b91b52a6 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 fdd64f2abbe9375..ea084b328d61244 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 89ae1a2fa39546e..adbd5ec5d4b544f 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 4f54791b4c1e5ce..da6265a79316ff8 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 c4e931e220f69b5..fde7f7efa6d5b4b 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 6bb4bf14b873d7b..6f9fc93b11bedef 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 55c6b732b7081f4..d8204fc5a0d21a3 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 83af7998f683382..df76a5796d25bdd 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 ffa7c6802ea6e19..7026fca8554ce9f 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 373972eb6cab11b..2c290904a0ad0e3 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 2a62ac0175afb72..13e2be98aec2233 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 50ab6ea59be9d03..f045148e05d15bd 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 ab4a013de5f552c..8fd42d0405ba25f 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 8128219dd6f63c9..6d99cbf5740a456 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 c8e452e2feab0bf..ab83e833f869393 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 e4f5f40cd625996..673032fced19d58 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 0b52d99ad07f164..6b344d7d92d5141 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 a75f630e1a4c767..0abb1f00b4b421b 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 810b28f25fa18bf..22f55fe9aac904e 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 f3cbd1d0451ebe4..f9c2f95dc1c40c9 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 69616dcc07efe1f..7790617b4dcf82f 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 378437364767f69..c8d20704e05aaed 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 64521ce7182eee6..a61d9024d84fa68 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 573c90a36eeab36..518a168ee246fb0 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 06f2c8798b049f8..37c8be5d8a50cdd 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 35b1a93a54a6aab..1987ae6bb0dc94e 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 897810557976151..aa5c83f3a641934 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 79db094e098f8e6..236418291d549a0 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 8b653b1c4f8eaf4..521bed781855a1a 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 176d2149e73184e..0fd25263e14599f 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 781f24cb71ae994..2a559561c810c4d 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 44a40215b90dfb4..98833e931c6dad7 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 a6cd0bb9ea2a829..3ecf9eb65289f53 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 3688192e6cbe5c5..a198913897b07e0 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 75730ea888afb41..8d58ef5ee16d52d 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 fc39d6149c1cc65..acf627ff0363bd0 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 023411c7edc946b..75dc91518696f6a 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 766ebd90fded0c4..bbd4c252552d420 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 699e0985e595b65..c2279e96be67ef1 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 0f4e9e7f94c81e9..3b87becc156b8ef 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 560feafa1857cb3..425ec618476931a 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 e24f710fdedd4e2..f7b9d6b8f86d756 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 42b48d230af7a97..3df8b4faad8c2ff 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 d7d0c0e5bb21b47..0f6d6aa03ed9dff 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 0161ad10f3f2381..98eec9a9873b03e 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 59be6828fafabf6..fd95848751fc201 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 24e91a22fd6884f..ccc3c0f1e0c1002 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 000000000000000..cf8124617b3c681 --- /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 000000000000000..1e92705dd66c829 --- /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 000000000000000..31138fb6730c433 --- /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 df630f66f0b946a..dc195f2a2ffef69 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 fd03c48ba1a42aa..c997b5c105e21e1 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 621439d0bae9666..814bf9e66862f21 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> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits