Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jan 27, 2024
Merged
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())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there good reason this isn't an assert?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's called from places that don't have another way to check whether an it would be valid.
Now that we don't store an index, we have to look at the expression to establish what the index is.

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);
}
Comment on lines +4961 to +4965
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this correct? Calling getTypeInfo on a PackIndexingType where hasSelectedType is false will cause it to infinitely recurse as it tries to desugar itself over and over again. This seems to happen if the type is broken, like the not_pack examples in cxx2c-pack-indexing.cpp.

Maybe the issue lies elsewhere, but something is strange with the error recovery for these types. Perhaps because isDependentType isn't true for them when they are broken?


QualType getSelectedType() const {
assert(hasSelectedType() && "Type is dependant");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trying to dump/print a broken pack indexing expression/type breaks on this assertion. Try adding -ast-dump to cxx2c-pack-indexing.cpp and see what happens.

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