Skip to content
Open
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
f8bd65d
Enumerating expansion statements mostly work
Sirraide Oct 24, 2025
b864b4c
Fix some instantiation issues and add codegen tests
Sirraide Oct 24, 2025
cb66211
Support pack expansion in enumerating expansion statements
Sirraide Oct 24, 2025
34960d6
Iterating expansion statements
Sirraide Oct 25, 2025
dbf5fec
Dependent expansion statements
Sirraide Oct 25, 2025
a9e1d55
Sema for destructuring expansion statements
Sirraide Oct 25, 2025
86cab85
Implement CWG 3044
Sirraide Oct 26, 2025
1deea2b
Add codegen tests for destructuring expansion statements
Sirraide Oct 26, 2025
a365f3e
Add tests for break/continue
Sirraide Oct 26, 2025
355b761
Disallow (non-local) labels in expansion statements
Sirraide Oct 26, 2025
3603a54
Disallow case/default statements in expansion statements
Sirraide Oct 26, 2025
316f81a
Add another case/default test
Sirraide Oct 26, 2025
9e43b8c
Add comment explaining why we diagnose this here
Sirraide Oct 26, 2025
66ca06c
Proper support for local labels
Sirraide Oct 26, 2025
278977a
Make substatement memory representation less horrible
Sirraide Oct 26, 2025
7ba15b0
Update out-of-date comment
Sirraide Oct 26, 2025
fb7a000
Check for unexpanded parameter packs
Sirraide Oct 26, 2025
9264ff0
Add -fexpansion-limit
Sirraide Oct 26, 2025
b0468c7
Add release note
Sirraide Oct 26, 2025
a2b72d1
Add StmtPrinter/TextNodeDumper/Serialisation tests
Sirraide Oct 26, 2025
a217f90
Implement CWG 3061
Sirraide Oct 26, 2025
a9f4244
Add a test for CWG 3048
Sirraide Oct 26, 2025
bfdb5ed
Add a test for lifetime extension (CWG 3034)
Sirraide Oct 26, 2025
6739929
Fix lifetime extension in templates
Sirraide Oct 27, 2025
ea2a347
Remove commented-out code
Sirraide Oct 27, 2025
d10d82a
ASTImporter
Sirraide Oct 27, 2025
1ae64d3
Minor fixes
Sirraide Oct 27, 2025
9b09c05
clang-format
Sirraide Oct 27, 2025
8e6a4af
clang-format, again
Sirraide Oct 27, 2025
84a3d71
Update LanguageExtensions.rst
Sirraide Oct 27, 2025
ddede95
Add extension warning test
Sirraide Oct 27, 2025
b188e7c
Apply code review suggestions
Sirraide Oct 27, 2025
5a0c30f
Handle returning from an expansion properly in constant evaluation
Sirraide Oct 27, 2025
61d7899
Merge branch 'main' into expansion-statements
Sirraide Oct 29, 2025
17d05ae
Actually make this test expand to nothing
Sirraide Oct 29, 2025
4965e7d
Amend diagnostic wording
Sirraide Nov 3, 2025
79af6ec
Only move up to the parent context during expansion
Sirraide Nov 3, 2025
3642092
Add tests for destructuring using tuple_size
Sirraide Nov 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1784,6 +1784,7 @@ Pack Indexing __cpp_pack_indexing C
``= delete ("should have a reason");`` __cpp_deleted_function C++26 C++03
Variadic Friends __cpp_variadic_friend C++26 C++03
Trivial Relocatability __cpp_trivial_relocatability C++26 C++03
Expansion Statements C++26 C++03
--------------------------------------------- -------------------------------- ------------- -------------
Designated initializers (N494) C99 C89
``_Complex`` (N693) C99 C89, C++
Expand Down
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ C++2c Feature Support
At this timem, references to constexpr and decomposition of *tuple-like* types are not supported
(only arrays and aggregates are).

- Implemented `P1306R5 <https://wg21.link/P1306R5>`_ Expansion Statements.

C++23 Feature Support
^^^^^^^^^^^^^^^^^^^^^

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 @@ -959,6 +959,12 @@ class ASTNodeTraverser
}
}

void VisitExpansionStmtDecl(const ExpansionStmtDecl *Node) {
Visit(Node->getExpansionPattern());
if (Traversal != TK_IgnoreUnlessSpelledInSource)
Visit(Node->getInstantiations());
}

