Skip to content

Commit

Permalink
[Clang][C++26] Implement Pack Indexing (P2662R3). (#72644)
Browse files Browse the repository at this point in the history
Implements https://isocpp.org/files/papers/P2662R3.pdf

The feature is exposed as an extension in older language modes.
Mangling is not yet supported and that is something we will have to do before release.
  • Loading branch information
cor3ntin authored Jan 27, 2024
1 parent 1f13203 commit ad1a65f
Show file tree
Hide file tree
Showing 76 changed files with 1,520 additions and 29 deletions.
3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ C++23 Feature Support
C++2c Feature Support
^^^^^^^^^^^^^^^^^^^^^

- Implemented `P2662R3 Pack Indexing <https://wg21.link/P2662R3>`_.


Resolutions to C++ Defect Reports
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Expand Down
7 changes: 6 additions & 1 deletion clang/include/clang-c/Index.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
8 changes: 8 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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 FullySubstituted = false,
ArrayRef<QualType> Expansions = {},
int Index = -1) const;

/// Unary type transforms
QualType getUnaryTransformType(QualType BaseType, QualType UnderlyingType,
UnaryTransformType::UTTKind UKind) const;
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/AST/ASTNodeTraverser.h
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/AST/ComputeDependence.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ class ArrayTypeTraitExpr;
class ExpressionTraitExpr;
class CXXNoexceptExpr;
class PackExpansionExpr;
class PackIndexingExpr;
class SubstNonTypeTemplateParmExpr;
class CoroutineSuspendExpr;
class DependentCoawaitExpr;
Expand Down Expand Up @@ -150,6 +151,7 @@ ExprDependence computeDependence(ArrayTypeTraitExpr *E);
ExprDependence computeDependence(ExpressionTraitExpr *E);
ExprDependence computeDependence(CXXNoexceptExpr *E, CanThrowResult CT);
ExprDependence computeDependence(PackExpansionExpr *E);
ExprDependence computeDependence(PackIndexingExpr *E);
ExprDependence computeDependence(SubstNonTypeTemplateParmExpr *E);
ExprDependence computeDependence(CoroutineSuspendExpr *E);
ExprDependence computeDependence(DependentCoawaitExpr *E);
Expand Down
99 changes: 99 additions & 0 deletions clang/include/clang/AST/ExprCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -4331,6 +4331,105 @@ 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];

size_t TransformedExpressions;

PackIndexingExpr(QualType Type, SourceLocation EllipsisLoc,
SourceLocation RSquareLoc, Expr *PackIdExpr, Expr *IndexExpr,
ArrayRef<Expr *> SubstitutedExprs = {})
: Expr(PackIndexingExprClass, Type, VK_LValue, OK_Ordinary),
EllipsisLoc(EllipsisLoc), RSquareLoc(RSquareLoc),
SubExprs{PackIdExpr, IndexExpr},
TransformedExpressions(SubstitutedExprs.size()) {

auto *Exprs = getTrailingObjects<Expr *>();
std::uninitialized_copy(SubstitutedExprs.begin(), SubstitutedExprs.end(),
Exprs);

setDependence(computeDependence(this));
if (!isInstantiationDependent())
setValueKind(getSelectedExpr()->getValueKind());
}

/// 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,
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]); }

std::optional<unsigned> getSelectedIndex() const {
if (isInstantiationDependent())
return std::nullopt;
ConstantExpr *CE = cast<ConstantExpr>(getIndexExpr());
auto Index = CE->getResultAsAPSInt();
assert(Index.isNonNegative() && "Invalid index");
return static_cast<unsigned>(Index.getExtValue());
}

Expr *getSelectedExpr() const {
std::optional<unsigned> Index = getSelectedIndex();
assert(Index && "extracting the indexed expression of a dependant pack");
return getTrailingObjects<Expr *>()[*Index];
}

ArrayRef<Expr *> getExpressions() const {
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 {
Expand Down
11 changes: 11 additions & 0 deletions clang/include/clang/AST/RecursiveASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -1065,6 +1065,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()));
Expand Down Expand Up @@ -1343,6 +1348,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()));
})
Expand Down Expand Up @@ -2854,6 +2864,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, {})
Expand Down
67 changes: 67 additions & 0 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -4934,6 +4934,73 @@ 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;

protected:
friend class ASTContext; // ASTContext creates these.
PackIndexingType(const ASTContext &Context, QualType Canonical,
QualType Pattern, Expr *IndexExpr,
ArrayRef<QualType> Expansions = {});

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() + *getSelectedIndex());
}

std::optional<unsigned> getSelectedIndex() const;

bool hasSelectedType() const { return getSelectedIndex() != std::nullopt; }

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:
Expand Down
28 changes: 28 additions & 0 deletions clang/include/clang/AST/TypeLoc.h
Original file line number Diff line number Diff line change
Expand Up @@ -2059,6 +2059,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
Expand Down
14 changes: 14 additions & 0 deletions clang/include/clang/AST/TypeProperties.td
Original file line number Diff line number Diff line change
Expand Up @@ -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() }];
Expand Down
11 changes: 10 additions & 1 deletion clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -5721,9 +5721,18 @@ 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<
"invalid index %0 for pack %1 of size %2">;

def ext_pack_indexing : ExtWarn<
"pack indexing is a C++2c extension">, InGroup<CXX26>;
def warn_cxx23_pack_indexing : Warning<
"pack indexing is incompatible with C++ standards before C++2c">,
DefaultIgnore, InGroup<CXXPre26Compat>;

def err_fold_expression_packs_both_sides : Error<
"binary fold expression has unexpanded parameter packs in both operands">;
def err_fold_expression_empty : Error<
Expand Down
12 changes: 7 additions & 5 deletions clang/include/clang/Basic/Specifiers.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,22 +79,24 @@ namespace clang {
TST_enum,
TST_union,
TST_struct,
TST_class, // C++ class type
TST_interface, // C++ (Microsoft-specific) __interface type
TST_typename, // Typedef, C++ class-name or enum name, etc.
TST_class, // C++ class type
TST_interface, // C++ (Microsoft-specific) __interface type
TST_typename, // Typedef, C++ class-name or enum name, etc.
TST_typeofType, // C23 (and GNU extension) typeof(type-name)
TST_typeofExpr, // C23 (and GNU extension) typeof(expression)
TST_typeof_unqualType, // C23 typeof_unqual(type-name)
TST_typeof_unqualExpr, // C23 typeof_unqual(expression)
TST_decltype, // C++11 decltype
TST_decltype, // C++11 decltype
#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) TST_##Trait,
#include "clang/Basic/TransformTypeTraits.def"
TST_auto, // C++11 auto
TST_decltype_auto, // C++1y decltype(auto)
TST_auto_type, // __auto_type extension
TST_unknown_anytype, // __unknown_anytype extension
TST_atomic, // C11 _Atomic
#define GENERIC_IMAGE_TYPE(ImgType, Id) TST_##ImgType##_t, // OpenCL image types
TST_typename_pack_indexing,
#define GENERIC_IMAGE_TYPE(ImgType, Id) \
TST_##ImgType##_t, // OpenCL image types
#include "clang/Basic/OpenCLImageTypes.def"
TST_error // erroneous type
};
Expand Down
Loading

0 comments on commit ad1a65f

Please sign in to comment.