[clang] [Clang] [C++26] Expansion Statements (Part 1: AST) (PR #169680)
Shafik Yaghmour via cfe-commits
cfe-commits at lists.llvm.org
Thu Apr 16 15:51:55 PDT 2026
================
@@ -524,6 +525,520 @@ class CoreturnStmt : public Stmt {
}
};
+/// CXXExpansionStmtPattern - Represents an unexpanded C++ expansion statement.
+///
+/// There are four kinds of expansion statements.
+///
+/// 1. Enumerating expansion statements.
+/// 2. Iterating expansion statements.
+/// 3. Destructuring expansion statements.
+/// 4. Dependent expansion statements.
+///
+/// 1. An 'enumerating' expansion statement is one whose expansion-initializer
+/// is a brace-enclosed expression-list; this list is syntactically similar to
+/// an initializer list, but it isn't actually an expression in and of itself
+/// (in that it is never evaluated or emitted) and instead is just treated as
+/// a group of expressions. The expansion initializer of this is always a
+/// syntactic-form 'InitListExpr'.
+///
+/// Example:
+/// \verbatim
+/// template for (auto x : { 1, 2, 3 }) {
+/// // ...
+/// }
+/// \endverbatim
+///
+/// Note that the expression-list may also contain pack expansions, e.g.
+/// '{ 1, xs... }', in which case the expansion size is dependent.
+///
+/// Here, the '{ 1, 2, 3 }' is parsed as an 'InitListExpr'. This node
+/// handles storing (and pack-expanding) the individual expressions.
+///
+/// Sema then wraps this with a 'CXXExpansionSelectExpr', which also
+/// contains a reference to an integral NTTP that is used as the expansion
+/// index; this index is either dependent (if the expansion-size is dependent),
+/// or set to a value of I in the I-th expansion during the expansion process.
+///
+/// The actual expansion is done by 'BuildCXXExpansionSelectExpr()': for
+/// example, during the 2nd expansion of '{ a, b, c }', I is equal to 1, and
+/// BuildCXXExpansionSelectExpr(), when called via TreeTransform,
+/// 'instantiates' the expression '{ a, b, c }' to just 'b'.
+///
+/// 2. Represents an unexpanded iterating expansion statement.
+///
+/// An 'iterating' expansion statement is one whose expansion-initializer is a
+/// a range (i.e. it has a corresponding 'begin()'/'end()' pair that is
+/// determined based on a number of conditions as stated in [stmt.expand] and
+/// [stmt.ranged]).
+///
+/// The expression used to compute the size of the expansion is not stored and
+/// is only created at the moment of expansion.
+///
+/// Example:
+/// \verbatim
+/// static constexpr std::string_view foo = "1234";
+/// template for (auto x : foo) {
+/// // ...
+/// }
+/// \endverbatim
+///
+/// 3. Represents an unexpanded destructuring expansion statement.
+///
+/// A 'destructuring' expansion statement is any expansion statement that is
+/// not enumerating or iterating (i.e. destructuring is the last thing we try,
+/// and if it doesn't work, the program is ill-formed).
+///
+/// This essentially involves treating the expansion-initializer as the
+/// initializer of a structured-binding declarations, with the number of
+/// bindings and expansion size determined by the usual means (array size,
+/// std::tuple_size, etc.).
+///
+/// Example:
+/// \verbatim
+/// std::array<int, 3> a {1, 2, 3};
+/// template for (auto x : a) {
+/// // ...
+/// }
+/// \endverbatim
+///
+/// Sema wraps the initializer with a CXXExpansionSelectExpr, which selects a
+/// binding based on the current expansion index.
+///
+/// 4. Represents an expansion statement whose expansion-initializer is
+/// type-dependent.
+///
+/// This will be instantiated as either an iterating or destructuring expansion
+/// statement. Dependent expansion statements can never be enumerating, even if
+/// the expansion size is dependent because the expression-list contains a pack.
+///
+/// Example:
+/// \verbatim
+/// template <typename T>
+/// void f() {
+/// template for (auto x : T()) {
+/// // ...
+/// }
+/// }
+/// \endverbatim
+///
+/// \see CXXExpansionStmtDecl for more documentation on expansion statements.
+class CXXExpansionStmtPattern final
+ : public Stmt,
+ llvm::TrailingObjects<CXXExpansionStmtPattern, Stmt *> {
+ friend class ASTStmtReader;
+ friend TrailingObjects;
+
+public:
+ enum class ExpansionStmtKind : uint8_t {
+ Enumerating,
+ Iterating,
+ Destructuring,
+ Dependent,
+ };
+
+private:
+ ExpansionStmtKind PatternKind;
+ SourceLocation LParenLoc;
+ SourceLocation ColonLoc;
+ SourceLocation RParenLoc;
+ CXXExpansionStmtDecl *ParentDecl;
+
+ enum SubStmt {
+ INIT,
+ VAR,
+ BODY,
+ FIRST_CHILD_STMT,
+ COUNT_Enumerating = FIRST_CHILD_STMT,
+
+ // Dependent expansion initializer.
+ EXPANSION_INITIALIZER = FIRST_CHILD_STMT,
----------------
shafik wrote:
The use of `FIRST_CHILD_STMT` over again here deserves a detailed comment.
https://github.com/llvm/llvm-project/pull/169680
More information about the cfe-commits
mailing list