void VisitCallExpr(const CallExpr *Node) {
for (const auto *Child :
make_filter_range(Node->children(), [this](const Stmt *Child) {
Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/AST/ComputeDependence.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ class DesignatedInitExpr;
class ParenListExpr;
class PseudoObjectExpr;
class AtomicExpr;
class CXXExpansionInitListExpr;
class ArraySectionExpr;
class OMPArrayShapingExpr;
class OMPIteratorExpr;
Expand Down Expand Up @@ -191,6 +192,8 @@ ExprDependence computeDependence(ParenListExpr *E);
ExprDependence computeDependence(PseudoObjectExpr *E);
ExprDependence computeDependence(AtomicExpr *E);

ExprDependence computeDependence(CXXExpansionInitListExpr *E);

ExprDependence computeDependence(ArraySectionExpr *E);
ExprDependence computeDependence(OMPArrayShapingExpr *E);
ExprDependence computeDependence(OMPIteratorExpr *E);
Expand Down
4 changes: 3 additions & 1 deletion clang/include/clang/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1254,7 +1254,9 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> {
if (getKind() != Decl::Var && getKind() != Decl::Decomposition)
return false;
if (const DeclContext *DC = getLexicalDeclContext())
return DC->getRedeclContext()->isFunctionOrMethod();
return DC->getEnclosingNonExpansionStatementContext()
->getRedeclContext()
->isFunctionOrMethod();
return false;
}

Expand Down
11 changes: 11 additions & 0 deletions clang/include/clang/AST/DeclBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -2195,6 +2195,8 @@ class DeclContext {
return getDeclKind() == Decl::RequiresExprBody;
}

bool isExpansionStmt() const { return getDeclKind() == Decl::ExpansionStmt; }

bool isNamespace() const { return getDeclKind() == Decl::Namespace; }

bool isStdNamespace() const;
Expand Down Expand Up @@ -2292,6 +2294,15 @@ class DeclContext {
return const_cast<DeclContext *>(this)->getOuterLexicalRecordContext();
}

/// Retrieve the innermost enclosing context that doesn't belong to an
/// expansion statement. Returns 'this' if this context is not an expansion
/// statement.
DeclContext *getEnclosingNonExpansionStatementContext();
const DeclContext *getEnclosingNonExpansionStatementContext() const {
return const_cast<DeclContext *>(this)
->getEnclosingNonExpansionStatementContext();
}

/// Test if this context is part of the enclosing namespace set of
/// the context NS, as defined in C++0x [namespace.def]p9. If either context
/// isn't a namespace, this is equivalent to Equals().
Expand Down
49 changes: 49 additions & 0 deletions clang/include/clang/AST/DeclTemplate.h
Original file line number Diff line number Diff line change
Expand Up @@ -3343,6 +3343,55 @@ class TemplateParamObjectDecl : public ValueDecl,
static bool classofKind(Kind K) { return K == TemplateParamObject; }
};

/// Represents a C++26 expansion statement declaration.
///
/// This is a bit of a hack, since expansion statements shouldn't really be
/// "declarations" per se (they don't declare anything). Nevertheless, we *do*
/// need them to be declaration *contexts*, because the DeclContext is used to
/// compute the "template depth" of entities enclosed therein. In particular,
/// the "template depth" is used to find instantiations of parameter variables,
/// and a lambda enclosed within an expansion statement cannot compute its
/// template depth without a pointer to the enclosing expansion statement.
class ExpansionStmtDecl : public Decl, public DeclContext {
CXXExpansionStmt *Expansion = nullptr;
TemplateParameterList *TParams;
CXXExpansionInstantiationStmt *Instantiations = nullptr;

ExpansionStmtDecl(DeclContext *DC, SourceLocation Loc,
TemplateParameterList *TParams);

public:
friend class ASTDeclReader;

static ExpansionStmtDecl *Create(ASTContext &C, DeclContext *DC,
SourceLocation Loc,
TemplateParameterList *TParams);
static ExpansionStmtDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID);

CXXExpansionStmt *getExpansionPattern() { return Expansion; }
const CXXExpansionStmt *getExpansionPattern() const { return Expansion; }
void setExpansionPattern(CXXExpansionStmt *S) { Expansion = S; }

CXXExpansionInstantiationStmt *getInstantiations() { return Instantiations; }
const CXXExpansionInstantiationStmt *getInstantiations() const {
return Instantiations;
}

void setInstantiations(CXXExpansionInstantiationStmt *S) {
Instantiations = S;
}

NonTypeTemplateParmDecl *getIndexTemplateParm() const {
return cast<NonTypeTemplateParmDecl>(TParams->getParam(0));
}
TemplateParameterList *getTemplateParameters() const { return TParams; }

SourceRange getSourceRange() const override LLVM_READONLY;

static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classofKind(Kind K) { return K == ExpansionStmt; }
};

inline NamedDecl *getAsNamedDecl(TemplateParameter P) {
if (auto *PD = P.dyn_cast<TemplateTypeParmDecl *>())
return PD;
Expand Down
150 changes: 150 additions & 0 deletions clang/include/clang/AST/ExprCXX.h
Original file line number Diff line number Diff line change
Expand Up @@ -5501,6 +5501,156 @@ class BuiltinBitCastExpr final
}
};

/// Represents an expansion-init-list to be expanded over by an expansion
/// statement.
class CXXExpansionInitListExpr final
: public Expr,
llvm::TrailingObjects<CXXExpansionInitListExpr, Expr *> {
friend class ASTStmtReader;
friend TrailingObjects;

const unsigned NumExprs;
SourceLocation LBraceLoc;
SourceLocation RBraceLoc;

CXXExpansionInitListExpr(EmptyShell ES, unsigned NumExprs);
CXXExpansionInitListExpr(ArrayRef<Expr *> Exprs, SourceLocation LBraceLoc,
SourceLocation RBraceLoc);

public:
static CXXExpansionInitListExpr *Create(const ASTContext &C,
ArrayRef<Expr *> Exprs,
SourceLocation LBraceLoc,
SourceLocation RBraceLoc);

static CXXExpansionInitListExpr *
CreateEmpty(const ASTContext &C, EmptyShell Empty, unsigned NumExprs);

ArrayRef<Expr *> getExprs() const { return getTrailingObjects(NumExprs); }
MutableArrayRef<Expr *> getExprs() { return getTrailingObjects(NumExprs); }
unsigned getNumExprs() const { return NumExprs; }

bool containsPackExpansion() const;

SourceLocation getBeginLoc() const { return getLBraceLoc(); }
SourceLocation getEndLoc() const { return getRBraceLoc(); }

SourceLocation getLBraceLoc() const { return LBraceLoc; }
SourceLocation getRBraceLoc() const { return RBraceLoc; }

child_range children() {
const_child_range CCR =
const_cast<const CXXExpansionInitListExpr *>(this)->children();
return child_range(cast_away_const(CCR.begin()),
cast_away_const(CCR.end()));
}

const_child_range children() const {
Stmt **Stmts = getTrailingStmts();
return const_child_range(Stmts, Stmts + NumExprs);
}

static bool classof(const Stmt *T) {
return T->getStmtClass() == CXXExpansionInitListExprClass;
}

private:
Stmt **getTrailingStmts() const {
return reinterpret_cast<Stmt **>(const_cast<Expr **>(getTrailingObjects()));
}
};

/// Helper that selects an expression from an expansion init list depending
/// on the current expansion index.
class CXXExpansionInitListSelectExpr : public Expr {
friend class ASTStmtReader;

enum SubExpr { RANGE, INDEX, COUNT };
Expr *SubExprs[COUNT];

public:
CXXExpansionInitListSelectExpr(EmptyShell Empty);
CXXExpansionInitListSelectExpr(const ASTContext &C,
CXXExpansionInitListExpr *Range, Expr *Idx);

CXXExpansionInitListExpr *getRangeExpr() {
return cast<CXXExpansionInitListExpr>(SubExprs[RANGE]);
}

const CXXExpansionInitListExpr *getRangeExpr() const {
return cast<CXXExpansionInitListExpr>(SubExprs[RANGE]);
}

void setRangeExpr(CXXExpansionInitListExpr *E) { SubExprs[RANGE] = E; }

Expr *getIndexExpr() { return SubExprs[INDEX]; }
const Expr *getIndexExpr() const { return SubExprs[INDEX]; }
void setIndexExpr(Expr *E) { SubExprs[INDEX] = E; }

SourceLocation getBeginLoc() const { return getRangeExpr()->getBeginLoc(); }
SourceLocation getEndLoc() const { return getRangeExpr()->getEndLoc(); }

child_range children() {
return child_range(reinterpret_cast<Stmt **>(SubExprs),
reinterpret_cast<Stmt **>(SubExprs + COUNT));
}

const_child_range children() const {
return const_child_range(
reinterpret_cast<Stmt **>(const_cast<Expr **>(SubExprs)),
reinterpret_cast<Stmt **>(const_cast<Expr **>(SubExprs + COUNT)));
}

static bool classof(const Stmt *T) {
return T->getStmtClass() == CXXExpansionInitListSelectExprClass;
}
};

class CXXDestructuringExpansionSelectExpr : public Expr {
friend class ASTStmtReader;

DecompositionDecl *Decomposition;
Expr *Index;

public:
CXXDestructuringExpansionSelectExpr(EmptyShell Empty);
CXXDestructuringExpansionSelectExpr(const ASTContext &C,
DecompositionDecl *Decomposition,
Expr *Index);

DecompositionDecl *getDecompositionDecl() {
return cast<DecompositionDecl>(Decomposition);
}

const DecompositionDecl *getDecompositionDecl() const {
return cast<DecompositionDecl>(Decomposition);
}

void setDecompositionDecl(DecompositionDecl *E) { Decomposition = E; }

Expr *getIndexExpr() { return Index; }
const Expr *getIndexExpr() const { return Index; }
void setIndexExpr(Expr *E) { Index = E; }

SourceLocation getBeginLoc() const { return Decomposition->getBeginLoc(); }
SourceLocation getEndLoc() const { return Decomposition->getEndLoc(); }

child_range children() {
return child_range(reinterpret_cast<Stmt **>(&Index),
reinterpret_cast<Stmt **>(&Index + 1));
}

const_child_range children() const {
return const_child_range(
reinterpret_cast<Stmt **>(const_cast<Expr **>(&Index)),
reinterpret_cast<Stmt **>(const_cast<Expr **>(&Index + 1)));
}

static bool classof(const Stmt *T) {
return T->getStmtClass() == CXXDestructuringExpansionSelectExprClass;
}
};

} // namespace clang

