[clang] [Clang] [C++26] Expansion Statements (Part 1: AST) (PR #169680)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Jul 1 08:08:36 PDT 2026
https://github.com/Sirraide updated https://github.com/llvm/llvm-project/pull/169680
>From 5db185c78b21bd8f3aef98ab2aaaed3db554bf16 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Tue, 25 Nov 2025 17:18:05 +0100
Subject: [PATCH] [Clang] [C++26] Expansion Statements (Part 1)
---
clang/include/clang/AST/ASTNodeTraverser.h | 6 +
clang/include/clang/AST/Decl.h | 6 +-
clang/include/clang/AST/DeclBase.h | 13 +
clang/include/clang/AST/DeclTemplate.h | 125 ++++
clang/include/clang/AST/ExprCXX.h | 44 ++
clang/include/clang/AST/RecursiveASTVisitor.h | 12 +
clang/include/clang/AST/StmtCXX.h | 583 ++++++++++++++++++
clang/include/clang/AST/TextNodeDumper.h | 3 +
clang/include/clang/Basic/DeclNodes.td | 1 +
clang/include/clang/Basic/StmtNodes.td | 8 +
clang/include/clang/Sema/Scope.h | 18 +
.../include/clang/Serialization/ASTBitCodes.h | 10 +
clang/lib/AST/ASTImporter.cpp | 119 ++++
clang/lib/AST/DeclBase.cpp | 14 +-
clang/lib/AST/DeclPrinter.cpp | 6 +
clang/lib/AST/DeclTemplate.cpp | 23 +
clang/lib/AST/Expr.cpp | 1 +
clang/lib/AST/ExprCXX.cpp | 12 +
clang/lib/AST/ExprClassification.cpp | 1 +
clang/lib/AST/ExprConstant.cpp | 1 +
clang/lib/AST/ItaniumMangle.cpp | 8 +-
clang/lib/AST/StmtCXX.cpp | 156 +++++
clang/lib/AST/StmtPrinter.cpp | 35 +-
clang/lib/AST/StmtProfile.cpp | 16 +
clang/lib/AST/TextNodeDumper.cpp | 32 +-
clang/lib/CodeGen/CGDecl.cpp | 3 +
clang/lib/CodeGen/CGStmt.cpp | 4 +
clang/lib/Sema/IdentifierResolver.cpp | 7 +-
clang/lib/Sema/Scope.cpp | 1 +
clang/lib/Sema/Sema.cpp | 7 +-
clang/lib/Sema/SemaDecl.cpp | 4 +-
clang/lib/Sema/SemaExceptionSpec.cpp | 8 +
clang/lib/Sema/SemaExpr.cpp | 5 +-
clang/lib/Sema/SemaExprCXX.cpp | 1 -
clang/lib/Sema/SemaLambda.cpp | 28 +-
clang/lib/Sema/SemaLookup.cpp | 4 +-
clang/lib/Sema/SemaStmtAttr.cpp | 12 +
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 5 +
clang/lib/Sema/TreeTransform.h | 18 +
clang/lib/Serialization/ASTCommon.cpp | 1 +
clang/lib/Serialization/ASTReaderDecl.cpp | 12 +
clang/lib/Serialization/ASTReaderStmt.cpp | 46 ++
clang/lib/Serialization/ASTWriterDecl.cpp | 9 +
clang/lib/Serialization/ASTWriterStmt.cpp | 32 +
clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 3 +
clang/tools/libclang/CIndex.cpp | 1 +
clang/tools/libclang/CXCursor.cpp | 3 +
47 files changed, 1438 insertions(+), 29 deletions(-)
diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h
index 3be24ff868c2d..c999b79c3b2a7 100644
--- a/clang/include/clang/AST/ASTNodeTraverser.h
+++ b/clang/include/clang/AST/ASTNodeTraverser.h
@@ -972,6 +972,12 @@ class ASTNodeTraverser
}
}
+ void VisitCXXExpansionStmtDecl(const CXXExpansionStmtDecl *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) {
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 076d9ba935583..0224cf5a3612b 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -1262,14 +1262,16 @@ class VarDecl : public DeclaratorDecl, public Redeclarable<VarDecl> {
/// Returns true for local variable declarations other than parameters.
/// Note that this includes static variables inside of functions. It also
- /// includes variables inside blocks.
+ /// includes variables inside blocks and expansion statements.
///
/// void foo() { int x; static int y; extern int z; }
bool isLocalVarDecl() const {
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;
}
diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h
index 5519787d71f88..71e6898f4c94d 100644
--- a/clang/include/clang/AST/DeclBase.h
+++ b/clang/include/clang/AST/DeclBase.h
@@ -2195,6 +2195,10 @@ class DeclContext {
return getDeclKind() == Decl::RequiresExprBody;
}
+ bool isExpansionStmt() const {
+ return getDeclKind() == Decl::CXXExpansionStmt;
+ }
+
bool isNamespace() const { return getDeclKind() == Decl::Namespace; }
bool isStdNamespace() const;
@@ -2292,6 +2296,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().
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index a4a1bb9c13c79..3a416f83d1d8f 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -3343,6 +3343,131 @@ 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.
+/// A lambda enclosed within an expansion statement cannot compute its
+/// template depth without a pointer to the enclosing expansion statement.
+///
+/// For the remainder of this comment, let 'expanding' an expansion statement
+/// refer to the process of performing template substitution on its body N
+/// times, where N is the expansion size (how this size is determined depends on
+/// the kind of expansion statement); by contrast we may sometimes 'instantiate'
+/// an expansion statement (because it happens to be in a template). This is
+/// just regular template instantiation.
+///
+/// This node contains a 'CXXExpansionStmtPattern' as well as a
+/// 'CXXExpansionStmtInstantiation'. These two members correspond to
+/// distinct representations of the expansion statement: the former is used
+/// prior to expansion and contains all the parts needed to perform expansion;
+/// the latter holds the expanded/desugared AST nodes that result from the
+/// expansion.
+///
+/// Additionally, there is a 'NonTypeTemplateParmDecl', which is a template
+/// parameter that serves as the expansion index, e.g. during the N-th
+/// expansion, it is set to 'N'. See the documentation of
+/// 'CXXExpansionStmtPattern', for more information on how this is used.
+///
+/// After expansion, the 'CXXExpansionStmtPattern' is no longer updated and left
+/// as-is; this also means that, if an already-expanded expansion statement is
+/// inside a template, and that template is then instantiated, the
+/// 'CXXExpansionStmtPattern' is *not* instantiated; only the
+/// 'CXXExpansionStmtInstantiation' is. The latter is also what's used for
+/// codegen and constant evaluation.
+///
+/// There are different kinds of expansion statements; see the comment on
+/// 'CXXExpansionStmtPattern' for more information.
+///
+/// As an example, if the user writes the following expansion statement:
+/// \verbatim
+/// std::tuple<int, int, int> a{1, 2, 3};
+/// template for (auto x : a) {
+/// // ...
+/// }
+/// \endverbatim
+///
+/// The 'CXXExpansionStmtPattern' of this particular 'CXXExpansionStmtDecl'
+/// stores, amongst other things, the declaration of the variable 'x' as well
+/// as the expansion-initializer 'a'.
+///
+/// After expansion, we end up with a 'CXXExpansionStmtInstantiation' that
+/// is *equivalent* to the AST shown below. Note that only the inner '{}' (i.e.
+/// those marked as 'Actual "CompoundStmt"' below) are actually present as
+/// 'CompoundStmt's in the AST; the outer braces that wrap everything do *not*
+/// correspond to an actual 'CompoundStmt' and are implicit in the sense that we
+/// simply push a scope when evaluating or emitting IR for a
+/// 'CXXExpansionStmtInstantiation'.
+///
+/// \verbatim
+/// { // Not actually present in the AST.
+/// auto [__u0, __u1, __u2] = a;
+/// { // Actual 'CompoundStmt'.
+/// auto x = __u0;
+/// // ...
+/// }
+/// { // Actual 'CompoundStmt'.
+/// auto x = __u1;
+/// // ...
+/// }
+/// { // Actual 'CompoundStmt'.
+/// auto x = __u2;
+/// // ...
+/// }
+/// }
+/// \endverbatim
+///
+/// See the documentation around 'CXXExpansionStmtInstantiation' for more notes
+/// as to why this node exist and how it is used.
+///
+/// \see CXXExpansionStmtPattern
+/// \see CXXExpansionStmtInstantiation
+class CXXExpansionStmtDecl : public Decl, public DeclContext {
+ CXXExpansionStmtPattern *Pattern = nullptr;
+ NonTypeTemplateParmDecl *IndexNTTP = nullptr;
+ CXXExpansionStmtInstantiation *Instantiations = nullptr;
+
+ CXXExpansionStmtDecl(DeclContext *DC, SourceLocation Loc,
+ NonTypeTemplateParmDecl *NTTP);
+
+public:
+ friend class ASTDeclReader;
+
+ static CXXExpansionStmtDecl *Create(ASTContext &C, DeclContext *DC,
+ SourceLocation Loc,
+ NonTypeTemplateParmDecl *NTTP);
+ static CXXExpansionStmtDecl *CreateDeserialized(ASTContext &C,
+ GlobalDeclID ID);
+
+ CXXExpansionStmtPattern *getExpansionPattern() { return Pattern; }
+ const CXXExpansionStmtPattern *getExpansionPattern() const {
+ return Pattern;
+ }
+ void setExpansionPattern(CXXExpansionStmtPattern *S) { Pattern = S; }
+
+ CXXExpansionStmtInstantiation *getInstantiations() { return Instantiations; }
+ const CXXExpansionStmtInstantiation *getInstantiations() const {
+ return Instantiations;
+ }
+
+ void setInstantiations(CXXExpansionStmtInstantiation *S) {
+ Instantiations = S;
+ }
+
+ NonTypeTemplateParmDecl *getIndexTemplateParm() { return IndexNTTP; }
+ const NonTypeTemplateParmDecl *getIndexTemplateParm() const {
+ return IndexNTTP;
+ }
+
+ SourceRange getSourceRange() const override LLVM_READONLY;
+
+ static bool classof(const Decl *D) { return classofKind(D->getKind()); }
+ static bool classofKind(Kind K) { return K == CXXExpansionStmt; }
+};
+
inline NamedDecl *getAsNamedDecl(TemplateParameter P) {
if (auto *PD = P.dyn_cast<TemplateTypeParmDecl *>())
return PD;
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index c40cd929b7408..44f2559062943 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -5554,6 +5554,50 @@ class CXXReflectExpr : public Expr {
}
};
+/// Helper that selects an expression from an InitListExpr depending on the
+/// current expansion index. See 'CXXExpansionStmtPattern' for how this is used.
+class CXXExpansionSelectExpr : public Expr {
+ friend class ASTStmtReader;
+
+ enum SubExpr { RANGE, INDEX, COUNT };
+ Expr *SubExprs[COUNT];
+
+public:
+ CXXExpansionSelectExpr(EmptyShell Empty);
+ CXXExpansionSelectExpr(const ASTContext &C, InitListExpr *Range, Expr *Idx);
+
+ InitListExpr *getRangeExpr() {
+ return cast<InitListExpr>(SubExprs[RANGE]);
+ }
+
+ const InitListExpr *getRangeExpr() const {
+ return cast<InitListExpr>(SubExprs[RANGE]);
+ }
+
+ void setRangeExpr(InitListExpr *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() == CXXExpansionSelectExprClass;
+ }
+};
} // namespace clang
#endif // LLVM_CLANG_AST_EXPRCXX_H
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index ce6ad723191e0..eafbe0f6c23ad 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -1887,6 +1887,14 @@ DEF_TRAVERSE_DECL(UsingShadowDecl, {})
DEF_TRAVERSE_DECL(ConstructorUsingShadowDecl, {})
+DEF_TRAVERSE_DECL(CXXExpansionStmtDecl, {
+ 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));
@@ -3136,6 +3144,10 @@ DEF_TRAVERSE_STMT(RequiresExpr, {
TRY_TO(TraverseConceptRequirement(Req));
})
+DEF_TRAVERSE_STMT(CXXExpansionStmtPattern, {})
+DEF_TRAVERSE_STMT(CXXExpansionStmtInstantiation, {})
+DEF_TRAVERSE_STMT(CXXExpansionSelectExpr, {})
+
// These literals (all of them) do not need any action.
DEF_TRAVERSE_STMT(IntegerLiteral, {})
DEF_TRAVERSE_STMT(FixedPointLiteral, {})
diff --git a/clang/include/clang/AST/StmtCXX.h b/clang/include/clang/AST/StmtCXX.h
index 5d68d3ef64a20..cf44268c6e695 100644
--- a/clang/include/clang/AST/StmtCXX.h
+++ b/clang/include/clang/AST/StmtCXX.h
@@ -22,6 +22,7 @@
namespace clang {
class VarDecl;
+class CXXExpansionStmtDecl;
/// CXXCatchStmt - This represents a C++ catch block.
///
@@ -524,6 +525,588 @@ 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].
+///
+/// Specifically, let E denote the expansion-initializer; the expansion
+/// statement is iterating if the type of E is not an array type, and either
+///
+/// 2a. 'E.begin' and 'E.end' *exist* (irrespective of whether they're
+/// accessible, deleted, or even callable), or
+///
+/// 2b. ADL for 'begin(E)' and 'end(E)' finds at least one viable function.
+///
+/// If neither A nor B apply to E (or if E is an array type), we treat this as
+/// a destructuring expansion statement instead (see case 3 below).
+///
+/// Notably, case 2a only checks whether the 'begin' and 'end' members exist and
+/// does *not* perform proper overload resolution; this is because if there is
+/// a begin/end function, but it for some reason is not usable (e.g. because it
+/// is non-const but E is const), then we'd rather error and tell the user that
+/// their begin/end function is wrong rather than falling back to destructuring.
+///
+/// Conversely, case 2b *does* perform overload resolution, simply because ADL
+/// may find quite a few begin/end overloads for unrelated types that happen to
+/// be in the same namespace. E.g. if the type of E is 'std::tuple', then there
+/// are quite a few begin/end pairs in the namespace 'std', but non of them can
+/// actually be used for a 'std::tuple', and we definitely want to destructure a
+/// tuple rather than error about it not being iterable.
+///
+/// In either case, once we've decided that the expansion statement is indeed
+/// iterating, we *do* make sure that the expression 'E.begin()'/'begin(E)' is
+/// well-formed, but any error at that point is a hard error and does not make
+/// us switch to destructuring instead.
+///
+/// The result of this expression is stored in a variable 'begin', which is then
+/// used to compute another variable 'iter' (which is just 'begin' + the
+/// expansion index) during expansion. During the N-th expansion, the expansion
+/// variable is then set to '*iter'. See [stmt.expand] for more information.
+///
+/// The expression used to compute the size of the expansion is not stored and
+/// is only created at the moment of expansion. See Sema::ComputeExpansionSize()
+/// for more information about this.
+///
+/// Example:
+/// \verbatim
+/// static constexpr std::string_view foo = "abcd";
+/// template for (auto x : foo) {
+/// // ...
+/// }
+/// \endverbatim
+///
+/// Here, 'begin' is 'foo.begin()', and during e.g. the 0-th expansion, 'iter'
+/// is 'begin + 0', and thus '*iter' yields 'a', which results in 'x' being
+/// a variable of type 'char' with value 'a'.
+///
+/// 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 declaration, with the number of
+/// bindings and expansion size determined by the usual means (array size,
+/// std::tuple_size, etc.).
+///
+/// During the N-th expansion, the expansion variable is then initialized with
+/// the N-th binding of the structured-binding declaration. This is implemented
+/// by wrapping the initializer with a CXXExpansionSelectExpr, which selects a
+/// binding based on the current expansion index when called from TreeTransform.
+///
+/// Example:
+/// \verbatim
+/// std::tuple<int, long, unsigned> a {1, 2l, 3u};
+/// template for (auto x : a) {
+/// // ...
+/// }
+/// \endverbatim
+///
+/// Here, we build 'auto [_U0, _U1, _U2] = a', and during e.g. the 0-th
+/// expansion, 'x' is initialized with '_U0'.
+///
+/// 4. Represents an expansion statement whose expansion-initializer is
+/// type-dependent.
+///
+/// This will eventually become an iterating or destructuring expansion
+/// statement once the expansion-initializer is no longer dependent.
+///
+/// Dependent expansion statements can never be enumerating: even if the
+/// expansion size of an enumerating expansion statement is dependent (which
+/// is possible if the expression-list contains a pack), we still don't build
+/// an 'Enumerating' 'CXXExpansionStmtPattern' for it.
+///
+/// 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;
+
+ /// Substatements of an unexpanded expansion statement.
+ ///
+ /// 'INIT', 'VAR', and 'BODY' are common to all kinds of expansion statements;
+ /// the former may be null if there is no init-statement.
+ ///
+ /// Depending on the kind of expansion statement, we may have to store
+ /// additional sub-statements, the first of which is denoted by
+ /// 'FIRST_CHILD_STATEMENT'.
+ ///
+ /// All of the sub-statements are allocated as 'TrailingObjects' so we can
+ /// return a single contiguous range from 'children()'.
+ enum SubStmt {
+ INIT,
+ VAR,
+ BODY,
+ FIRST_CHILD_STMT,
+
+ // Enumerating expansion statement (no additional sub-statements).
+ COUNT_Enumerating = FIRST_CHILD_STMT,
+
+ // Dependent expansion statement (1 additional sub-statement).
+ EXPANSION_INITIALIZER = FIRST_CHILD_STMT,
+ COUNT_Dependent,
+
+ // Destructuring expansion statement (1 additional sub-statement).
+ DECOMP_DECL = FIRST_CHILD_STMT,
+ COUNT_Destructuring,
+
+ // Iterating expansion statement (3 additional sub-statements).
+ RANGE = FIRST_CHILD_STMT,
+ BEGIN,
+ ITER,
+ COUNT_Iterating,
+ };
+
+ CXXExpansionStmtPattern(ExpansionStmtKind PatternKind, EmptyShell Empty);
+ CXXExpansionStmtPattern(ExpansionStmtKind PatternKind,
+ CXXExpansionStmtDecl *ESD, Stmt *Init,
+ DeclStmt *ExpansionVar, SourceLocation LParenLoc,
+ SourceLocation ColonLoc, SourceLocation RParenLoc);
+
+public:
+ static CXXExpansionStmtPattern *
+ CreateEmpty(ASTContext &Context, EmptyShell Empty, ExpansionStmtKind Kind);
+
+ /// Create a dependent expansion statement pattern.
+ static CXXExpansionStmtPattern *
+ CreateDependent(ASTContext &Context, CXXExpansionStmtDecl *ESD, Stmt *Init,
+ DeclStmt *ExpansionVar, Expr *ExpansionInitializer,
+ SourceLocation LParenLoc, SourceLocation ColonLoc,
+ SourceLocation RParenLoc);
+
+ /// Create a destructuring expansion statement pattern.
+ static CXXExpansionStmtPattern *
+ CreateDestructuring(ASTContext &Context, CXXExpansionStmtDecl *ESD,
+ Stmt *Init, DeclStmt *ExpansionVar,
+ Stmt *DecompositionDeclStmt, SourceLocation LParenLoc,
+ SourceLocation ColonLoc, SourceLocation RParenLoc);
+
+ /// Create an enumerating expansion statement pattern.
+ static CXXExpansionStmtPattern *
+ CreateEnumerating(ASTContext &Context, CXXExpansionStmtDecl *ESD, Stmt *Init,
+ DeclStmt *ExpansionVar, SourceLocation LParenLoc,
+ SourceLocation ColonLoc, SourceLocation RParenLoc);
+
+ /// Create an iterating expansion statement pattern.
+ static CXXExpansionStmtPattern *
+ CreateIterating(ASTContext &Context, CXXExpansionStmtDecl *ESD, Stmt *Init,
+ DeclStmt *ExpansionVar, DeclStmt *Range, DeclStmt *Begin,
+ DeclStmt* Iter, SourceLocation LParenLoc,
+ SourceLocation ColonLoc, SourceLocation RParenLoc);
+
+ SourceLocation getLParenLoc() const { return LParenLoc; }
+ SourceLocation getColonLoc() const { return ColonLoc; }
+ SourceLocation getRParenLoc() const { return RParenLoc; }
+ SourceLocation getBeginLoc() const;
+ SourceLocation getEndLoc() const {
+ return getBody() ? getBody()->getEndLoc() : RParenLoc;
+ }
+
+ ExpansionStmtKind getKind() const { return PatternKind; }
+ bool isDependent() const {
+ return PatternKind == ExpansionStmtKind::Dependent;
+ }
+ bool isEnumerating() const {
+ return PatternKind == ExpansionStmtKind::Enumerating;
+ }
+ bool isIterating() const {
+ return PatternKind == ExpansionStmtKind::Iterating;
+ }
+ bool isDestructuring() const {
+ return PatternKind == ExpansionStmtKind::Destructuring;
+ }
+
+ unsigned getNumSubStmts() const { return getNumSubStmts(PatternKind); }
+
+ // Accessors for subcomponents common to all expansion statements.
+ CXXExpansionStmtDecl *getDecl() { return ParentDecl; }
+ const CXXExpansionStmtDecl *getDecl() const { return ParentDecl; }
+
+ Stmt *getInit() { return getSubStmt(INIT); }
+ const Stmt *getInit() const { return getSubStmt(INIT); }
+ void setInit(Stmt *S) { getSubStmt(INIT) = S; }
+
+ VarDecl *getExpansionVariable();
+ const VarDecl *getExpansionVariable() const {
+ return const_cast<CXXExpansionStmtPattern *>(this)->getExpansionVariable();
+ }
+
+ DeclStmt *getExpansionVarStmt() { return cast<DeclStmt>(getSubStmt(VAR)); }
+ const DeclStmt *getExpansionVarStmt() const {
+ return cast<DeclStmt>(getSubStmt(VAR));
+ }
+
+ void setExpansionVarStmt(Stmt *S) { getSubStmt(VAR) = S; }
+
+ Stmt *getBody() { return getSubStmt(BODY); }
+ const Stmt *getBody() const { return getSubStmt(BODY); }
+ void setBody(Stmt *S) { getSubStmt(BODY) = S; }
+
+ // Accessors for iterating statements.
+ const DeclStmt *getRangeVarStmt() const {
+ assert(isIterating());
+ return cast<DeclStmt>(getSubStmt(RANGE));
+ }
+
+ DeclStmt *getRangeVarStmt() {
+ assert(isIterating());
+ return cast<DeclStmt>(getSubStmt(RANGE));
+ }
+
+ void setRangeVarStmt(DeclStmt *S) {
+ assert(isIterating());
+ getSubStmt(RANGE) = S;
+ }
+
+ const VarDecl *getRangeVar() const {
+ assert(isIterating());
+ return cast<VarDecl>(getRangeVarStmt()->getSingleDecl());
+ }
+
+ VarDecl *getRangeVar() {
+ assert(isIterating());
+ return cast<VarDecl>(getRangeVarStmt()->getSingleDecl());
+ }
+
+ const DeclStmt *getBeginVarStmt() const {
+ assert(isIterating());
+ return cast<DeclStmt>(getSubStmt(BEGIN));
+ }
+
+ DeclStmt *getBeginVarStmt() {
+ assert(isIterating());
+ return cast<DeclStmt>(getSubStmt(BEGIN));
+ }
+
+ void setBeginVarStmt(DeclStmt *S) {
+ assert(isIterating());
+ getSubStmt(BEGIN) = S;
+ }
+
+ const VarDecl *getBeginVar() const {
+ assert(isIterating());
+ return cast<VarDecl>(getBeginVarStmt()->getSingleDecl());
+ }
+
+ VarDecl *getBeginVar() {
+ assert(isIterating());
+ return cast<VarDecl>(getBeginVarStmt()->getSingleDecl());
+ }
+
+ const DeclStmt *getIterVarStmt() const {
+ assert(isIterating());
+ return cast<DeclStmt>(getSubStmt(ITER));
+ }
+
+ DeclStmt *getIterVarStmt() {
+ assert(isIterating());
+ return cast<DeclStmt>(getSubStmt(ITER));
+ }
+
+ void setIterVarStmt(DeclStmt *S) {
+ assert(isIterating());
+ getSubStmt(ITER) = S;
+ }
+
+ const VarDecl *getIterVar() const {
+ assert(isIterating());
+ return cast<VarDecl>(getIterVarStmt()->getSingleDecl());
+ }
+
+ VarDecl *getIterVar() {
+ assert(isIterating());
+ return cast<VarDecl>(getIterVarStmt()->getSingleDecl());
+ }
+
+
+ // Accessors for destructuring statements.
+ Stmt *getDecompositionDeclStmt() {
+ assert(isDestructuring());
+ return getSubStmt(DECOMP_DECL);
+ }
+
+ const Stmt *getDecompositionDeclStmt() const {
+ assert(isDestructuring());
+ return getSubStmt(DECOMP_DECL);
+ }
+
+ void setDecompositionDeclStmt(Stmt *S) {
+ assert(isDestructuring());
+ getSubStmt(DECOMP_DECL) = S;
+ }
+
+ DecompositionDecl *getDecompositionDecl();
+ const DecompositionDecl *getDecompositionDecl() const {
+ return const_cast<CXXExpansionStmtPattern *>(this)->getDecompositionDecl();
+ }
+
+ // Accessors for dependent statements.
+ Expr *getExpansionInitializer() {
+ assert(isDependent());
+ return cast<Expr>(getSubStmt(EXPANSION_INITIALIZER));
+ }
+
+ const Expr *getExpansionInitializer() const {
+ assert(isDependent());
+ return cast<Expr>(getSubStmt(EXPANSION_INITIALIZER));
+ }
+
+ void setExpansionInitializer(Expr *S) {
+ assert(isDependent());
+ getSubStmt(EXPANSION_INITIALIZER) = S;
+ }
+
+ child_range children() {
+ return child_range(getTrailingObjects(),
+ getTrailingObjects() + getNumSubStmts());
+ }
+
+ const_child_range children() const {
+ return const_child_range(getTrailingObjects(),
+ getTrailingObjects() + getNumSubStmts());
+ }
+
+ static bool classof(const Stmt *T) {
+ return T->getStmtClass() == CXXExpansionStmtPatternClass;
+ }
+
+private:
+ template <typename... Args>
+ static CXXExpansionStmtPattern *AllocateAndConstruct(ASTContext &Context,
+ ExpansionStmtKind Kind,
+ Args &&...Arguments);
+
+ static unsigned getNumSubStmts(ExpansionStmtKind Kind);
+ Stmt *getSubStmt(unsigned Idx) const {
+ assert(Idx < getNumSubStmts());
+ return getTrailingObjects()[Idx];
+ }
+
+ Stmt *&getSubStmt(unsigned Idx) {
+ assert(Idx < getNumSubStmts());
+ return getTrailingObjects()[Idx];
+ }
+};
+
+/// Represents the code generated for an expanded expansion statement.
+///
+/// This holds 'preamble statements' and 'instantiations'; these encode the
+/// general underlying pattern that all expansion statements desugar to. Note
+/// that only the inner '{}' (i.e. those marked as 'Actual "CompoundStmt"'
+/// below) are actually present as 'CompoundStmt's in the AST; the outer braces
+/// that wrap everything do *not* correspond to an actual 'CompoundStmt' and are
+/// implicit in the sense that we simply push a scope when evaluating or
+/// emitting IR for a 'CXXExpansionStmtInstantiation'.
+///
+/// The 'instantiations' are precisely these inner compound statements.
+///
+/// \verbatim
+/// { // Not actually present in the AST.
+/// <preamble statements>
+/// { // Actual 'CompoundStmt'.
+/// <1st instantiation>
+/// }
+/// ...
+/// { // Actual 'CompoundStmt'.
+/// <n-th instantiation>
+/// }
+/// }
+/// \endverbatim
+///
+/// For example, the CXXExpansionStmtInstantiation that corresponds to the
+/// following expansion statement
+///
+/// \verbatim
+/// std::tuple<int, int, int> a{1, 2, 3};
+/// template for (auto x : a) {
+/// // ...
+/// }
+/// \endverbatim
+///
+/// would be
+///
+/// \verbatim
+/// {
+/// auto [__u0, __u1, __u2] = a;
+/// {
+/// auto x = __u0;
+/// // ...
+/// }
+/// {
+/// auto x = __u1;
+/// // ...
+/// }
+/// {
+/// auto x = __u2;
+/// // ...
+/// }
+/// }
+/// \endverbatim
+///
+/// There are two reasons why this needs to exist and why we don't just store a
+/// list of instantiations in some other node:
+///
+/// 1. We need custom codegen to handle break/continue in expansion statements
+/// properly, so it can't just be a compound statement.
+///
+/// 2. The expansions are created after both the pattern and the
+/// 'CXXExpansionStmtDecl', so we can't just store them as trailing data in
+/// either of those nodes (because we don't know how many expansions there
+/// will be when those notes are allocated).
+///
+/// \see CXXExpansionStmtDecl
+class CXXExpansionStmtInstantiation final
+ : public Stmt,
+ llvm::TrailingObjects<CXXExpansionStmtInstantiation, Stmt *> {
+ friend class ASTStmtReader;
+ friend TrailingObjects;
+
+ CXXExpansionStmtDecl *Parent;
+
+ // Instantiations are stored first, then preamble statements.
+ const unsigned NumInstantiations : 20;
+ const unsigned NumPreambleStmts : 3;
+ unsigned ShouldApplyLifetimeExtensionToPreamble : 1;
+
+ CXXExpansionStmtInstantiation(EmptyShell Empty, unsigned NumInstantiations,
+ unsigned NumPreambleStmts);
+ CXXExpansionStmtInstantiation(CXXExpansionStmtDecl *Parent,
+ ArrayRef<Stmt *> Instantiations,
+ ArrayRef<Stmt *> PreambleStmts,
+ bool ShouldApplyLifetimeExtensionToPreamble);
+
+public:
+ static CXXExpansionStmtInstantiation *
+ Create(ASTContext &C, CXXExpansionStmtDecl *Parent,
+ ArrayRef<Stmt *> Instantiations, ArrayRef<Stmt *> PreambleStmts,
+ bool ShouldApplyLifetimeExtensionToPreamble);
+
+ static CXXExpansionStmtInstantiation *CreateEmpty(ASTContext &C,
+ EmptyShell Empty,
+ unsigned NumInstantiations,
+ unsigned NumPreambleStmts);
+
+ ArrayRef<Stmt *> getAllSubStmts() const {
+ return getTrailingObjects(getNumSubStmts());
+ }
+
+ MutableArrayRef<Stmt *> getAllSubStmts() {
+ return getTrailingObjects(getNumSubStmts());
+ }
+
+ unsigned getNumSubStmts() const { return NumInstantiations + NumPreambleStmts; }
+
+ ArrayRef<Stmt *> getInstantiations() const {
+ return getTrailingObjects(NumInstantiations);
+ }
+
+ ArrayRef<Stmt *> getPreambleStmts() const {
+ return getAllSubStmts().drop_front(NumInstantiations);
+ }
+
+ bool shouldApplyLifetimeExtensionToPreamble() const {
+ return ShouldApplyLifetimeExtensionToPreamble;
+ }
+
+ void setShouldApplyLifetimeExtensionToPreamble(bool Apply) {
+ ShouldApplyLifetimeExtensionToPreamble = Apply;
+ }
+
+ SourceLocation getBeginLoc() const {
+ return Parent->getExpansionPattern()->getBeginLoc();
+ }
+
+ SourceLocation getEndLoc() const {
+ return Parent->getExpansionPattern()->getEndLoc();
+ }
+
+ CXXExpansionStmtDecl *getParent() { return Parent; }
+ const CXXExpansionStmtDecl *getParent() const { return Parent; }
+
+ child_range children() {
+ Stmt **S = getTrailingObjects();
+ return child_range(S, S + getNumSubStmts());
+ }
+
+ const_child_range children() const {
+ Stmt *const *S = getTrailingObjects();
+ return const_child_range(S, S + getNumSubStmts());
+ }
+
+ static bool classof(const Stmt *T) {
+ return T->getStmtClass() == CXXExpansionStmtInstantiationClass;
+ }
+};
+
} // end namespace clang
#endif
diff --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h
index 32e83ebb5c8eb..a3e1eb4140c34 100644
--- a/clang/include/clang/AST/TextNodeDumper.h
+++ b/clang/include/clang/AST/TextNodeDumper.h
@@ -266,6 +266,9 @@ class TextNodeDumper
void VisitCoawaitExpr(const CoawaitExpr *Node);
void VisitCoreturnStmt(const CoreturnStmt *Node);
void VisitCompoundStmt(const CompoundStmt *Node);
+ void VisitCXXExpansionStmtPattern(const CXXExpansionStmtPattern *Node);
+ void
+ VisitCXXExpansionStmtInstantiation(const CXXExpansionStmtInstantiation *Node);
void VisitConstantExpr(const ConstantExpr *Node);
void VisitCallExpr(const CallExpr *Node);
void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *Node);
diff --git a/clang/include/clang/Basic/DeclNodes.td b/clang/include/clang/Basic/DeclNodes.td
index 04311055bb600..23f8e47939bdb 100644
--- a/clang/include/clang/Basic/DeclNodes.td
+++ b/clang/include/clang/Basic/DeclNodes.td
@@ -101,6 +101,7 @@ def AccessSpec : DeclNode<Decl>;
def Friend : DeclNode<Decl>;
def FriendTemplate : DeclNode<Decl>;
def StaticAssert : DeclNode<Decl>;
+def CXXExpansionStmt : DeclNode<Decl>, DeclContext;
def Block : DeclNode<Decl, "blocks">, DeclContext;
def OutlinedFunction : DeclNode<Decl>, DeclContext;
def Captured : DeclNode<Decl>, DeclContext;
diff --git a/clang/include/clang/Basic/StmtNodes.td b/clang/include/clang/Basic/StmtNodes.td
index 61d76bafdfcde..8f0d875b58d99 100644
--- a/clang/include/clang/Basic/StmtNodes.td
+++ b/clang/include/clang/Basic/StmtNodes.td
@@ -60,6 +60,11 @@ def CXXForRangeStmt : StmtNode<Stmt>;
def CoroutineBodyStmt : StmtNode<Stmt>;
def CoreturnStmt : StmtNode<Stmt>;
+// C++ expansion statements (P1306)
+def CXXExpansionStmtPattern : StmtNode<Stmt>;
+def CXXExpansionStmtInstantiation
+ : StmtNode<Stmt>; // *Not* derived from CXXExpansionStmtPattern!
+
// Expressions
def Expr : StmtNode<ValueStmt, 1>;
def PredefinedExpr : StmtNode<Expr>;
@@ -184,6 +189,9 @@ def RequiresExpr : StmtNode<Expr>;
// c++ 26 reflection
def CXXReflectExpr : StmtNode<Expr>;
+// C++26 Expansion statement support expressions
+def CXXExpansionSelectExpr : StmtNode<Expr>;
+
// Obj-C Expressions.
def ObjCObjectLiteral : StmtNode<Expr, 1>;
def ObjCStringLiteral : StmtNode<ObjCObjectLiteral>;
diff --git a/clang/include/clang/Sema/Scope.h b/clang/include/clang/Sema/Scope.h
index 0d1c0ff6a1e91..fb429b73f8627 100644
--- a/clang/include/clang/Sema/Scope.h
+++ b/clang/include/clang/Sema/Scope.h
@@ -196,6 +196,16 @@ class Scope {
/// declared in this scope.
unsigned short PrototypeIndex;
+ /// IsExpansionStmtScope - This is the scope corresponding to a C++26
+ /// expansion statement.
+ ///
+ /// FIXME: This should be part of ScopeFlags, but we're out of bits, so we
+ /// need to update every place that uses 'unsigned' to hold scope flags. We
+ /// should probably redefine ScopeFlags as an 'enum class : uint64_t' and
+ /// use LLVM_MARK_AS_BITMASK_ENUM() and friends so we can continue to '|'
+ /// scope flags together.
+ bool IsExpansionStmtScope;
+
/// FnParent - If this scope has a parent scope that is a function body, this
/// pointer is non-null and points to it. This is used for label processing.
Scope *FnParent;
@@ -317,6 +327,14 @@ class Scope {
return Flags & ConditionVarScope;
}
+ void setIsExpansionStmtScope(bool Value = true) {
+ IsExpansionStmtScope = Value;
+ }
+
+ bool isExpansionStmtScope() const {
+ return IsExpansionStmtScope;
+ }
+
/// getBreakParent - Return the closest scope that a break statement
/// would be affected by.
Scope *getBreakParent() {
diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h
index 2c394fd03e8ef..6589da6a04549 100644
--- a/clang/include/clang/Serialization/ASTBitCodes.h
+++ b/clang/include/clang/Serialization/ASTBitCodes.h
@@ -1466,6 +1466,9 @@ enum DeclCode {
/// \brief A StaticAssertDecl record.
DECL_STATIC_ASSERT,
+ /// A C++ expansion statement.
+ DECL_EXPANSION_STMT,
+
/// A record containing CXXBaseSpecifiers.
DECL_CXX_BASE_SPECIFIERS,
@@ -1845,6 +1848,12 @@ enum StmtCode {
STMT_CXX_FOR_RANGE,
+ /// A CXXExpansionPatternStmt.
+ STMT_CXX_EXPANSION_PATTERN,
+
+ /// A CXXExpansionInstantiationStmt.
+ STMT_CXX_EXPANSION_INSTANTIATION,
+
/// A CXXOperatorCallExpr record.
EXPR_CXX_OPERATOR_CALL,
@@ -1936,6 +1945,7 @@ enum StmtCode {
EXPR_CXX_FOLD, // CXXFoldExpr
EXPR_CONCEPT_SPECIALIZATION, // ConceptSpecializationExpr
EXPR_REQUIRES, // RequiresExpr
+ EXPR_CXX_EXPANSION_SELECT, // CXXExpansionSelectExpr
// Reflection
EXPR_REFLECT,
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 41ba98c53247d..a85e6d1e1bb15 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -516,6 +516,7 @@ namespace clang {
ExpectedDecl VisitEmptyDecl(EmptyDecl *D);
ExpectedDecl VisitAccessSpecDecl(AccessSpecDecl *D);
ExpectedDecl VisitStaticAssertDecl(StaticAssertDecl *D);
+ ExpectedDecl VisitCXXExpansionStmtDecl(CXXExpansionStmtDecl *D);
ExpectedDecl VisitTranslationUnitDecl(TranslationUnitDecl *D);
ExpectedDecl VisitBindingDecl(BindingDecl *D);
ExpectedDecl VisitNamespaceDecl(NamespaceDecl *D);
@@ -608,6 +609,9 @@ namespace clang {
ExpectedStmt VisitCXXCatchStmt(CXXCatchStmt *S);
ExpectedStmt VisitCXXTryStmt(CXXTryStmt *S);
ExpectedStmt VisitCXXForRangeStmt(CXXForRangeStmt *S);
+ ExpectedStmt VisitCXXExpansionStmtPattern(CXXExpansionStmtPattern *S);
+ ExpectedStmt
+ VisitCXXExpansionStmtInstantiation(CXXExpansionStmtInstantiation *S);
// FIXME: MSDependentExistsStmt
ExpectedStmt VisitObjCForCollectionStmt(ObjCForCollectionStmt *S);
ExpectedStmt VisitObjCAtCatchStmt(ObjCAtCatchStmt *S);
@@ -700,6 +704,7 @@ namespace clang {
VisitSubstNonTypeTemplateParmPackExpr(SubstNonTypeTemplateParmPackExpr *E);
ExpectedStmt VisitPseudoObjectExpr(PseudoObjectExpr *E);
ExpectedStmt VisitCXXParenListInitExpr(CXXParenListInitExpr *E);
+ ExpectedStmt VisitCXXExpansionSelectExpr(CXXExpansionSelectExpr *E);
// Helper for chaining together multiple imports. If an error is detected,
// subsequent imports will return default constructed nodes, so that failure
@@ -2863,6 +2868,34 @@ ExpectedDecl ASTNodeImporter::VisitStaticAssertDecl(StaticAssertDecl *D) {
return ToD;
}
+ExpectedDecl
+ASTNodeImporter::VisitCXXExpansionStmtDecl(CXXExpansionStmtDecl *D) {
+ auto DCOrErr = Importer.ImportContext(D->getDeclContext());
+ if (!DCOrErr)
+ return DCOrErr.takeError();
+ DeclContext *DC = *DCOrErr;
+ DeclContext *LexicalDC = DC;
+
+ Error Err = Error::success();
+ auto ToLocation = importChecked(Err, D->getLocation());
+ auto ToExpansion = importChecked(Err, D->getExpansionPattern());
+ auto ToIndex = importChecked(Err, D->getIndexTemplateParm());
+ auto ToInstantiations = importChecked(Err, D->getInstantiations());
+ if (Err)
+ return std::move(Err);
+
+ CXXExpansionStmtDecl *ToD;
+ if (GetImportedOrCreateDecl(ToD, D, Importer.getToContext(), DC, ToLocation,
+ ToIndex))
+ return ToD;
+
+ ToD->setExpansionPattern(ToExpansion);
+ ToD->setInstantiations(ToInstantiations);
+ ToD->setLexicalDeclContext(LexicalDC);
+ LexicalDC->addDeclInternal(ToD);
+ return ToD;
+}
+
ExpectedDecl ASTNodeImporter::VisitNamespaceDecl(NamespaceDecl *D) {
// Import the major distinguishing characteristics of this namespace.
DeclContext *DC, *LexicalDC;
@@ -7463,6 +7496,80 @@ ExpectedStmt ASTNodeImporter::VisitCXXForRangeStmt(CXXForRangeStmt *S) {
ToBody, ToForLoc, ToCoawaitLoc, ToColonLoc, ToRParenLoc);
}
+ExpectedStmt ASTNodeImporter::VisitCXXExpansionStmtPattern(
+ CXXExpansionStmtPattern *S) {
+ Error Err = Error::success();
+ auto ToESD = importChecked(Err, S->getDecl());
+ auto ToInit = importChecked(Err, S->getInit());
+ auto ToExpansionVar = importChecked(Err, S->getExpansionVarStmt());
+ auto ToLParenLoc = importChecked(Err, S->getLParenLoc());
+ auto ToColonLoc = importChecked(Err, S->getColonLoc());
+ auto ToRParenLoc = importChecked(Err, S->getRParenLoc());
+ if (Err)
+ return std::move(Err);
+
+ switch (S->getKind()) {
+ case CXXExpansionStmtPattern::ExpansionStmtKind::Enumerating:
+ return CXXExpansionStmtPattern::CreateEnumerating(
+ Importer.getToContext(), ToESD, ToInit, ToExpansionVar, ToLParenLoc,
+ ToColonLoc, ToRParenLoc);
+
+ case CXXExpansionStmtPattern::ExpansionStmtKind::Iterating: {
+ auto ToRange = importChecked(Err, S->getRangeVarStmt());
+ auto ToBegin = importChecked(Err, S->getBeginVarStmt());
+ auto ToIter = importChecked(Err, S->getIterVarStmt());
+ if (Err)
+ return std::move(Err);
+
+ return CXXExpansionStmtPattern::CreateIterating(
+ Importer.getToContext(), ToESD, ToInit, ToExpansionVar, ToRange,
+ ToBegin, ToIter, ToLParenLoc, ToColonLoc, ToRParenLoc);
+ }
+
+ case CXXExpansionStmtPattern::ExpansionStmtKind::Destructuring: {
+ auto ToDecompositionDeclStmt =
+ importChecked(Err, S->getDecompositionDeclStmt());
+ if (Err)
+ return std::move(Err);
+
+ return CXXExpansionStmtPattern::CreateDestructuring(
+ Importer.getToContext(), ToESD, ToInit, ToExpansionVar,
+ ToDecompositionDeclStmt, ToLParenLoc, ToColonLoc, ToRParenLoc);
+ }
+
+ case CXXExpansionStmtPattern::ExpansionStmtKind::Dependent: {
+ auto ToExpansionInitializer =
+ importChecked(Err, S->getExpansionInitializer());
+ if (Err)
+ return std::move(Err);
+ return CXXExpansionStmtPattern::CreateDependent(
+ Importer.getToContext(), ToESD, ToInit, ToExpansionVar,
+ ToExpansionInitializer, ToLParenLoc, ToColonLoc, ToRParenLoc);
+ }
+ }
+
+ llvm_unreachable("invalid pattern kind");
+}
+
+ExpectedStmt ASTNodeImporter::VisitCXXExpansionStmtInstantiation(
+ CXXExpansionStmtInstantiation *S) {
+ Error Err = Error::success();
+ SmallVector<Stmt *> ToInstantiations;
+ SmallVector<Stmt *> ToSharedStmts;
+ auto ToParent = importChecked(Err, S->getParent());
+ for (Stmt *FromInst : S->getInstantiations())
+ ToInstantiations.push_back(importChecked(Err, FromInst));
+ for (Stmt *FromShared : S->getPreambleStmts())
+ ToSharedStmts.push_back(importChecked(Err, FromShared));
+
+ if (Err)
+ return std::move(Err);
+
+ return CXXExpansionStmtInstantiation::Create(
+ Importer.getToContext(), ToParent, ToInstantiations, ToSharedStmts,
+ S->shouldApplyLifetimeExtensionToPreamble());
+}
+
ExpectedStmt
ASTNodeImporter::VisitObjCForCollectionStmt(ObjCForCollectionStmt *S) {
Error Err = Error::success();
@@ -9359,6 +9466,18 @@ ASTNodeImporter::VisitCXXParenListInitExpr(CXXParenListInitExpr *E) {
ToInitLoc, ToBeginLoc, ToEndLoc);
}
+ExpectedStmt ASTNodeImporter::VisitCXXExpansionSelectExpr(
+ CXXExpansionSelectExpr *E) {
+ Error Err = Error::success();
+ auto ToRange = importChecked(Err, E->getRangeExpr());
+ auto ToIndex = importChecked(Err, E->getIndexExpr());
+ if (Err)
+ return std::move(Err);
+
+ return new (Importer.getToContext())
+ CXXExpansionSelectExpr(Importer.getToContext(), ToRange, ToIndex);
+}
+
Error ASTNodeImporter::ImportOverriddenMethods(CXXMethodDecl *ToMethod,
CXXMethodDecl *FromMethod) {
Error ImportErrors = Error::success();
diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp
index 0a1e442656c35..e81bea6302456 100644
--- a/clang/lib/AST/DeclBase.cpp
+++ b/clang/lib/AST/DeclBase.cpp
@@ -325,6 +325,9 @@ unsigned Decl::getTemplateDepth() const {
if (auto *TPL = getDescribedTemplateParams())
return TPL->getDepth() + 1;
+ if (auto *ESD = dyn_cast<CXXExpansionStmtDecl>(this))
+ return ESD->getIndexTemplateParm()->getDepth() + 1;
+
// If this is a dependent lambda, there might be an enclosing variable
// template. In this case, the next step is not the parent DeclContext (or
// even a DeclContext at all).
@@ -1018,6 +1021,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) {
case ImplicitConceptSpecialization:
case OpenACCDeclare:
case OpenACCRoutine:
+ case CXXExpansionStmt:
// Never looked up by name.
return 0;
}
@@ -1382,7 +1386,7 @@ bool DeclContext::isDependentContext() const {
if (isFileContext())
return false;
- if (isa<ClassTemplatePartialSpecializationDecl>(this))
+ if (isa<ClassTemplatePartialSpecializationDecl, CXXExpansionStmtDecl>(this))
return true;
if (const auto *Record = dyn_cast<CXXRecordDecl>(this)) {
@@ -1491,6 +1495,7 @@ DeclContext *DeclContext::getPrimaryContext() {
case Decl::OMPDeclareReduction:
case Decl::OMPDeclareMapper:
case Decl::RequiresExprBody:
+ case Decl::CXXExpansionStmt:
// There is only one DeclContext for these entities.
return this;
@@ -2079,6 +2084,13 @@ RecordDecl *DeclContext::getOuterLexicalRecordContext() {
return OutermostRD;
}
+DeclContext *DeclContext::getEnclosingNonExpansionStatementContext() {
+ DeclContext *DC = this;
+ while (isa<CXXExpansionStmtDecl>(DC))
+ DC = DC->getParent();
+ return DC;
+}
+
bool DeclContext::InEnclosingNamespaceSetOf(const DeclContext *O) const {
// For non-file contexts, this is equivalent to Equals.
if (!isFileContext())
diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp
index 5e377a6c0c247..fb5631c8673b0 100644
--- a/clang/lib/AST/DeclPrinter.cpp
+++ b/clang/lib/AST/DeclPrinter.cpp
@@ -114,6 +114,7 @@ namespace {
void VisitNonTypeTemplateParmDecl(const NonTypeTemplateParmDecl *NTTP);
void VisitTemplateTemplateParmDecl(const TemplateTemplateParmDecl *);
void VisitHLSLBufferDecl(HLSLBufferDecl *D);
+ void VisitCXXExpansionStmtDecl(const CXXExpansionStmtDecl *D);
void VisitOpenACCDeclareDecl(OpenACCDeclareDecl *D);
void VisitOpenACCRoutineDecl(OpenACCRoutineDecl *D);
@@ -1347,6 +1348,11 @@ void DeclPrinter::VisitClassTemplatePartialSpecializationDecl(
VisitCXXRecordDecl(D);
}
+void DeclPrinter::VisitCXXExpansionStmtDecl(const CXXExpansionStmtDecl *D) {
+ D->getExpansionPattern()->printPretty(Out, /*PrinterHelper=*/nullptr, Policy,
+ Indentation, "\n", &Context);
+}
+
//----------------------------------------------------------------------------
// Objective-C declarations
//----------------------------------------------------------------------------
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index 5a8e1ed445f3a..7d0a214e25813 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -1711,6 +1711,9 @@ clang::getReplacedTemplateParameter(Decl *D, unsigned Index) {
return getReplacedTemplateParameter(
cast<FunctionDecl>(D)->getTemplateSpecializationInfo()->getTemplate(),
Index);
+ case Decl::Kind::CXXExpansionStmt:
+ assert(Index == 0 && "expansion stmts only have a single template param");
+ return {cast<CXXExpansionStmtDecl>(D)->getIndexTemplateParm(), {}};
default:
llvm_unreachable("Unhandled templated declaration kind");
}
@@ -1782,3 +1785,23 @@ const Decl &clang::adjustDeclToTemplate(const Decl &D) {
// FIXME: Adjust alias templates?
return D;
}
+
+CXXExpansionStmtDecl::CXXExpansionStmtDecl(DeclContext *DC, SourceLocation Loc,
+ NonTypeTemplateParmDecl *NTTP)
+ : Decl(CXXExpansionStmt, DC, Loc), DeclContext(CXXExpansionStmt),
+ IndexNTTP(NTTP) {}
+
+CXXExpansionStmtDecl *
+CXXExpansionStmtDecl::Create(ASTContext &C, DeclContext *DC, SourceLocation Loc,
+ NonTypeTemplateParmDecl *NTTP) {
+ return new (C, DC) CXXExpansionStmtDecl(DC, Loc, NTTP);
+}
+CXXExpansionStmtDecl *
+CXXExpansionStmtDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID) {
+ return new (C, ID)
+ CXXExpansionStmtDecl(/*DC=*/nullptr, SourceLocation(), /*NTTP=*/nullptr);
+}
+
+SourceRange CXXExpansionStmtDecl::getSourceRange() const {
+ return Pattern ? Pattern->getSourceRange() : SourceRange();
+}
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index 64d61dbc3d128..ba2f97b5b4b8d 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -3711,6 +3711,7 @@ bool Expr::HasSideEffects(const ASTContext &Ctx,
case FunctionParmPackExprClass:
case RecoveryExprClass:
case CXXFoldExprClass:
+ case CXXExpansionSelectExprClass:
// Make a conservative assumption for dependent nodes.
return IncludePossibleEffects;
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index dd603bf548926..c50ff64f9de64 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -2036,3 +2036,15 @@ CXXFoldExpr::CXXFoldExpr(QualType T, UnresolvedLookupExpr *Callee,
SubExprs[SubExpr::RHS] = RHS;
setDependence(computeDependence(this));
}
+
+CXXExpansionSelectExpr::CXXExpansionSelectExpr(EmptyShell Empty)
+ : Expr(CXXExpansionSelectExprClass, Empty) {}
+
+CXXExpansionSelectExpr::CXXExpansionSelectExpr(
+ const ASTContext &C, InitListExpr *Range, Expr *Idx)
+ : Expr(CXXExpansionSelectExprClass, C.DependentTy, VK_PRValue,
+ OK_Ordinary) {
+ setDependence(ExprDependence::TypeValueInstantiation);
+ SubExprs[RANGE] = Range;
+ SubExprs[INDEX] = Idx;
+}
diff --git a/clang/lib/AST/ExprClassification.cpp b/clang/lib/AST/ExprClassification.cpp
index a83c17074ea69..502a681ddf2a0 100644
--- a/clang/lib/AST/ExprClassification.cpp
+++ b/clang/lib/AST/ExprClassification.cpp
@@ -218,6 +218,7 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) {
case Expr::ConceptSpecializationExprClass:
case Expr::RequiresExprClass:
case Expr::CXXReflectExprClass:
+ case Expr::CXXExpansionSelectExprClass:
return Cl::CL_PRValue;
case Expr::EmbedExprClass:
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 4f45fa728c605..a464ed90f70b8 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -21423,6 +21423,7 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) {
case Expr::SYCLUniqueStableNameExprClass:
case Expr::CXXParenListInitExprClass:
case Expr::HLSLOutArgExprClass:
+ case Expr::CXXExpansionSelectExprClass:
return ICEDiag(IK_NotICE, E->getBeginLoc());
case Expr::MemberExprClass: {
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index ccf5717073fbf..ab1353321635b 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -45,7 +45,7 @@ namespace UnsupportedItaniumManglingKind =
namespace {
static bool isLocalContainerContext(const DeclContext *DC) {
- return isa<FunctionDecl>(DC) || isa<ObjCMethodDecl>(DC) || isa<BlockDecl>(DC);
+ return isa<FunctionDecl, ObjCMethodDecl, BlockDecl, CXXExpansionStmtDecl>(DC);
}
static const FunctionDecl *getStructor(const FunctionDecl *fn) {
@@ -1876,6 +1876,8 @@ static GlobalDecl getParentOfLocalEntity(const DeclContext *DC) {
GD = GlobalDecl(CD, Ctor_Complete);
else if (auto *DD = dyn_cast<CXXDestructorDecl>(DC))
GD = GlobalDecl(DD, Dtor_Complete);
+ else if (DC->isExpansionStmt())
+ GD = getParentOfLocalEntity(DC->getEnclosingNonExpansionStatementContext());
else
GD = GlobalDecl(cast<FunctionDecl>(DC));
return GD;
@@ -2217,6 +2219,9 @@ void CXXNameMangler::manglePrefix(const DeclContext *DC, bool NoFunction) {
if (NoFunction && isLocalContainerContext(DC))
return;
+ if (DC->isExpansionStmt())
+ return;
+
const NamedDecl *ND = cast<NamedDecl>(DC);
if (mangleSubstitution(ND))
return;
@@ -4977,6 +4982,7 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity,
case Expr::CXXInheritedCtorInitExprClass:
case Expr::CXXParenListInitExprClass:
case Expr::PackIndexingExprClass:
+ case Expr::CXXExpansionSelectExprClass:
llvm_unreachable("unexpected statement kind");
case Expr::ConstantExprClass:
diff --git a/clang/lib/AST/StmtCXX.cpp b/clang/lib/AST/StmtCXX.cpp
index 6a69fe75136f3..3ac68ca04a5ef 100644
--- a/clang/lib/AST/StmtCXX.cpp
+++ b/clang/lib/AST/StmtCXX.cpp
@@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//
#include "clang/AST/StmtCXX.h"
+#include "clang/AST/ExprCXX.h"
#include "clang/AST/ASTContext.h"
@@ -125,3 +126,158 @@ CoroutineBodyStmt::CoroutineBodyStmt(CoroutineBodyStmt::CtorArgs const &Args)
Args.ReturnStmtOnAllocFailure;
llvm::copy(Args.ParamMoves, const_cast<Stmt **>(getParamMoves().data()));
}
+
+CXXExpansionStmtPattern::CXXExpansionStmtPattern(ExpansionStmtKind PatternKind,
+ EmptyShell Empty)
+ : Stmt(CXXExpansionStmtPatternClass, Empty), PatternKind(PatternKind) {}
+
+CXXExpansionStmtPattern::CXXExpansionStmtPattern(
+ ExpansionStmtKind PatternKind, CXXExpansionStmtDecl *ESD, Stmt *Init,
+ DeclStmt *ExpansionVar, SourceLocation LParenLoc, SourceLocation ColonLoc,
+ SourceLocation RParenLoc)
+ : Stmt(CXXExpansionStmtPatternClass), PatternKind(PatternKind),
+ LParenLoc(LParenLoc), ColonLoc(ColonLoc), RParenLoc(RParenLoc),
+ ParentDecl(ESD) {
+ setInit(Init);
+ setExpansionVarStmt(ExpansionVar);
+ setBody(nullptr);
+}
+
+template <typename... Args>
+CXXExpansionStmtPattern *CXXExpansionStmtPattern::AllocateAndConstruct(
+ ASTContext &Context, ExpansionStmtKind Kind, Args &&...Arguments) {
+ std::size_t Size = totalSizeToAlloc<Stmt *>(getNumSubStmts(Kind));
+ void *Mem = Context.Allocate(Size, alignof(CXXExpansionStmtPattern));
+ return new (Mem)
+ CXXExpansionStmtPattern(Kind, std::forward<Args>(Arguments)...);
+}
+
+CXXExpansionStmtPattern *CXXExpansionStmtPattern::CreateDependent(
+ ASTContext &Context, CXXExpansionStmtDecl *ESD, Stmt *Init,
+ DeclStmt *ExpansionVar, Expr *ExpansionInitializer,
+ SourceLocation LParenLoc, SourceLocation ColonLoc,
+ SourceLocation RParenLoc) {
+ CXXExpansionStmtPattern *Pattern =
+ AllocateAndConstruct(Context, ExpansionStmtKind::Dependent, ESD, Init,
+ ExpansionVar, LParenLoc, ColonLoc, RParenLoc);
+ Pattern->setExpansionInitializer(ExpansionInitializer);
+ return Pattern;
+}
+
+CXXExpansionStmtPattern *CXXExpansionStmtPattern::CreateDestructuring(
+ ASTContext &Context, CXXExpansionStmtDecl *ESD, Stmt *Init,
+ DeclStmt *ExpansionVar, Stmt *DecompositionDeclStmt,
+ SourceLocation LParenLoc, SourceLocation ColonLoc,
+ SourceLocation RParenLoc) {
+ CXXExpansionStmtPattern *Pattern =
+ AllocateAndConstruct(Context, ExpansionStmtKind::Destructuring, ESD, Init,
+ ExpansionVar, LParenLoc, ColonLoc, RParenLoc);
+ Pattern->setDecompositionDeclStmt(DecompositionDeclStmt);
+ return Pattern;
+}
+
+CXXExpansionStmtPattern *
+CXXExpansionStmtPattern::CreateEmpty(ASTContext &Context, EmptyShell Empty,
+ ExpansionStmtKind Kind) {
+ return AllocateAndConstruct(Context, Kind, Empty);
+}
+
+CXXExpansionStmtPattern *CXXExpansionStmtPattern::CreateEnumerating(
+ ASTContext &Context, CXXExpansionStmtDecl *ESD, Stmt *Init,
+ DeclStmt *ExpansionVar, SourceLocation LParenLoc, SourceLocation ColonLoc,
+ SourceLocation RParenLoc) {
+ return AllocateAndConstruct(Context, ExpansionStmtKind::Enumerating, ESD,
+ Init, ExpansionVar, LParenLoc, ColonLoc,
+ RParenLoc);
+}
+
+CXXExpansionStmtPattern *CXXExpansionStmtPattern::CreateIterating(
+ ASTContext &Context, CXXExpansionStmtDecl *ESD, Stmt *Init,
+ DeclStmt *ExpansionVar, DeclStmt *Range, DeclStmt *Begin,
+ DeclStmt *Iter, SourceLocation LParenLoc, SourceLocation ColonLoc,
+ SourceLocation RParenLoc) {
+ CXXExpansionStmtPattern *Pattern =
+ AllocateAndConstruct(Context, ExpansionStmtKind::Iterating, ESD, Init,
+ ExpansionVar, LParenLoc, ColonLoc, RParenLoc);
+ Pattern->setRangeVarStmt(Range);
+ Pattern->setBeginVarStmt(Begin);
+ Pattern->setIterVarStmt(Iter);
+ return Pattern;
+}
+
+SourceLocation CXXExpansionStmtPattern::getBeginLoc() const {
+ return ParentDecl->getLocation();
+}
+
+DecompositionDecl *
+CXXExpansionStmtPattern::getDecompositionDecl() {
+ assert(isDestructuring());
+ return cast<DecompositionDecl>(
+ cast<DeclStmt>(getDecompositionDeclStmt())->getSingleDecl());
+}
+
+VarDecl *CXXExpansionStmtPattern::getExpansionVariable() {
+ Decl *LV = cast<DeclStmt>(getExpansionVarStmt())->getSingleDecl();
+ assert(LV && "No expansion variable in CXXExpansionStmtPattern");
+ return cast<VarDecl>(LV);
+}
+
+unsigned
+CXXExpansionStmtPattern::getNumSubStmts(ExpansionStmtKind PatternKind) {
+ switch (PatternKind) {
+ case ExpansionStmtKind::Enumerating:
+ return COUNT_Enumerating;
+ case ExpansionStmtKind::Iterating:
+ return COUNT_Iterating;
+ case ExpansionStmtKind::Destructuring:
+ return COUNT_Destructuring;
+ case ExpansionStmtKind::Dependent:
+ return COUNT_Dependent;
+ }
+
+ llvm_unreachable("invalid pattern kind");
+}
+
+CXXExpansionStmtInstantiation::CXXExpansionStmtInstantiation(
+ EmptyShell Empty, unsigned NumInstantiations, unsigned NumPreambleStmts)
+ : Stmt(CXXExpansionStmtInstantiationClass, Empty),
+ NumInstantiations(NumInstantiations), NumPreambleStmts(NumPreambleStmts) {
+ assert(NumPreambleStmts <= 4 && "might have to allocate more bits for this");
+}
+
+CXXExpansionStmtInstantiation::CXXExpansionStmtInstantiation(
+ CXXExpansionStmtDecl *Parent, ArrayRef<Stmt *> Instantiations,
+ ArrayRef<Stmt *> PreambleStmts, bool ShouldApplyLifetimeExtensionToPreamble)
+ : Stmt(CXXExpansionStmtInstantiationClass), Parent(Parent),
+ NumInstantiations(unsigned(Instantiations.size())),
+ NumPreambleStmts(unsigned(PreambleStmts.size())),
+ ShouldApplyLifetimeExtensionToPreamble(
+ ShouldApplyLifetimeExtensionToPreamble) {
+ assert(NumPreambleStmts <= 4 && "might have to allocate more bits for this");
+ llvm::uninitialized_copy(Instantiations, getTrailingObjects());
+ llvm::uninitialized_copy(PreambleStmts,
+ getTrailingObjects() + NumInstantiations);
+}
+
+CXXExpansionStmtInstantiation *CXXExpansionStmtInstantiation::Create(
+ ASTContext &C, CXXExpansionStmtDecl *Parent,
+ ArrayRef<Stmt *> Instantiations, ArrayRef<Stmt *> PreambleStmts,
+ bool ShouldApplyLifetimeExtensionToPreamble) {
+ void *Mem = C.Allocate(
+ totalSizeToAlloc<Stmt *>(Instantiations.size() + PreambleStmts.size()),
+ alignof(CXXExpansionStmtInstantiation));
+ return new (Mem)
+ CXXExpansionStmtInstantiation(Parent, Instantiations, PreambleStmts,
+ ShouldApplyLifetimeExtensionToPreamble);
+}
+
+CXXExpansionStmtInstantiation *
+CXXExpansionStmtInstantiation::CreateEmpty(ASTContext &C, EmptyShell Empty,
+ unsigned NumInstantiations,
+ unsigned NumPreambleStmts) {
+ void *Mem =
+ C.Allocate(totalSizeToAlloc<Stmt *>(NumInstantiations + NumPreambleStmts),
+ alignof(CXXExpansionStmtInstantiation));
+ return new (Mem)
+ CXXExpansionStmtInstantiation(Empty, NumInstantiations, NumPreambleStmts);
+}
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index 4d364fdcd5502..1cc7a20c0e4bf 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -263,7 +263,8 @@ void StmtPrinter::VisitDeclStmt(DeclStmt *Node) {
PrintRawDeclStmt(Node);
// Certain pragma declarations shouldn't have a semi-colon after them.
if (!Node->isSingleDecl() ||
- !isa<OpenACCDeclareDecl, OpenACCRoutineDecl>(Node->getSingleDecl()))
+ !isa<CXXExpansionStmtDecl, OpenACCDeclareDecl, OpenACCRoutineDecl>(
+ Node->getSingleDecl()))
OS << ";";
OS << NL;
}
@@ -447,6 +448,38 @@ void StmtPrinter::VisitCXXForRangeStmt(CXXForRangeStmt *Node) {
PrintControlledStmt(Node->getBody());
}
+void StmtPrinter::VisitCXXExpansionStmtPattern(CXXExpansionStmtPattern *Node) {
+ OS << "template for (";
+ if (Node->getInit())
+ PrintInitStmt(Node->getInit(), 14);
+ PrintingPolicy SubPolicy(Policy);
+ SubPolicy.SuppressInitializers = true;
+ Node->getExpansionVariable()->print(OS, SubPolicy, IndentLevel);
+ OS << " : ";
+
+ if (Node->isIterating())
+ PrintExpr(Node->getRangeVar()->getInit());
+ else if (Node->isDependent())
+ PrintExpr(Node->getExpansionInitializer());
+ else if (Node->isDestructuring())
+ PrintExpr(Node->getDecompositionDecl()->getInit());
+ else
+ PrintExpr(Node->getExpansionVariable()->getInit());
+
+ OS << ")";
+ PrintControlledStmt(Node->getBody());
+}
+
+void StmtPrinter::VisitCXXExpansionStmtInstantiation(
+ CXXExpansionStmtInstantiation *) {
+ llvm_unreachable("should never be printed");
+}
+
+void StmtPrinter::VisitCXXExpansionSelectExpr(
+ CXXExpansionSelectExpr *Node) {
+ PrintExpr(Node->getRangeExpr());
+}
+
void StmtPrinter::VisitMSDependentExistsStmt(MSDependentExistsStmt *Node) {
Indent();
if (Node->isIfExists())
diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index e8c1f8a8ecb5f..d5c08764d8e80 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -366,6 +366,17 @@ void StmtProfiler::VisitCXXForRangeStmt(const CXXForRangeStmt *S) {
VisitStmt(S);
}
+void StmtProfiler::VisitCXXExpansionStmtPattern(
+ const CXXExpansionStmtPattern *S) {
+ VisitStmt(S);
+}
+
+void StmtProfiler::VisitCXXExpansionStmtInstantiation(
+ const CXXExpansionStmtInstantiation *S) {
+ VisitStmt(S);
+ ID.AddBoolean(S->shouldApplyLifetimeExtensionToPreamble());
+}
+
void StmtProfiler::VisitMSDependentExistsStmt(const MSDependentExistsStmt *S) {
VisitStmt(S);
ID.AddBoolean(S->isIfExists());
@@ -2428,6 +2439,11 @@ void StmtProfiler::VisitSourceLocExpr(const SourceLocExpr *E) {
void StmtProfiler::VisitEmbedExpr(const EmbedExpr *E) { VisitExpr(E); }
+void StmtProfiler::VisitCXXExpansionSelectExpr(
+ const CXXExpansionSelectExpr *E) {
+ VisitExpr(E);
+}
+
void StmtProfiler::VisitRecoveryExpr(const RecoveryExpr *E) { VisitExpr(E); }
void StmtProfiler::VisitObjCObjectLiteral(const ObjCObjectLiteral *E) {
diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index 250ec8b666e05..0c3c47153d958 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -968,7 +968,11 @@ void TextNodeDumper::dumpBareDeclRef(const Decl *D) {
switch (ND->getKind()) {
case Decl::Decomposition: {
auto *DD = cast<DecompositionDecl>(ND);
- OS << " first_binding '" << DD->bindings()[0]->getDeclName() << '\'';
+
+ // Empty decomposition decls can occur in destructuring expansion
+ // statements.
+ if (!DD->bindings().empty())
+ OS << " first_binding '" << DD->bindings()[0]->getDeclName() << '\'';
break;
}
case Decl::Field: {
@@ -1512,6 +1516,32 @@ void clang::TextNodeDumper::VisitCoreturnStmt(const CoreturnStmt *Node) {
OS << " implicit";
}
+void TextNodeDumper::VisitCXXExpansionStmtPattern(
+ const CXXExpansionStmtPattern *Node) {
+ switch (Node->getKind()) {
+ case CXXExpansionStmtPattern::ExpansionStmtKind::Enumerating:
+ OS << " enumerating";
+ return;
+ case CXXExpansionStmtPattern::ExpansionStmtKind::Iterating:
+ OS << " iterating";
+ return;
+ case CXXExpansionStmtPattern::ExpansionStmtKind::Destructuring:
+ OS << " destructuring";
+ return;
+ case CXXExpansionStmtPattern::ExpansionStmtKind::Dependent:
+ OS << " dependent";
+ return;
+ }
+
+ llvm_unreachable("invalid expansion statement kind");
+}
+
+void TextNodeDumper::VisitCXXExpansionStmtInstantiation(
+ const CXXExpansionStmtInstantiation *Node) {
+ if (Node->shouldApplyLifetimeExtensionToPreamble())
+ OS << " applies_lifetime_extension";
+}
+
void TextNodeDumper::VisitConstantExpr(const ConstantExpr *Node) {
if (Node->hasAPValueResult())
AddChild("value",
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 8b5ffde1b73f3..047aa55dc4b4a 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -143,6 +143,9 @@ void CodeGenFunction::EmitDecl(const Decl &D, bool EvaluateConditionDecl) {
// None of these decls require codegen support.
return;
+ case Decl::CXXExpansionStmt:
+ llvm_unreachable("TODO");
+
case Decl::NamespaceAlias:
if (CGDebugInfo *DI = getDebugInfo())
DI->EmitNamespaceAlias(cast<NamespaceAliasDecl>(D));
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index a923002bec9b6..8d9eb67cc2cef 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -203,6 +203,10 @@ void CodeGenFunction::EmitStmt(const Stmt *S, ArrayRef<const Attr *> Attrs) {
case Stmt::CXXForRangeStmtClass:
EmitCXXForRangeStmt(cast<CXXForRangeStmt>(*S), Attrs);
break;
+ case Stmt::CXXExpansionStmtPatternClass:
+ llvm_unreachable("unexpanded expansion statements should not be emitted");
+ case Stmt::CXXExpansionStmtInstantiationClass:
+ llvm_unreachable("Todo");
case Stmt::SEHTryStmtClass:
EmitSEHTryStmt(cast<SEHTryStmt>(*S));
break;
diff --git a/clang/lib/Sema/IdentifierResolver.cpp b/clang/lib/Sema/IdentifierResolver.cpp
index 2213c3c837243..5daa42e4bc9bd 100644
--- a/clang/lib/Sema/IdentifierResolver.cpp
+++ b/clang/lib/Sema/IdentifierResolver.cpp
@@ -99,6 +99,8 @@ IdentifierResolver::~IdentifierResolver() {
/// isDeclInScope - If 'Ctx' is a function/method, isDeclInScope returns true
/// if 'D' is in Scope 'S', otherwise 'S' is ignored and isDeclInScope returns
/// true if 'D' belongs to the given declaration context.
+///
+/// If 'Ctx' is an expansion statement, we use its enclosing function instead.
bool IdentifierResolver::isDeclInScope(Decl *D, DeclContext *Ctx, Scope *S,
bool AllowInlineNamespace) const {
Ctx = Ctx->getRedeclContext();
@@ -107,7 +109,10 @@ bool IdentifierResolver::isDeclInScope(Decl *D, DeclContext *Ctx, Scope *S,
// conflict with other Decls.
if (LangOpt.HLSL && isa<HLSLBufferDecl>(D))
return false;
- if (Ctx->isFunctionOrMethod() || (S && S->isFunctionPrototypeScope())) {
+ if (Ctx->getEnclosingNonExpansionStatementContext()
+ ->getRedeclContext()
+ ->isFunctionOrMethod() ||
+ (S && S->isFunctionPrototypeScope())) {
// Ignore the scopes associated within transparent declaration contexts.
while (S->getEntity() &&
(S->getEntity()->isTransparentContext() ||
diff --git a/clang/lib/Sema/Scope.cpp b/clang/lib/Sema/Scope.cpp
index e66cce255230b..997378f3d8368 100644
--- a/clang/lib/Sema/Scope.cpp
+++ b/clang/lib/Sema/Scope.cpp
@@ -18,6 +18,7 @@
using namespace clang;
void Scope::setFlags(Scope *parent, unsigned flags) {
+ IsExpansionStmtScope = false;
AnyParent = parent;
Flags = flags;
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index ee750284e4266..50bc923ebc0a4 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -1701,14 +1701,15 @@ DeclContext *Sema::getFunctionLevelDeclContext(bool AllowLambda) const {
DeclContext *DC = CurContext;
while (true) {
- if (isa<BlockDecl>(DC) || isa<EnumDecl>(DC) || isa<CapturedDecl>(DC) ||
- isa<RequiresExprBodyDecl>(DC)) {
+ if (isa<BlockDecl, EnumDecl, CapturedDecl, RequiresExprBodyDecl,
+ CXXExpansionStmtDecl>(DC)) {
DC = DC->getParent();
} else if (!AllowLambda && isa<CXXMethodDecl>(DC) &&
cast<CXXMethodDecl>(DC)->getOverloadedOperator() == OO_Call &&
cast<CXXRecordDecl>(DC->getParent())->isLambda()) {
DC = DC->getParent()->getParent();
- } else break;
+ } else
+ break;
}
return DC;
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 90307423a20b6..16a34475aac1a 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -7467,7 +7467,7 @@ static bool shouldConsiderLinkage(const VarDecl *VD) {
if (DC->getDeclKind() == Decl::HLSLBuffer)
return false;
- if (isa<RequiresExprBodyDecl>(DC))
+ if (isa<RequiresExprBodyDecl, CXXExpansionStmtDecl>(DC))
return false;
llvm_unreachable("Unexpected context");
}
@@ -7477,7 +7477,7 @@ static bool shouldConsiderLinkage(const FunctionDecl *FD) {
if (DC->isFileContext() || DC->isFunctionOrMethod() ||
isa<OMPDeclareReductionDecl>(DC) || isa<OMPDeclareMapperDecl>(DC))
return true;
- if (DC->isRecord())
+ if (DC->isRecord() || isa<CXXExpansionStmtDecl>(DC))
return false;
llvm_unreachable("Unexpected context");
}
diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp
index 56079ea8e1bf8..cabcb3ae7bc6d 100644
--- a/clang/lib/Sema/SemaExceptionSpec.cpp
+++ b/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -1302,6 +1302,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
case Expr::ConvertVectorExprClass:
case Expr::VAArgExprClass:
case Expr::CXXParenListInitExprClass:
+ case Expr::CXXExpansionSelectExprClass:
return canSubStmtsThrow(*this, S);
case Expr::CompoundLiteralExprClass:
@@ -1554,6 +1555,13 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
case Stmt::SwitchStmtClass:
case Stmt::WhileStmtClass:
case Stmt::DeferStmtClass:
+ case Stmt::CXXExpansionStmtInstantiationClass:
+ return canSubStmtsThrow(*this, S);
+
+ case Stmt::CXXExpansionStmtPatternClass:
+ if (auto *Pattern = cast<CXXExpansionStmtPattern>(S);
+ Pattern->isDependent())
+ return CT_Dependent;
return canSubStmtsThrow(*this, S);
case Stmt::DeclStmtClass: {
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 102b6315b4e3b..66306d172a462 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -19679,11 +19679,12 @@ bool Sema::tryCaptureVariable(
QualType &DeclRefType, const unsigned *const FunctionScopeIndexToStopAt) {
// An init-capture is notionally from the context surrounding its
// declaration, but its parent DC is the lambda class.
- DeclContext *VarDC = Var->getDeclContext();
+ DeclContext *VarDC =
+ Var->getDeclContext()->getEnclosingNonExpansionStatementContext();
DeclContext *DC = CurContext;
// Skip past RequiresExprBodys because they don't constitute function scopes.
- while (DC->isRequiresExprBody())
+ while (DC->isRequiresExprBody() || DC->isExpansionStmt())
DC = DC->getParent();
// tryCaptureVariable is called every time a DeclRef is formed,
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index f7e005a40363c..b47fbbf691133 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -7645,7 +7645,6 @@ static void CheckIfAnyEnclosingLambdasMustCaptureAnyPotentialCaptures(
Expr *const FE, LambdaScopeInfo *const CurrentLSI, Sema &S) {
assert(!S.isUnevaluatedContext());
- assert(S.CurContext->isDependentContext());
#ifndef NDEBUG
DeclContext *DC = S.CurContext;
while (isa_and_nonnull<CapturedDecl>(DC))
diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index 8572e3a742a6c..db3eae4be1c41 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -100,8 +100,9 @@ static inline UnsignedOrNone getStackIndexOfNearestEnclosingCaptureReadyLambda(
// innermost nested lambda are dependent (otherwise we wouldn't have
// arrived here) - so we don't yet have a lambda that can capture the
// variable.
- if (IsCapturingVariable &&
- VarToCapture->getDeclContext()->Equals(EnclosingDC))
+ if (IsCapturingVariable && VarToCapture->getDeclContext()
+ ->getEnclosingNonExpansionStatementContext()
+ ->Equals(EnclosingDC))
return NoLambdaIsCaptureReady;
// For an enclosing lambda to be capture ready for an entity, all
@@ -126,7 +127,8 @@ static inline UnsignedOrNone getStackIndexOfNearestEnclosingCaptureReadyLambda(
if (IsCapturingThis && !LSI->isCXXThisCaptured())
return NoLambdaIsCaptureReady;
}
- EnclosingDC = getLambdaAwareParentOfDeclContext(EnclosingDC);
+ EnclosingDC = getLambdaAwareParentOfDeclContext(EnclosingDC)
+ ->getEnclosingNonExpansionStatementContext();
assert(CurScopeIndex);
--CurScopeIndex;
@@ -190,11 +192,6 @@ UnsignedOrNone clang::getStackIndexOfNearestEnclosingCaptureCapableLambda(
return NoLambdaIsCaptureCapable;
const unsigned IndexOfCaptureReadyLambda = *OptionalStackIndex;
- assert(((IndexOfCaptureReadyLambda != (FunctionScopes.size() - 1)) ||
- S.getCurGenericLambda()) &&
- "The capture ready lambda for a potential capture can only be the "
- "current lambda if it is a generic lambda");
-
const sema::LambdaScopeInfo *const CaptureReadyLambdaLSI =
cast<sema::LambdaScopeInfo>(FunctionScopes[IndexOfCaptureReadyLambda]);
@@ -248,7 +245,7 @@ CXXRecordDecl *
Sema::createLambdaClosureType(SourceRange IntroducerRange, TypeSourceInfo *Info,
unsigned LambdaDependencyKind,
LambdaCaptureDefault CaptureDefault) {
- DeclContext *DC = CurContext;
+ DeclContext *DC = CurContext->getEnclosingNonExpansionStatementContext();
bool IsGenericLambda =
Info && getGenericLambdaTemplateParameterList(getCurLambda(), *this);
@@ -1403,7 +1400,9 @@ void Sema::ActOnLambdaClosureQualifiers(LambdaIntroducer &Intro,
// odr-use 'this' (in particular, in a default initializer for a non-static
// data member).
if (Intro.Default != LCD_None &&
- !LSI->Lambda->getParent()->isFunctionOrMethod() &&
+ !LSI->Lambda->getParent()
+ ->getEnclosingNonExpansionStatementContext()
+ ->isFunctionOrMethod() &&
(getCurrentThisType().isNull() ||
CheckCXXThisCapture(SourceLocation(), /*Explicit=*/true,
/*BuildAndDiagnose=*/false)))
@@ -2551,9 +2550,12 @@ Sema::LambdaScopeForCallOperatorInstantiationRAII::
while (FDPattern && FD) {
InstantiationAndPatterns.emplace_back(FDPattern, FD);
- FDPattern =
- dyn_cast<FunctionDecl>(getLambdaAwareParentOfDeclContext(FDPattern));
- FD = dyn_cast<FunctionDecl>(getLambdaAwareParentOfDeclContext(FD));
+ FDPattern = dyn_cast<FunctionDecl>(
+ getLambdaAwareParentOfDeclContext(FDPattern)
+ ->getEnclosingNonExpansionStatementContext());
+ FD = dyn_cast<FunctionDecl>(
+ getLambdaAwareParentOfDeclContext(FD)
+ ->getEnclosingNonExpansionStatementContext());
}
// Add instantiated parameters and local vars to scopes, starting from the
diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp
index de53f6010a1b6..9502b440dbe97 100644
--- a/clang/lib/Sema/SemaLookup.cpp
+++ b/clang/lib/Sema/SemaLookup.cpp
@@ -4460,7 +4460,9 @@ LabelDecl *Sema::LookupExistingLabel(IdentifierInfo *II, SourceLocation Loc) {
RedeclarationKind::NotForRedeclaration);
// If we found a label, check to see if it is in the same context as us.
// When in a Block, we don't want to reuse a label in an enclosing function.
- if (!Res || Res->getDeclContext() != CurContext)
+ if (!Res ||
+ Res->getDeclContext()->getEnclosingNonExpansionStatementContext() !=
+ CurContext->getEnclosingNonExpansionStatementContext())
return nullptr;
return cast<LabelDecl>(Res);
}
diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp
index f7de98a3e5cf0..740d2c6a6505c 100644
--- a/clang/lib/Sema/SemaStmtAttr.cpp
+++ b/clang/lib/Sema/SemaStmtAttr.cpp
@@ -38,6 +38,18 @@ static Attr *handleFallThroughAttr(Sema &S, Stmt *St, const ParsedAttr &A,
return nullptr;
}
+ // CWG 3045: The innermost enclosing switch statement of a fallthrough
+ // statement S shall be contained in the innermost enclosing expansion
+ // statement (8.7 [stmt.expand]) of S, if any.
+ for (Scope *Sc = S.getCurScope();
+ Sc && !Sc->isFunctionScope() && !Sc->isSwitchScope();
+ Sc = Sc->getParent()) {
+ if (Sc->isExpansionStmtScope()) {
+ S.Diag(A.getLoc(), diag::err_fallthrough_attr_invalid_placement);
+ return nullptr;
+ }
+ }
+
// If this is spelled as the standard C++17 attribute, but not in C++17, warn
// about using it as an extension.
if (!S.getLangOpts().CPlusPlus17 && A.isCXX11Attribute() &&
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index cc24e03e77c07..d057476a012db 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -2101,6 +2101,11 @@ Decl *TemplateDeclInstantiator::VisitStaticAssertDecl(StaticAssertDecl *D) {
InstantiatedMessageExpr.get(), D->getRParenLoc(), D->isFailed());
}
+Decl *TemplateDeclInstantiator::VisitCXXExpansionStmtDecl(
+ CXXExpansionStmtDecl *OldESD) {
+ llvm_unreachable("TODO");
+}
+
Decl *TemplateDeclInstantiator::VisitEnumDecl(EnumDecl *D) {
EnumDecl *PrevDecl = nullptr;
if (EnumDecl *PatternPrev = getPreviousDeclForInstantiation(D)) {
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 8ae5df367e0dd..ad8e0b76f1dcb 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -9369,6 +9369,24 @@ TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) {
return FinishCXXForRangeStmt(NewStmt.get(), Body.get());
}
+template <typename Derived>
+StmtResult TreeTransform<Derived>::TransformCXXExpansionStmtPattern(
+ CXXExpansionStmtPattern *S) {
+ llvm_unreachable("TOOD");
+}
+
+template <typename Derived>
+StmtResult TreeTransform<Derived>::TransformCXXExpansionStmtInstantiation(
+ CXXExpansionStmtInstantiation *S) {
+ llvm_unreachable("TOOD");
+}
+
+template <typename Derived>
+ExprResult TreeTransform<Derived>::TransformCXXExpansionSelectExpr(
+ CXXExpansionSelectExpr *E) {
+ llvm_unreachable("TOOD");
+}
+
template<typename Derived>
StmtResult
TreeTransform<Derived>::TransformMSDependentExistsStmt(
diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp
index 69db02f2efc40..d07661a5b2f64 100644
--- a/clang/lib/Serialization/ASTCommon.cpp
+++ b/clang/lib/Serialization/ASTCommon.cpp
@@ -459,6 +459,7 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) {
case Decl::HLSLRootSignature:
case Decl::OpenACCDeclare:
case Decl::OpenACCRoutine:
+ case Decl::CXXExpansionStmt:
return false;
// These indirectly derive from Redeclarable<T> but are not actually
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index b49bd5ea8bca6..e0b8589af33dd 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -405,6 +405,7 @@ class ASTDeclReader : public DeclVisitor<ASTDeclReader, void> {
void VisitFriendDecl(FriendDecl *D);
void VisitFriendTemplateDecl(FriendTemplateDecl *D);
void VisitStaticAssertDecl(StaticAssertDecl *D);
+ void VisitCXXExpansionStmtDecl(CXXExpansionStmtDecl *D);
void VisitBlockDecl(BlockDecl *BD);
void VisitOutlinedFunctionDecl(OutlinedFunctionDecl *D);
void VisitCapturedDecl(CapturedDecl *CD);
@@ -2784,6 +2785,14 @@ void ASTDeclReader::VisitStaticAssertDecl(StaticAssertDecl *D) {
D->RParenLoc = readSourceLocation();
}
+void ASTDeclReader::VisitCXXExpansionStmtDecl(CXXExpansionStmtDecl *D) {
+ VisitDecl(D);
+ D->Pattern = cast<CXXExpansionStmtPattern>(Record.readStmt());
+ D->Instantiations =
+ cast_or_null<CXXExpansionStmtInstantiation>(Record.readStmt());
+ D->IndexNTTP = cast<NonTypeTemplateParmDecl>(Record.readDeclRef());
+}
+
void ASTDeclReader::VisitEmptyDecl(EmptyDecl *D) {
VisitDecl(D);
}
@@ -4106,6 +4115,9 @@ Decl *ASTReader::ReadDeclRecord(GlobalDeclID ID) {
case DECL_STATIC_ASSERT:
D = StaticAssertDecl::CreateDeserialized(Context, ID);
break;
+ case DECL_EXPANSION_STMT:
+ D = CXXExpansionStmtDecl::CreateDeserialized(Context, ID);
+ break;
case DECL_OBJC_METHOD:
D = ObjCMethodDecl::CreateDeserialized(Context, ID);
break;
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 801eed43c2440..41a35dbf72f15 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -1775,6 +1775,34 @@ void ASTStmtReader::VisitCXXForRangeStmt(CXXForRangeStmt *S) {
S->setBody(Record.readSubStmt());
}
+void ASTStmtReader::VisitCXXExpansionStmtPattern(CXXExpansionStmtPattern *S) {
+ VisitStmt(S);
+ Record.skipInts(1); // Skip kind.
+ S->LParenLoc = readSourceLocation();
+ S->ColonLoc = readSourceLocation();
+ S->RParenLoc = readSourceLocation();
+ S->ParentDecl = cast<CXXExpansionStmtDecl>(Record.readDeclRef());
+ for (Stmt *&SubStmt : S->children())
+ SubStmt = Record.readSubStmt();
+}
+
+void ASTStmtReader::VisitCXXExpansionStmtInstantiation(
+ CXXExpansionStmtInstantiation *S) {
+ VisitStmt(S);
+ Record.skipInts(2);
+ S->Parent = cast<CXXExpansionStmtDecl>(Record.readDeclRef());
+ for (unsigned I = 0; I < S->getNumSubStmts(); ++I)
+ S->getAllSubStmts()[I] = Record.readSubStmt();
+ S->setShouldApplyLifetimeExtensionToPreamble(Record.readBool());
+}
+
+void ASTStmtReader::VisitCXXExpansionSelectExpr(
+ CXXExpansionSelectExpr *E) {
+ VisitExpr(E);
+ E->setRangeExpr(cast<InitListExpr>(Record.readSubExpr()));
+ E->setIndexExpr(Record.readSubExpr());
+}
+
void ASTStmtReader::VisitMSDependentExistsStmt(MSDependentExistsStmt *S) {
VisitStmt(S);
S->KeywordLoc = readSourceLocation();
@@ -3623,6 +3651,19 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
/*numHandlers=*/Record[ASTStmtReader::NumStmtFields]);
break;
+ case STMT_CXX_EXPANSION_PATTERN:
+ S = CXXExpansionStmtPattern::CreateEmpty(
+ Context, Empty,
+ static_cast<CXXExpansionStmtPattern::ExpansionStmtKind>(
+ Record[ASTStmtReader::NumStmtFields]));
+ break;
+
+ case STMT_CXX_EXPANSION_INSTANTIATION:
+ S = CXXExpansionStmtInstantiation::CreateEmpty(
+ Context, Empty, Record[ASTStmtReader::NumStmtFields],
+ Record[ASTStmtReader::NumStmtFields + 1]);
+ break;
+
case STMT_CXX_FOR_RANGE:
S = new (Context) CXXForRangeStmt(Empty);
break;
@@ -4500,6 +4541,11 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
S = new (Context) ConceptSpecializationExpr(Empty);
break;
}
+
+ case EXPR_CXX_EXPANSION_SELECT:
+ S = new (Context) CXXExpansionSelectExpr(Empty);
+ break;
+
case STMT_OPENACC_COMPUTE_CONSTRUCT: {
unsigned NumClauses = Record[ASTStmtReader::NumStmtFields];
S = OpenACCComputeConstruct::CreateEmpty(Context, NumClauses);
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index 7646d5d5efe00..71971e8ae0fcd 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -144,6 +144,7 @@ namespace clang {
void VisitFriendDecl(FriendDecl *D);
void VisitFriendTemplateDecl(FriendTemplateDecl *D);
void VisitStaticAssertDecl(StaticAssertDecl *D);
+ void VisitCXXExpansionStmtDecl(CXXExpansionStmtDecl *D);
void VisitBlockDecl(BlockDecl *D);
void VisitOutlinedFunctionDecl(OutlinedFunctionDecl *D);
void VisitCapturedDecl(CapturedDecl *D);
@@ -2175,6 +2176,14 @@ void ASTDeclWriter::VisitStaticAssertDecl(StaticAssertDecl *D) {
Code = serialization::DECL_STATIC_ASSERT;
}
+void ASTDeclWriter::VisitCXXExpansionStmtDecl(CXXExpansionStmtDecl *D) {
+ VisitDecl(D);
+ Record.AddStmt(D->getExpansionPattern());
+ Record.AddStmt(D->getInstantiations());
+ Record.AddDeclRef(D->getIndexTemplateParm());
+ Code = serialization::DECL_EXPANSION_STMT;
+}
+
/// Emit the DeclContext part of a declaration context decl.
void ASTDeclWriter::VisitDeclContext(DeclContext *DC) {
static_assert(DeclContext::NumDeclContextBits == 13,
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index 934a95df1be7e..da62a6a31c6dc 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -1757,6 +1757,38 @@ void ASTStmtWriter::VisitCXXForRangeStmt(CXXForRangeStmt *S) {
Code = serialization::STMT_CXX_FOR_RANGE;
}
+void ASTStmtWriter::VisitCXXExpansionStmtPattern(CXXExpansionStmtPattern *S) {
+ VisitStmt(S);
+ Record.push_back(static_cast<unsigned>(S->getKind()));
+ Record.AddSourceLocation(S->getLParenLoc());
+ Record.AddSourceLocation(S->getColonLoc());
+ Record.AddSourceLocation(S->getRParenLoc());
+ Record.AddDeclRef(S->getDecl());
+ for (Stmt* SubStmt : S->children())
+ Record.AddStmt(SubStmt);
+ Code = serialization::STMT_CXX_EXPANSION_PATTERN;
+}
+
+void ASTStmtWriter::VisitCXXExpansionStmtInstantiation(
+ CXXExpansionStmtInstantiation *S) {
+ VisitStmt(S);
+ Record.push_back(S->getInstantiations().size());
+ Record.push_back(S->getPreambleStmts().size());
+ Record.AddDeclRef(S->getParent());
+ for (Stmt *St : S->getAllSubStmts())
+ Record.AddStmt(St);
+ Record.push_back(S->shouldApplyLifetimeExtensionToPreamble());
+ Code = serialization::STMT_CXX_EXPANSION_INSTANTIATION;
+}
+
+void ASTStmtWriter::VisitCXXExpansionSelectExpr(
+ CXXExpansionSelectExpr *E) {
+ VisitExpr(E);
+ Record.AddStmt(E->getRangeExpr());
+ Record.AddStmt(E->getIndexExpr());
+ Code = serialization::EXPR_CXX_EXPANSION_SELECT;
+}
+
void ASTStmtWriter::VisitMSDependentExistsStmt(MSDependentExistsStmt *S) {
VisitStmt(S);
Record.AddSourceLocation(S->getKeywordLoc());
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index e9522a7975515..76a682bf983cf 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1745,6 +1745,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::SEHExceptStmtClass:
case Stmt::SEHLeaveStmtClass:
case Stmt::SEHFinallyStmtClass:
+ case Stmt::CXXExpansionStmtPatternClass:
+ case Stmt::CXXExpansionStmtInstantiationClass:
+ case Stmt::CXXExpansionSelectExprClass:
case Stmt::OMPCanonicalLoopClass:
case Stmt::OMPParallelDirectiveClass:
case Stmt::OMPSimdDirectiveClass:
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index 3ee37ed2dfc27..a6d53af8e4eda 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -7282,6 +7282,7 @@ CXCursor clang_getCursorDefinition(CXCursor C) {
case Decl::UnresolvedUsingIfExists:
case Decl::OpenACCDeclare:
case Decl::OpenACCRoutine:
+ case Decl::CXXExpansionStmt:
return C;
// Declaration kinds that don't make any sense here, but are
diff --git a/clang/tools/libclang/CXCursor.cpp b/clang/tools/libclang/CXCursor.cpp
index d31d2c0c9bb67..08624d4ce3f73 100644
--- a/clang/tools/libclang/CXCursor.cpp
+++ b/clang/tools/libclang/CXCursor.cpp
@@ -295,6 +295,8 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent,
case Stmt::CoroutineBodyStmtClass:
case Stmt::CoreturnStmtClass:
+ case Stmt::CXXExpansionStmtPatternClass:
+ case Stmt::CXXExpansionStmtInstantiationClass:
K = CXCursor_UnexposedStmt;
break;
@@ -345,6 +347,7 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent,
case Stmt::EmbedExprClass:
case Stmt::HLSLOutArgExprClass:
case Stmt::OpenACCAsteriskSizeExprClass:
+ case Stmt::CXXExpansionSelectExprClass:
K = CXCursor_UnexposedExpr;
break;
More information about the cfe-commits
mailing list