#endif // LLVM_CLANG_AST_EXPRCXX_H
17 changes: 17 additions & 0 deletions clang/include/clang/AST/RecursiveASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -1881,6 +1881,14 @@ DEF_TRAVERSE_DECL(UsingShadowDecl, {})

DEF_TRAVERSE_DECL(ConstructorUsingShadowDecl, {})

DEF_TRAVERSE_DECL(ExpansionStmtDecl, {
if (D->getInstantiations() &&
getDerived().shouldVisitTemplateInstantiations())
TRY_TO(TraverseStmt(D->getInstantiations()));

TRY_TO(TraverseStmt(D->getExpansionPattern()));
})

DEF_TRAVERSE_DECL(OMPThreadPrivateDecl, {
for (auto *I : D->varlist()) {
TRY_TO(TraverseStmt(I));
Expand Down Expand Up @@ -3117,6 +3125,15 @@ DEF_TRAVERSE_STMT(RequiresExpr, {
TRY_TO(TraverseConceptRequirement(Req));
})

DEF_TRAVERSE_STMT(CXXEnumeratingExpansionStmt, {})
DEF_TRAVERSE_STMT(CXXIteratingExpansionStmt, {})
DEF_TRAVERSE_STMT(CXXDestructuringExpansionStmt, {})
DEF_TRAVERSE_STMT(CXXDependentExpansionStmt, {})
DEF_TRAVERSE_STMT(CXXExpansionInstantiationStmt, {})
DEF_TRAVERSE_STMT(CXXExpansionInitListExpr, {})
DEF_TRAVERSE_STMT(CXXExpansionInitListSelectExpr, {})
DEF_TRAVERSE_STMT(CXXDestructuringExpansionSelectExpr, {})

// These literals (all of them) do not need any action.
DEF_TRAVERSE_STMT(IntegerLiteral, {})
DEF_TRAVERSE_STMT(FixedPointLiteral, {})
Expand Down
Loading
Loading