[clang] [Clang] [C++26] Expansion Statements (P1306R5) (PR #169680)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Jul 10 13:49:33 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 1/5] [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;
>From 8fa0c738926406a4b1ecce04e759b290fc1f6cd5 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Fri, 10 Jul 2026 22:04:34 +0200
Subject: [PATCH 2/5] [Clang] [C++26] Expansion Statements (Part 2: Parsing and
Parser Tests) (#169681)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This patch implements parsing of expansion statements and expansion
initialiser lists. I’ve also had to implement sema for
`CXXExpansionStmtDecl` because parsing doesn’t really work without that
node.
---
clang/docs/ReleaseNotes.rst | 2 +
.../clang/Basic/DiagnosticParseKinds.td | 11 +-
.../clang/Basic/DiagnosticSemaKinds.td | 27 +-
clang/include/clang/Parse/Parser.h | 37 +-
clang/include/clang/Sema/ScopeInfo.h | 9 +-
clang/include/clang/Sema/Sema.h | 105 +-
clang/lib/AST/ByteCode/Compiler.cpp | 42 +
clang/lib/AST/ByteCode/Compiler.h | 3 +-
clang/lib/AST/ExprConstant.cpp | 40 +
clang/lib/CodeGen/CGDecl.cpp | 8 +-
clang/lib/CodeGen/CGStmt.cpp | 29 +-
clang/lib/CodeGen/CodeGenFunction.h | 3 +
clang/lib/Frontend/FrontendActions.cpp | 2 +
clang/lib/Parse/ParseDecl.cpp | 59 +-
clang/lib/Parse/ParseExpr.cpp | 14 +-
clang/lib/Parse/ParseInit.cpp | 19 +
clang/lib/Parse/ParseStmt.cpp | 195 +-
clang/lib/Sema/CMakeLists.txt | 1 +
clang/lib/Sema/SemaDecl.cpp | 10 +-
clang/lib/Sema/SemaDeclCXX.cpp | 3 +
clang/lib/Sema/SemaExpand.cpp | 641 +++++++
clang/lib/Sema/SemaLookup.cpp | 47 +-
clang/lib/Sema/SemaStmt.cpp | 542 +++---
clang/lib/Sema/SemaTemplateInstantiate.cpp | 29 +-
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 38 +-
clang/lib/Sema/SemaTemplateVariadic.cpp | 8 +-
clang/lib/Sema/TreeTransform.h | 190 +-
clang/test/AST/ast-dump-expansion-stmt.cpp | 54 +
clang/test/AST/ast-print-expansion-stmts.cpp | 113 ++
.../cxx2c-destructuring-expansion-stmt.cpp | 532 ++++++
...cxx2c-enumerating-expansion-statements.cpp | 1518 ++++++++++++++++
.../cxx2c-expansion-stmts-control-flow.cpp | 430 +++++
.../cxx2c-expansion-stmts-mangling.cpp | 134 ++
.../cxx2c-expansion-stmts-templates.cpp | 208 +++
.../cxx2c-iterating-expansion-stmt.cpp | 551 ++++++
...2c-expansion-statements-not-backported.cpp | 5 +
.../Parser/cxx2c-expansion-statements.cpp | 68 +
.../cxx2c-expansion-statements-shadow.cpp | 11 +
.../cxx2c-expansion-stmts-control-flow.cpp | 135 ++
clang/test/SemaCXX/cxx2c-expansion-stmts.cpp | 1590 +++++++++++++++++
clang/test/SemaTemplate/GH176155.cpp | 20 +-
clang/www/cxx_status.html | 8 +-
42 files changed, 7157 insertions(+), 334 deletions(-)
create mode 100644 clang/lib/Sema/SemaExpand.cpp
create mode 100644 clang/test/AST/ast-dump-expansion-stmt.cpp
create mode 100644 clang/test/AST/ast-print-expansion-stmts.cpp
create mode 100644 clang/test/CodeGenCXX/cxx2c-destructuring-expansion-stmt.cpp
create mode 100644 clang/test/CodeGenCXX/cxx2c-enumerating-expansion-statements.cpp
create mode 100644 clang/test/CodeGenCXX/cxx2c-expansion-stmts-control-flow.cpp
create mode 100644 clang/test/CodeGenCXX/cxx2c-expansion-stmts-mangling.cpp
create mode 100644 clang/test/CodeGenCXX/cxx2c-expansion-stmts-templates.cpp
create mode 100644 clang/test/CodeGenCXX/cxx2c-iterating-expansion-stmt.cpp
create mode 100644 clang/test/Parser/cxx2c-expansion-statements-not-backported.cpp
create mode 100644 clang/test/Parser/cxx2c-expansion-statements.cpp
create mode 100644 clang/test/SemaCXX/cxx2c-expansion-statements-shadow.cpp
create mode 100644 clang/test/SemaCXX/cxx2c-expansion-stmts-control-flow.cpp
create mode 100644 clang/test/SemaCXX/cxx2c-expansion-stmts.cpp
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index fc17eead02589..1fab855bb1fc2 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -126,6 +126,8 @@ C++ Language Changes
C++2c Feature Support
^^^^^^^^^^^^^^^^^^^^^
+- Clang now has partial support for `P1306R5 <https://wg21.link/P1306R5>`_ Expansion Statements. Iterating expansion
+ statements currently cannot be expanded and will result in a diagnostic, but other types of expansion statements work.
C++23 Feature Support
^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 7bcd1870a2600..b0f9819397842 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -421,9 +421,10 @@ def warn_cxx98_compat_for_range : Warning<
"range-based for loop is incompatible with C++98">,
InGroup<CXX98Compat>, DefaultIgnore;
def err_for_range_identifier : Error<
- "range-based for loop requires type for loop variable">;
+ "%select{range-based for loop|expansion statement}0 requires "
+ "type for %select{loop|expansion}0 variable">;
def err_for_range_expected_decl : Error<
- "for range declaration must declare a variable">;
+ "%select{for range|expansion statement}0 declaration must declare a variable">;
def err_argument_required_after_attribute : Error<
"argument required after attribute">;
def err_missing_param : Error<"expected parameter declarator">;
@@ -450,6 +451,12 @@ def err_unspecified_size_with_static : Error<
"'static' may not be used without an array size">;
def err_expected_parentheses_around_typename : Error<
"expected parentheses around type name in %0 expression">;
+def err_expansion_stmt_requires_range : Error<
+ "expansion statement must use the syntax of a range-based for loop">;
+def err_expansion_stmt_requires_cxx2c : Error<
+ "expansion statements are only supported in C++2c">;
+def err_for_template : Error<
+ "'for template' is invalid; use 'template for' instead">;
def err_expected_case_before_expression: Error<
"expected 'case' keyword before expression">;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 4d352f1def04b..bfe0fe75389fa 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -165,6 +165,10 @@ def err_ice_too_large : Error<
def err_expr_not_string_literal : Error<"expression is not a string literal">;
def note_constexpr_assert_failed : Note<
"assertion failed during evaluation of constant expression">;
+def err_expansion_size_expr_not_ice : Error<
+ "expansion statement size is not a constant expression">;
+def err_iterating_expansion_stmt_unsupported : Error<
+ "iterating expansion statements are not yet supported">;
// Semantic analysis of constant literals.
def ext_predef_outside_function : Warning<
@@ -2926,10 +2930,10 @@ def note_which_delegates_to : Note<"which delegates to">;
// C++11 range-based for loop
def err_for_range_decl_must_be_var : Error<
- "for range declaration must declare a variable">;
+ "%select{for range|expansion statement}0 declaration must declare a variable">;
def err_for_range_storage_class : Error<
- "loop variable %0 may not be declared %select{'extern'|'static'|"
- "'__private_extern__'|'auto'|'register'|'constexpr'|'thread_local'}1">;
+ "%select{loop|expansion}0 variable %1 may not be declared %select{'extern'|'static'|"
+ "'__private_extern__'|'auto'|'register'|'constexpr'|'thread_local'}2">;
def err_type_defined_in_for_range : Error<
"types may not be defined in a for range declaration">;
def err_for_range_deduction_failure : Error<
@@ -3734,6 +3738,21 @@ def err_conflicting_codeseg_attribute : Error<
def warn_duplicate_codeseg_attribute : Warning<
"duplicate code segment specifiers">, InGroup<Section>;
+def err_expansion_stmt_invalid_init : Error<
+ "cannot expand expression of type %0">;
+def err_expansion_stmt_vla : Error<
+ "cannot expand variable length array type %0">;
+def err_expansion_stmt_incomplete : Error<
+ "cannot expand expression of incomplete type %0">;
+def err_expansion_stmt_lambda : Error<
+ "cannot expand lambda closure type">;
+def err_expansion_stmt_case : Error<
+ "%select{'case'|'default'}0 belongs to 'switch' outside enclosing expansion statement">;
+def note_enclosing_switch_statement_here : Note<
+ "switch statement is here">;
+def err_expansion_stmt_label : Error<
+ "labels are not allowed in expansion statements">;
+
def err_attribute_patchable_function_entry_invalid_section
: Error<"section argument to 'patchable_function_entry' attribute is not "
"valid for this target: %0">;
@@ -5954,6 +5973,8 @@ def note_template_nsdmi_here : Note<
"in instantiation of default member initializer %q0 requested here">;
def note_template_type_alias_instantiation_here : Note<
"in instantiation of template type alias %0 requested here">;
+def note_expansion_stmt_instantiation_here : Note<
+ "in instantiation of expansion statement requested here">;
def note_template_exception_spec_instantiation_here : Note<
"in instantiation of exception specification for %0 requested here">;
def note_template_requirement_instantiation_here : Note<
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 08a3d88ee6a36..4d23f62a44646 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -1718,6 +1718,7 @@ class Parser : public CodeCompletionHandler {
SourceLocation ColonLoc;
ExprResult RangeExpr;
SmallVector<MaterializeTemporaryExpr *, 8> LifetimeExtendTemps;
+ CXXExpansionStmtDecl* ExpansionStmt = nullptr;
bool ParsedForRangeDecl() { return !ColonLoc.isInvalid(); }
};
struct ForRangeInfo : ForRangeInit {
@@ -4196,7 +4197,8 @@ class Parser : public CodeCompletionHandler {
bool ParseExpressionList(SmallVectorImpl<Expr *> &Exprs,
llvm::function_ref<void()> ExpressionStarts =
llvm::function_ref<void()>(),
- bool FailImmediatelyOnInvalidExpr = false);
+ bool FailImmediatelyOnInvalidExpr = false,
+ bool ParsingExpansionStmtInitList = false);
/// ParseSimpleExpressionList - A simple comma-separated list of expressions,
/// used for misc language extensions.
@@ -5289,6 +5291,16 @@ class Parser : public CodeCompletionHandler {
///
ExprResult ParseBraceInitializer();
+ /// ParseExpansionInitList - Called when the initializer of an expansion
+ /// statement starts with an open brace.
+ ///
+ /// \verbatim
+ /// expansion-init-list: [C++26 [stmt.expand]]
+ /// '{' expression-list ','[opt] '}'
+ /// '{' '}'
+ /// \endverbatim
+ ExprResult ParseExpansionInitList();
+
struct DesignatorCompletionInfo {
SmallVectorImpl<Expr *> &InitExprs;
QualType PreferredBaseType;
@@ -7503,7 +7515,11 @@ class Parser : public CodeCompletionHandler {
/// [C++0x] braced-init-list [TODO]
/// \endverbatim
StmtResult ParseForStatement(SourceLocation *TrailingElseLoc,
- LabelDecl *PrecedingLabel);
+ LabelDecl *PrecedingLabel,
+ CXXExpansionStmtDecl *ESD = nullptr);
+
+ void ParseForRangeInitializerAfterColon(ForRangeInit &FRI,
+ ParsingDeclSpec *VarDeclSpec);
/// ParseGotoStatement
/// \verbatim
@@ -7560,6 +7576,23 @@ class Parser : public CodeCompletionHandler {
/// \endverbatim
StmtResult ParseDeferStatement(SourceLocation *TrailingElseLoc);
+ /// ParseExpansionStatement - Parse a C++26 expansion
+ /// statement ('template for').
+ ///
+ /// \verbatim
+ /// expansion-statement:
+ /// 'template' 'for' '(' init-statement[opt]
+ /// for-range-declaration ':' expansion-initializer ')'
+ /// compound-statement
+ ///
+ /// expansion-initializer:
+ /// expression
+ /// expansion-init-list
+ /// \endverbatim
+ StmtResult ParseExpansionStatement(SourceLocation *TrailingElseLoc,
+ LabelDecl *PrecedingLabel,
+ SourceLocation TemplateLoc);
+
StmtResult ParsePragmaLoopHint(StmtVector &Stmts, ParsedStmtContext StmtCtx,
SourceLocation *TrailingElseLoc,
ParsedAttributes &Attrs,
diff --git a/clang/include/clang/Sema/ScopeInfo.h b/clang/include/clang/Sema/ScopeInfo.h
index f334f58ebd0a7..a0753fa333770 100644
--- a/clang/include/clang/Sema/ScopeInfo.h
+++ b/clang/include/clang/Sema/ScopeInfo.h
@@ -201,8 +201,13 @@ class FunctionScopeInfo {
public:
/// A SwitchStmt, along with a flag indicating if its list of case statements
- /// is incomplete (because we dropped an invalid one while parsing).
- using SwitchInfo = llvm::PointerIntPair<SwitchStmt*, 1, bool>;
+ /// is incomplete (because we dropped an invalid one while parsing), as well
+ /// as the DeclContext containing the statement.
+ struct SwitchInfo : llvm::PointerIntPair<SwitchStmt *, 1, bool> {
+ DeclContext *EnclosingDC;
+ SwitchInfo(SwitchStmt *Switch, DeclContext *DC)
+ : PointerIntPair(Switch, false), EnclosingDC(DC) {}
+ };
/// SwitchStack - This is the current set of active switch statements in the
/// block.
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 1b8a9803be472..d41cf7cdbcc00 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -907,6 +907,7 @@ class Sema final : public SemaBase {
// 33. Types (SemaType.cpp)
// 34. FixIt Helpers (SemaFixItUtils.cpp)
// 35. Function Effects (SemaFunctionEffects.cpp)
+ // 36. C++ Expansion Statements (SemaExpand.cpp)
/// \name Semantic Analysis
/// Implementations are in Sema.cpp
@@ -4164,7 +4165,7 @@ class Sema final : public SemaBase {
/// complete.
void ActOnInitializerError(Decl *Dcl);
- void ActOnCXXForRangeDecl(Decl *D);
+ void ActOnCXXForRangeDecl(Decl *D, bool InExpansionStmt);
StmtResult ActOnCXXForRangeIdentifier(Scope *S, SourceLocation IdentLoc,
IdentifierInfo *Ident,
ParsedAttributes &Attrs);
@@ -9609,9 +9610,11 @@ class Sema final : public SemaBase {
/// LookupOrCreateLabel - Do a name lookup of a label with the specified name.
/// If GnuLabelLoc is a valid source location, then this is a definition
/// of an __label__ label name, otherwise it is a normal label definition
- /// or use.
+ /// or use. If IsLabelStmt is true, then this is the label of a
+ /// labeled-statement.
LabelDecl *LookupOrCreateLabel(IdentifierInfo *II, SourceLocation IdentLoc,
- SourceLocation GnuLabelLoc = SourceLocation());
+ SourceLocation GnuLabelLoc = SourceLocation(),
+ bool IsLabelStmt = false);
/// Perform a name lookup for a label with the specified name; this does not
/// create a new label if the lookup fails.
@@ -11177,6 +11180,43 @@ class Sema final : public SemaBase {
BuildForRangeKind Kind,
ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps = {});
+ /// Set the type of a for-range declaration whose for-range or expansion
+ /// initialiser is dependent.
+ void ActOnDependentForRangeInitializer(VarDecl *LoopVar,
+ BuildForRangeKind BFRK);
+
+ /// Holds the 'begin' and 'end' variables of a range-based for loop or
+ /// expansion statement; begin-expr and end-expr are also provided; the
+ /// latter are used in some diagnostics.
+ struct ForRangeBeginEndInfo {
+ VarDecl *BeginVar = nullptr;
+ VarDecl *EndVar = nullptr;
+ Expr *BeginExpr = nullptr;
+ Expr *EndExpr = nullptr;
+ bool isValid() const { return BeginVar != nullptr && EndVar != nullptr; }
+ };
+
+ /// Determine begin-expr and end-expr and build variable declarations for
+ /// them as per [stmt.ranged].
+ ForRangeBeginEndInfo BuildCXXForRangeBeginEndVars(
+ Scope *S, VarDecl *RangeVar, SourceLocation ColonLoc,
+ SourceLocation CoawaitLoc,
+ ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps,
+ BuildForRangeKind Kind, bool IsConstexpr,
+ StmtResult *RebuildResult = nullptr,
+ llvm::function_ref<StmtResult()> RebuildWithDereference = {},
+ IdentifierInfo *BeginName = nullptr, IdentifierInfo *EndName = nullptr);
+
+ /// Helper used by the expansion statements and for-range code to build
+ /// a variable declaration for e.g. 'begin' and 'end'.
+ VarDecl *BuildForRangeVarDecl(SourceLocation Loc, QualType Type,
+ IdentifierInfo *Name, bool IsConstexpr);
+
+ /// Build the range variable of a range-based for loop or iterating
+ /// expansion statement and return its DeclStmt.
+ StmtResult BuildCXXForRangeRangeVar(Scope *S, Expr *Range, QualType Type,
+ bool IsConstexpr = false);
+
/// FinishCXXForRangeStmt - Attach the body to a C++0x for-range statement.
/// This is a separate step from ActOnCXXForRangeStmt because analysis of the
/// body cannot be performed until after the type of the range variable is
@@ -11322,6 +11362,9 @@ class Sema final : public SemaBase {
SourceLocation Loc,
unsigned NumParams);
+ void ApplyForRangeOrExpansionStatementLifetimeExtension(
+ VarDecl *RangeVar, ArrayRef<MaterializeTemporaryExpr *> Temporaries);
+
private:
/// Check whether the given statement can have musttail applied to it,
/// issuing a diagnostic and returning false if not.
@@ -13309,6 +13352,9 @@ class Sema final : public SemaBase {
/// We are performing overload resolution for a call to a function
/// template or variable template named 'sycl_kernel_launch'.
SYCLKernelLaunchOverloadResolution,
+
+ /// We are instantiating an expansion statement.
+ ExpansionStmtInstantiation,
} Kind;
/// Whether we're substituting into constraints.
@@ -13504,6 +13550,12 @@ class Sema final : public SemaBase {
concepts::Requirement *Req,
SourceRange InstantiationRange = SourceRange());
+ /// \brief Note that we are substituting the body of an expansion statement.
+ InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
+ CXXExpansionStmtPattern *ExpansionStmt,
+ ArrayRef<TemplateArgument> TArgs,
+ SourceRange InstantiationRange);
+
/// \brief Note that we are checking the satisfaction of the constraint
/// expression inside of a nested requirement.
InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation,
@@ -15795,6 +15847,53 @@ class Sema final : public SemaBase {
void performFunctionEffectAnalysis(TranslationUnitDecl *TU);
///@}
+
+ //
+ //
+ // -------------------------------------------------------------------------
+ //
+ //
+
+ /// \name Expansion Statements
+ /// Implementations are in SemaExpand.cpp
+ ///@{
+public:
+ CXXExpansionStmtDecl *ActOnCXXExpansionStmtDecl(unsigned TemplateDepth,
+ SourceLocation TemplateKWLoc);
+
+ CXXExpansionStmtDecl *
+ BuildCXXExpansionStmtDecl(DeclContext *Ctx, SourceLocation TemplateKWLoc,
+ NonTypeTemplateParmDecl *NTTP);
+
+ ExprResult ActOnCXXExpansionInitList(MultiExprArg SubExprs,
+ SourceLocation LBraceLoc,
+ SourceLocation RBraceLoc);
+
+ StmtResult ActOnCXXExpansionStmtPattern(
+ CXXExpansionStmtDecl *ESD, Stmt *Init, Stmt *ExpansionVarStmt,
+ Expr *ExpansionInitializer, SourceLocation LParenLoc,
+ SourceLocation ColonLoc, SourceLocation RParenLoc,
+ ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps);
+
+ StmtResult FinishCXXExpansionStmt(Stmt *Expansion, Stmt *Body);
+
+ StmtResult BuildCXXEnumeratingExpansionStmtPattern(Decl *ESD, Stmt *Init,
+ Stmt *ExpansionVar,
+ SourceLocation LParenLoc,
+ SourceLocation ColonLoc,
+ SourceLocation RParenLoc);
+
+ StmtResult BuildNonEnumeratingCXXExpansionStmtPattern(
+ CXXExpansionStmtDecl *ESD, Stmt *Init, DeclStmt *ExpansionVarStmt,
+ Expr *ExpansionInitializer, SourceLocation LParenLoc,
+ SourceLocation ColonLoc, SourceLocation RParenLoc,
+ ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps = {});
+
+ ExprResult BuildCXXExpansionSelectExpr(InitListExpr *Range, Expr *Idx);
+
+ std::optional<uint64_t>
+ ComputeExpansionSize(CXXExpansionStmtPattern *Expansion);
+ ///@}
};
DeductionFailureInfo
diff --git a/clang/lib/AST/ByteCode/Compiler.cpp b/clang/lib/AST/ByteCode/Compiler.cpp
index c7f074c9efc6a..0215db156e516 100644
--- a/clang/lib/AST/ByteCode/Compiler.cpp
+++ b/clang/lib/AST/ByteCode/Compiler.cpp
@@ -5920,6 +5920,9 @@ template <class Emitter> bool Compiler<Emitter>::visitStmt(const Stmt *S) {
return this->emitInvalid(S);
case Stmt::LabelStmtClass:
return this->visitStmt(cast<LabelStmt>(S)->getSubStmt());
+ case Stmt::CXXExpansionStmtInstantiationClass:
+ return this->visitCXXExpansionStmtInstantiation(
+ cast<CXXExpansionStmtInstantiation>(S));
default: {
if (const auto *E = dyn_cast<Expr>(S))
return this->discard(E);
@@ -6004,6 +6007,13 @@ bool Compiler<Emitter>::visitDeclStmt(const DeclStmt *DS,
FunctionDecl, NamespaceAliasDecl, UsingDirectiveDecl>(D))
continue;
+ if (const auto *ESD = dyn_cast<CXXExpansionStmtDecl>(D)) {
+ assert(ESD->getInstantiations() && "not expanded?");
+ if (!this->visitStmt(ESD->getInstantiations()))
+ return false;
+ continue;
+ }
+
const auto *VD = dyn_cast<VarDecl>(D);
if (!VD)
return false;
@@ -6590,6 +6600,38 @@ bool Compiler<Emitter>::visitCXXTryStmt(const CXXTryStmt *S) {
return this->visitStmt(S->getTryBlock());
}
+/// template for (auto x : {1, 2}) {}
+///
+/// This is not a loop from an AST perspective at all since it has already
+/// been instantiated to a list of compound statements.
+///
+/// Since we can have control flow in those compound statements, we need to
+/// handle it mostly like a loop though.
+template <class Emitter>
+bool Compiler<Emitter>::visitCXXExpansionStmtInstantiation(
+ const CXXExpansionStmtInstantiation *S) {
+ LocalScope<Emitter> WholeLoopScope(this, ScopeKind::Block);
+
+ for (const Stmt *PreambleStmt : S->getPreambleStmts()) {
+ if (!this->visitDeclStmt(cast<DeclStmt>(PreambleStmt), true))
+ return false;
+ }
+
+ LabelTy EndLabel = this->getLabel();
+ for (const Stmt *Instantiation : S->getInstantiations()) {
+ LabelTy ContinueLabel = this->getLabel();
+ LoopScope<Emitter> LS(this, S, EndLabel, ContinueLabel);
+
+ if (!this->visitStmt(Instantiation))
+ return false;
+ this->emitLabel(ContinueLabel);
+ }
+
+ this->emitLabel(EndLabel);
+
+ return WholeLoopScope.destroyLocals();
+}
+
template <class Emitter>
bool Compiler<Emitter>::emitLambdaStaticInvokerBody(const CXXMethodDecl *MD) {
assert(MD->isLambdaStaticInvoker());
diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h
index cd14f72b87f9f..551b07f9d8db0 100644
--- a/clang/lib/AST/ByteCode/Compiler.h
+++ b/clang/lib/AST/ByteCode/Compiler.h
@@ -249,7 +249,8 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
bool visitDefaultStmt(const DefaultStmt *S);
bool visitAttributedStmt(const AttributedStmt *S);
bool visitCXXTryStmt(const CXXTryStmt *S);
-
+ bool
+ visitCXXExpansionStmtInstantiation(const CXXExpansionStmtInstantiation *S);
protected:
bool visitStmt(const Stmt *S);
bool visitExpr(const Expr *E, bool DestroyToplevelScope) override;
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index a464ed90f70b8..f9030cbd77515 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -5968,6 +5968,12 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
const VarDecl *VD = dyn_cast_or_null<VarDecl>(D);
if (VD && !CheckLocalVariableDeclaration(Info, VD))
return ESR_Failed;
+
+ if (const auto *ESD = dyn_cast<CXXExpansionStmtDecl>(D)) {
+ assert(ESD->getInstantiations() && "not expanded?");
+ return EvaluateStmt(Result, Info, ESD->getInstantiations(), Case);
+ }
+
// Each declaration initialization is its own full-expression.
FullExpressionRAII Scope(Info);
if (!EvaluateDecl(Info, D, /*EvaluateConditionDecl=*/true) &&
@@ -6240,6 +6246,40 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
return Scope.destroy() ? ESR_Succeeded : ESR_Failed;
}
+ case Stmt::CXXExpansionStmtInstantiationClass: {
+ BlockScopeRAII Scope(Info);
+ const auto *Expansion = cast<CXXExpansionStmtInstantiation>(S);
+ for (const Stmt *PreambleStmt : Expansion->getPreambleStmts()) {
+ EvalStmtResult ESR = EvaluateStmt(Result, Info, PreambleStmt);
+ if (ESR != ESR_Succeeded) {
+ if (ESR != ESR_Failed && !Scope.destroy())
+ return ESR_Failed;
+ return ESR;
+ }
+ }
+
+ // No need to push an extra scope for these since they're already
+ // CompoundStmts.
+ EvalStmtResult ESR = ESR_Succeeded;
+ for (const Stmt *Instantiation : Expansion->getInstantiations()) {
+ ESR = EvaluateStmt(Result, Info, Instantiation);
+ if (ESR == ESR_Failed ||
+ ShouldPropagateBreakContinue(Info, Expansion, &Scope, ESR))
+ return ESR;
+ if (ESR != ESR_Continue) {
+ // Succeeded here actually means we encountered a 'break'.
+ assert(ESR == ESR_Succeeded || ESR == ESR_Returned);
+ break;
+ }
+ }
+
+ // Map Continue back to Succeeded if we fell off the end of the loop.
+ if (ESR == ESR_Continue)
+ ESR = ESR_Succeeded;
+
+ return Scope.destroy() ? ESR : ESR_Failed;
+ }
+
case Stmt::SwitchStmtClass:
return EvaluateSwitch(Result, Info, cast<SwitchStmt>(S));
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 047aa55dc4b4a..de6b751c155ff 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -143,8 +143,12 @@ void CodeGenFunction::EmitDecl(const Decl &D, bool EvaluateConditionDecl) {
// None of these decls require codegen support.
return;
- case Decl::CXXExpansionStmt:
- llvm_unreachable("TODO");
+ case Decl::CXXExpansionStmt: {
+ const auto *ESD = cast<CXXExpansionStmtDecl>(&D);
+ assert(ESD->getInstantiations() && "expansion statement not expanded?");
+ EmitStmt(ESD->getInstantiations());
+ return;
+ }
case Decl::NamespaceAlias:
if (CGDebugInfo *DI = getDebugInfo())
diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp
index 8d9eb67cc2cef..9e0ea7d5eb880 100644
--- a/clang/lib/CodeGen/CGStmt.cpp
+++ b/clang/lib/CodeGen/CGStmt.cpp
@@ -206,7 +206,8 @@ void CodeGenFunction::EmitStmt(const Stmt *S, ArrayRef<const Attr *> Attrs) {
case Stmt::CXXExpansionStmtPatternClass:
llvm_unreachable("unexpanded expansion statements should not be emitted");
case Stmt::CXXExpansionStmtInstantiationClass:
- llvm_unreachable("Todo");
+ EmitCXXExpansionStmtInstantiation(cast<CXXExpansionStmtInstantiation>(*S));
+ break;
case Stmt::SEHTryStmtClass:
EmitSEHTryStmt(cast<SEHTryStmt>(*S));
break;
@@ -1511,6 +1512,32 @@ CodeGenFunction::EmitCXXForRangeStmt(const CXXForRangeStmt &S,
}
}
+void CodeGenFunction::EmitCXXExpansionStmtInstantiation(
+ const CXXExpansionStmtInstantiation &S) {
+ LexicalScope Scope(*this, S.getSourceRange());
+
+ for (const Stmt *DS : S.getPreambleStmts())
+ EmitStmt(DS);
+
+ if (S.getInstantiations().empty())
+ return;
+
+ JumpDest ExpandExit = getJumpDestInCurrentScope("expand.end");
+ JumpDest ContinueDest;
+ for (auto [N, Inst] : enumerate(S.getInstantiations())) {
+ if (N == S.getInstantiations().size() - 1)
+ ContinueDest = ExpandExit;
+ else
+ ContinueDest = getJumpDestInCurrentScope("expand.next");
+
+ LexicalScope ExpansionScope(*this, Inst->getSourceRange());
+ BreakContinueStack.push_back(BreakContinue(S, ExpandExit, ContinueDest));
+ EmitStmt(Inst);
+ BreakContinueStack.pop_back();
+ EmitBlock(ContinueDest.getBlock(), true);
+ }
+}
+
void CodeGenFunction::EmitReturnOfRValue(RValue RV, QualType Ty) {
if (RV.isScalar()) {
Builder.CreateStore(RV.getScalarVal(), ReturnValue);
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 0ff93d2ce7363..b045edb83b568 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -3734,6 +3734,9 @@ class CodeGenFunction : public CodeGenTypeCache {
void EmitCXXForRangeStmt(const CXXForRangeStmt &S,
ArrayRef<const Attr *> Attrs = {});
+ void
+ EmitCXXExpansionStmtInstantiation(const CXXExpansionStmtInstantiation &S);
+
/// Controls insertion of cancellation exit blocks in worksharing constructs.
class OMPCancelStackRAII {
CodeGenFunction &CGF;
diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp
index e5eaab0da7adb..786aeba183ff6 100644
--- a/clang/lib/Frontend/FrontendActions.cpp
+++ b/clang/lib/Frontend/FrontendActions.cpp
@@ -478,6 +478,8 @@ class DefaultTemplateInstCallback : public TemplateInstantiationCallback {
return "SYCLKernelLaunchLookup";
case CodeSynthesisContext::SYCLKernelLaunchOverloadResolution:
return "SYCLKernelLaunchOverloadResolution";
+ case CodeSynthesisContext::ExpansionStmtInstantiation:
+ return "ExpansionStmtInstantiation";
}
return "";
}
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index d448cb8a552bc..0ab6f10ed6ca6 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -33,6 +33,7 @@
#include "clang/Sema/SemaOpenMP.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/StringSwitch.h"
+#include "llvm/ADT/ScopeExit.h"
#include <optional>
using namespace clang;
@@ -2303,43 +2304,39 @@ Parser::DeclGroupPtrTy Parser::ParseDeclGroup(ParsingDeclSpec &DS,
// Handle the Objective-C for-in loop variable similarly, although we
// don't need to parse the container in advance.
if (FRI && (Tok.is(tok::colon) || isTokIdentifier_in())) {
- bool IsForRangeLoop = false;
+ // If we're parsing an expansion statement, enter its context to ensure
+ // the declaration ends up in a dependent context. We previously left the
+ // context of the expansion statement because init-statements should *not*
+ // be in a dependent context, and only once we get here do we know that this
+ // is not an init-statement but rather a for-range-declaration.
+ //
+ // Note that this is *not* the case if we get here via ParseCXXCondition()
+ // since if that is called when we parse an expansion statement, we know for
+ // sure that it has to be the for-range-declaration.
+ //
+ // We can't use ContextRAII here since that pushes a new delayed diagnostics
+ // pool, which causes issues during declaration parsing.
+ bool EnterContext = FRI->ExpansionStmt &&
+ Actions.CurContext == FRI->ExpansionStmt->getParent();
+ if (EnterContext)
+ Actions.PushDeclContext(nullptr, FRI->ExpansionStmt);
+ llvm::scope_exit RestoreDeclContext{[&] {
+ if (EnterContext)
+ Actions.PopDeclContext();
+ }};
+
+ bool IsForRangeLoopOrExpansionStmt = false;
if (TryConsumeToken(tok::colon, FRI->ColonLoc)) {
- IsForRangeLoop = true;
- EnterExpressionEvaluationContext ForRangeInitContext(
- Actions, Sema::ExpressionEvaluationContext::PotentiallyEvaluated,
- /*LambdaContextDecl=*/nullptr,
- Sema::ExpressionEvaluationContextRecord::EK_Other,
- getLangOpts().CPlusPlus23);
-
- // P2718R0 - Lifetime extension in range-based for loops.
- if (getLangOpts().CPlusPlus23) {
- auto &LastRecord = Actions.currentEvaluationContext();
- LastRecord.InLifetimeExtendingContext = true;
- LastRecord.RebuildDefaultArgOrDefaultInit = true;
- }
-
- if (getLangOpts().OpenMP)
+ IsForRangeLoopOrExpansionStmt = true;
+ if (getLangOpts().OpenMP && !FRI->ExpansionStmt)
Actions.OpenMP().startOpenMPCXXRangeFor();
- if (Tok.is(tok::l_brace))
- FRI->RangeExpr = ParseBraceInitializer();
- else
- FRI->RangeExpr = ParseExpression();
-
- // Before c++23, ForRangeLifetimeExtendTemps should be empty.
- assert(
- getLangOpts().CPlusPlus23 ||
- Actions.ExprEvalContexts.back().ForRangeLifetimeExtendTemps.empty());
- // Move the collected materialized temporaries into ForRangeInit before
- // ForRangeInitContext exit.
- FRI->LifetimeExtendTemps = std::move(
- Actions.ExprEvalContexts.back().ForRangeLifetimeExtendTemps);
+ ParseForRangeInitializerAfterColon(*FRI, &DS);
}
Decl *ThisDecl = Actions.ActOnDeclarator(getCurScope(), D);
- if (IsForRangeLoop) {
- Actions.ActOnCXXForRangeDecl(ThisDecl);
+ if (IsForRangeLoopOrExpansionStmt) {
+ Actions.ActOnCXXForRangeDecl(ThisDecl, FRI->ExpansionStmt);
} else {
// Obj-C for loop
if (auto *VD = dyn_cast_or_null<VarDecl>(ThisDecl))
diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp
index 11d06b73d27ea..cf593c11d6690 100644
--- a/clang/lib/Parse/ParseExpr.cpp
+++ b/clang/lib/Parse/ParseExpr.cpp
@@ -3200,7 +3200,8 @@ void Parser::injectEmbedTokens() {
bool Parser::ParseExpressionList(SmallVectorImpl<Expr *> &Exprs,
llvm::function_ref<void()> ExpressionStarts,
- bool FailImmediatelyOnInvalidExpr) {
+ bool FailImmediatelyOnInvalidExpr,
+ bool ParsingExpansionStmtInitList) {
bool SawError = false;
while (true) {
if (ExpressionStarts)
@@ -3229,7 +3230,11 @@ bool Parser::ParseExpressionList(SmallVectorImpl<Expr *> &Exprs,
SawError = true;
if (FailImmediatelyOnInvalidExpr)
break;
- SkipUntil(tok::comma, tok::r_paren, StopAtSemi | StopBeforeMatch);
+
+ // We expect '}' rather than ')' at the end of an expansion-init-list.
+ SkipUntil(tok::comma,
+ ParsingExpansionStmtInitList ? tok::r_brace : tok::r_paren,
+ StopAtSemi | StopBeforeMatch);
} else {
Exprs.push_back(Expr.get());
}
@@ -3239,6 +3244,11 @@ bool Parser::ParseExpressionList(SmallVectorImpl<Expr *> &Exprs,
// Move to the next argument, remember where the comma was.
Token Comma = Tok;
ConsumeToken();
+
+ // CWG 3061: Trailing commas are allowed in expansion-init-lists.
+ if (ParsingExpansionStmtInitList && Tok.is(tok::r_brace))
+ break;
+
checkPotentialAngleBracketDelimiter(Comma);
}
return SawError;
diff --git a/clang/lib/Parse/ParseInit.cpp b/clang/lib/Parse/ParseInit.cpp
index 0e86c4c48d5e4..40d78b5d3d2a6 100644
--- a/clang/lib/Parse/ParseInit.cpp
+++ b/clang/lib/Parse/ParseInit.cpp
@@ -516,6 +516,25 @@ ExprResult Parser::ParseBraceInitializer() {
return ExprError(); // an error occurred.
}
+ExprResult Parser::ParseExpansionInitList() {
+ BalancedDelimiterTracker T(*this, tok::l_brace);
+ T.consumeOpen();
+
+ ExprVector InitExprs;
+
+ if (!Tok.is(tok::r_brace) &&
+ ParseExpressionList(InitExprs, /*ExpressionStarts=*/{},
+ /*FailImmediatelyOnInvalidExpr=*/false,
+ /*ParsingExpansionStmtInitList=*/true)) {
+ T.consumeClose();
+ return ExprError();
+ }
+
+ T.consumeClose();
+ return Actions.ActOnCXXExpansionInitList(InitExprs, T.getOpenLocation(),
+ T.getCloseLocation());
+}
+
bool Parser::ParseMicrosoftIfExistsBraceInitializer(ExprVector &InitExprs,
bool &InitExprsOk) {
bool trailingComma = false;
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 1a45ed66950be..32cab6782f9e5 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -261,6 +261,22 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes(
}
case tok::kw_template: {
+ if (NextToken().is(tok::kw_for)) {
+ // Expansion statements are not backported for now.
+ if (!getLangOpts().CPlusPlus26) {
+ Diag(Tok.getLocation(), diag::err_expansion_stmt_requires_cxx2c);
+
+ // Trying to parse this as a regular 'for' statement instead yields
+ // better error recovery.
+ ConsumeToken();
+ return ParseForStatement(TrailingElseLoc, PrecedingLabel);
+ }
+
+ SourceLocation TemplateLoc = ConsumeToken();
+ return ParseExpansionStatement(TrailingElseLoc, PrecedingLabel,
+ TemplateLoc);
+ }
+
SourceLocation DeclEnd;
ParseTemplateDeclarationOrSpecialization(DeclaratorContext::Block, DeclEnd,
getAccessSpecifierIfPresent());
@@ -291,6 +307,19 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes(
SemiError = "do/while";
break;
case tok::kw_for: // C99 6.8.5.3: for-statement
+ // Correct 'for template' to 'template for'.
+ if (NextToken().is(tok::kw_template)) {
+ Diag(Tok.getLocation(), diag::err_for_template)
+ << FixItHint::CreateReplacement(
+ SourceRange(Tok.getLocation(), NextToken().getEndLoc()),
+ "template for");
+ Tok.setKind(tok::kw_template);
+ SourceLocation TemplateLoc = ConsumeToken();
+ Tok.setKind(tok::kw_for);
+ return ParseExpansionStatement(TrailingElseLoc, PrecedingLabel,
+ TemplateLoc);
+ }
+
return ParseForStatement(TrailingElseLoc, PrecedingLabel);
case tok::kw_goto: // C99 6.8.6.1: goto-statement
@@ -709,8 +738,9 @@ StmtResult Parser::ParseLabeledStatement(ParsedAttributes &Attrs,
// identifier ':' statement
SourceLocation ColonLoc = ConsumeToken();
- LabelDecl *LD = Actions.LookupOrCreateLabel(IdentTok.getIdentifierInfo(),
- IdentTok.getLocation());
+ LabelDecl *LD = Actions.LookupOrCreateLabel(
+ IdentTok.getIdentifierInfo(), IdentTok.getLocation(), /*GnuLabelLoc=*/{},
+ /*IsLabelStmt=*/true);
// Read label attributes, if present.
StmtResult SubStmt;
@@ -754,6 +784,13 @@ StmtResult Parser::ParseLabeledStatement(ParsedAttributes &Attrs,
DiagnoseLabelFollowedByDecl(*this, SubStmt.get());
+ // If a label cannot appear here, just return the underlying statement. We
+ // already diagnosed this as invalid in LookupOrCreateLabel() above.
+ if (!LD) {
+ Attrs.clear();
+ return SubStmt.get();
+ }
+
Actions.ProcessDeclAttributeList(Actions.CurScope, LD, Attrs);
Attrs.clear();
@@ -1895,8 +1932,56 @@ bool Parser::isForRangeIdentifier() {
return false;
}
-StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
- LabelDecl *PrecedingLabel) {
+void Parser::ParseForRangeInitializerAfterColon(ForRangeInit &FRI,
+ ParsingDeclSpec *VarDeclSpec) {
+ // Use an immediate function context if this is the initializer for a
+ // constexpr variable in an expansion statement.
+ auto Ctx = Sema::ExpressionEvaluationContext::PotentiallyEvaluated;
+ if (FRI.ExpansionStmt && VarDeclSpec && VarDeclSpec->hasConstexprSpecifier())
+ Ctx = Sema::ExpressionEvaluationContext::ImmediateFunctionContext;
+
+ EnterExpressionEvaluationContext InitContext(
+ Actions, Ctx,
+ /*LambdaContextDecl=*/nullptr,
+ Sema::ExpressionEvaluationContextRecord::EK_Other,
+ getLangOpts().CPlusPlus23);
+
+ // P2718R0 - Lifetime extension in range-based for loops.
+ if (getLangOpts().CPlusPlus23) {
+ auto &LastRecord = Actions.currentEvaluationContext();
+ LastRecord.InLifetimeExtendingContext = true;
+ LastRecord.RebuildDefaultArgOrDefaultInit = true;
+ }
+
+ if (FRI.ExpansionStmt) {
+ // The expansion-initializer is not in a dependent context and should
+ // thus be parsed in the parent context of the expansion statement.
+ assert(Actions.CurContext->isExpansionStmt());
+ Sema::ContextRAII CtxGuard(Actions, Actions.CurContext->getParent(),
+ /*NewThis=*/false);
+ FRI.RangeExpr =
+ Tok.is(tok::l_brace) ? ParseExpansionInitList() : ParseExpression();
+ FRI.RangeExpr = Actions.MaybeCreateExprWithCleanups(FRI.RangeExpr);
+ } else if (Tok.is(tok::l_brace)) {
+ FRI.RangeExpr = ParseBraceInitializer();
+ } else {
+ FRI.RangeExpr = ParseExpression();
+ }
+
+ // Before c++23, ForRangeLifetimeExtendTemps should be empty.
+ assert(getLangOpts().CPlusPlus23 ||
+ Actions.ExprEvalContexts.back().ForRangeLifetimeExtendTemps.empty());
+
+ // Move the collected materialized temporaries into ForRangeInit before
+ // ForRangeInitContext exit.
+ FRI.LifetimeExtendTemps =
+ std::move(Actions.ExprEvalContexts.back().ForRangeLifetimeExtendTemps);
+}
+
+StmtResult
+Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
+ LabelDecl *PrecedingLabel,
+ CXXExpansionStmtDecl *ESD) {
assert(Tok.is(tok::kw_for) && "Not a for stmt!");
SourceLocation ForLoc = ConsumeToken(); // eat the 'for'.
@@ -1931,8 +2016,14 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
unsigned ScopeFlags = 0;
if (C99orCXXorObjC)
ScopeFlags = Scope::DeclScope | Scope::ControlScope;
+ if (ESD)
+ ScopeFlags |= Scope::TemplateParamScope;
ParseScope ForScope(this, ScopeFlags);
+ if (ESD)
+ // FIXME: Combine this with the scope flags above once this is actually a
+ // proper scope flag.
+ getCurScope()->setIsExpansionStmtScope();
BalancedDelimiterTracker T(*this, tok::l_paren);
T.consumeOpen();
@@ -1945,7 +2036,21 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
ExprResult Collection;
ForRangeInfo ForRangeInfo;
FullExprArg ThirdPart(Actions);
+ ForRangeInfo.ExpansionStmt = ESD;
+ // RAII helper to enter a context if we're parsing an expansion statement.
+ //
+ // This is required because some parts of an expansion statement (e.g. the
+ // init-statement) are not in a dependent context and must thus be parsed in
+ // the parent context.
+ struct [[nodiscard]] ExpansionStmtContextRAII : Sema::ContextRAII {
+ ExpansionStmtContextRAII(Sema &S, struct ForRangeInfo &Info,
+ DeclContext *Ctx)
+ : ContextRAII(S, Info.ExpansionStmt ? Ctx : S.CurContext,
+ /*NewThis=*/false) {}
+ };
+
+ assert(!ESD || Actions.CurContext->isExpansionStmt());
if (Tok.is(tok::code_completion)) {
cutOffParsing();
Actions.CodeCompletion().CodeCompleteOrdinaryName(
@@ -1969,26 +2074,30 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
ConsumeToken();
} else if (getLangOpts().CPlusPlus && Tok.is(tok::identifier) &&
isForRangeIdentifier()) {
+ // Note: This path is solely for error recovery if a user omits the type-id
+ // and writes 'for (x : ...)'; normally, the for-range-declaration is parsed
+ // in the 'if (isForInitDeclaration())' branch below.
ProhibitAttributes(attrs);
IdentifierInfo *Name = Tok.getIdentifierInfo();
SourceLocation Loc = ConsumeToken();
MaybeParseCXX11Attributes(attrs);
ForRangeInfo.ColonLoc = ConsumeToken();
- if (Tok.is(tok::l_brace))
- ForRangeInfo.RangeExpr = ParseBraceInitializer();
- else
- ForRangeInfo.RangeExpr = ParseExpression();
+ ParseForRangeInitializerAfterColon(ForRangeInfo, /*VarDeclSpec=*/nullptr);
Diag(Loc, diag::err_for_range_identifier)
- << ((getLangOpts().CPlusPlus11 && !getLangOpts().CPlusPlus17)
- ? FixItHint::CreateInsertion(Loc, "auto &&")
- : FixItHint());
-
- ForRangeInfo.LoopVar =
- Actions.ActOnCXXForRangeIdentifier(getCurScope(), Loc, Name, attrs);
+ << (ForRangeInfo.ExpansionStmt != nullptr)
+ << ((getLangOpts().CPlusPlus11 && !getLangOpts().CPlusPlus17)
+ ? FixItHint::CreateInsertion(Loc, "auto &&")
+ : FixItHint());
+
+ if (!ForRangeInfo.ExpansionStmt)
+ ForRangeInfo.LoopVar =
+ Actions.ActOnCXXForRangeIdentifier(getCurScope(), Loc, Name, attrs);
} else if (isForInitDeclaration()) { // for (int X = 4;
ParenBraceBracketBalancer BalancerRAIIObj(*this);
+ ExpansionStmtContextRAII EnterParentContext{
+ Actions, ForRangeInfo, Actions.CurContext->getParent()};
// Parse declaration, which eats the ';'.
if (!C99orCXXorObjC) { // Use of C99-style for loops in C90 mode?
@@ -2041,6 +2150,9 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
}
}
} else {
+ // An expression here should not be inside the expansion statement context.
+ ExpansionStmtContextRAII EnterParentContext{
+ Actions, ForRangeInfo, Actions.CurContext->getParent()};
ProhibitAttributes(attrs);
Value = ParseExpression();
@@ -2078,7 +2190,8 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
// User tried to write the reasonable, but ill-formed, for-range-statement
// for (expr : expr) { ... }
Diag(Tok, diag::err_for_range_expected_decl)
- << FirstPart.get()->getSourceRange();
+ << (ESD != nullptr)
+ << FirstPart.get()->getSourceRange();
SkipUntil(tok::r_paren, StopBeforeMatch);
SecondPart = Sema::ConditionError();
} else {
@@ -2200,7 +2313,13 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
StmtResult ForRangeStmt;
StmtResult ForEachStmt;
- if (ForRangeInfo.ParsedForRangeDecl()) {
+ if (ESD) {
+ ForRangeStmt = Actions.ActOnCXXExpansionStmtPattern(
+ ESD, FirstPart.get(), ForRangeInfo.LoopVar.get(),
+ ForRangeInfo.RangeExpr.get(), T.getOpenLocation(),
+ ForRangeInfo.ColonLoc, T.getCloseLocation(),
+ ForRangeInfo.LifetimeExtendTemps);
+ } else if (ForRangeInfo.ParsedForRangeDecl()) {
ForRangeStmt = Actions.ActOnCXXForRangeStmt(
getCurScope(), ForLoc, CoawaitLoc, FirstPart.get(),
ForRangeInfo.LoopVar.get(), ForRangeInfo.ColonLoc,
@@ -2222,7 +2341,9 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
// OpenACC Restricts a for-loop inside of certain construct/clause
// combinations, so diagnose that here in OpenACC mode.
SemaOpenACC::LoopInConstructRAII LCR{getActions().OpenACC()};
- if (ForRangeInfo.ParsedForRangeDecl())
+ if (ESD)
+ ; // Nothing.
+ else if (ForRangeInfo.ParsedForRangeDecl())
getActions().OpenACC().ActOnRangeForStmtBegin(ForLoc, ForRangeStmt.get());
else
getActions().OpenACC().ActOnForStmtBegin(
@@ -2276,6 +2397,15 @@ StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
return Actions.ObjC().FinishObjCForCollectionStmt(ForEachStmt.get(),
Body.get());
+ if (ESD) {
+ if (!ForRangeInfo.ParsedForRangeDecl()) {
+ Diag(ForLoc, diag::err_expansion_stmt_requires_range);
+ return StmtError();
+ }
+
+ return Actions.FinishCXXExpansionStmt(ForRangeStmt.get(), Body.get());
+ }
+
if (ForRangeInfo.ParsedForRangeDecl())
return Actions.FinishCXXForRangeStmt(ForRangeStmt.get(), Body.get());
@@ -2657,6 +2787,37 @@ StmtResult Parser::ParseCXXCatchBlock(bool FnCatch) {
return Actions.ActOnCXXCatchBlock(CatchLoc, ExceptionDecl, Block.get());
}
+StmtResult Parser::ParseExpansionStatement(SourceLocation *TrailingElseLoc,
+ LabelDecl *PrecedingLabel,
+ SourceLocation TemplateLoc) {
+ assert(Tok.is(tok::kw_for));
+
+ CXXExpansionStmtDecl *ExpansionDecl =
+ Actions.ActOnCXXExpansionStmtDecl(TemplateParameterDepth, TemplateLoc);
+
+ CXXExpansionStmtPattern *Expansion;
+ {
+ Sema::ContextRAII CtxGuard(Actions, ExpansionDecl, /*NewThis=*/false);
+ TemplateParameterDepthRAII TParamDepthGuard(TemplateParameterDepth);
+ ++TParamDepthGuard;
+
+ StmtResult SR =
+ ParseForStatement(TrailingElseLoc, PrecedingLabel, ExpansionDecl);
+ if (SR.isInvalid())
+ return SR;
+
+ Expansion = cast<CXXExpansionStmtPattern>(SR.get());
+ ExpansionDecl->setExpansionPattern(Expansion);
+ }
+
+ DeclSpec DS(AttrFactory);
+ DeclGroupPtrTy DeclGroupPtr =
+ Actions.FinalizeDeclaratorGroup(getCurScope(), DS, {ExpansionDecl});
+
+ return Actions.ActOnDeclStmt(DeclGroupPtr, Expansion->getBeginLoc(),
+ Expansion->getEndLoc());
+}
+
void Parser::ParseMicrosoftIfExistsStatement(StmtVector &Stmts) {
IfExistsCondition Result;
if (ParseMicrosoftIfExistsCondition(Result))
diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt
index 0ebf56ecffe69..ef729e22c1dc8 100644
--- a/clang/lib/Sema/CMakeLists.txt
+++ b/clang/lib/Sema/CMakeLists.txt
@@ -53,6 +53,7 @@ add_clang_library(clangSema
SemaDeclCXX.cpp
SemaDeclObjC.cpp
SemaExceptionSpec.cpp
+ SemaExpand.cpp
SemaExpr.cpp
SemaExprCXX.cpp
SemaExprMember.cpp
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 16a34475aac1a..3e22afe0da59d 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -1384,7 +1384,8 @@ void Sema::PushDeclContext(Scope *S, DeclContext *DC) {
assert(DC->getLexicalParent() == CurContext &&
"The next DeclContext should be lexically contained in the current one.");
CurContext = DC;
- S->setEntity(DC);
+ if (S)
+ S->setEntity(DC);
}
void Sema::PopDeclContext() {
@@ -14778,14 +14779,15 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) {
}
}
-void Sema::ActOnCXXForRangeDecl(Decl *D) {
+void Sema::ActOnCXXForRangeDecl(Decl *D, bool InExpansionStmt) {
// If there is no declaration, there was an error parsing it. Ignore it.
if (!D)
return;
VarDecl *VD = dyn_cast<VarDecl>(D);
if (!VD) {
- Diag(D->getLocation(), diag::err_for_range_decl_must_be_var);
+ Diag(D->getLocation(), diag::err_for_range_decl_must_be_var)
+ << InExpansionStmt;
D->setInvalidDecl();
return;
}
@@ -14827,7 +14829,7 @@ void Sema::ActOnCXXForRangeDecl(Decl *D) {
if (Error != -1) {
Diag(VD->getOuterLocStart(), diag::err_for_range_storage_class)
- << VD << Error;
+ << InExpansionStmt << VD << Error;
D->setInvalidDecl();
}
}
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index b3a02a3fd5d02..deb6342e94ef4 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -2027,6 +2027,9 @@ static bool CheckConstexprDeclStmt(Sema &SemaRef, const FunctionDecl *Dcl,
// - using-enum-declaration
continue;
+ case Decl::CXXExpansionStmt:
+ continue;
+
case Decl::Typedef:
case Decl::TypeAlias: {
// - typedef declarations and alias-declarations that do not define
diff --git a/clang/lib/Sema/SemaExpand.cpp b/clang/lib/Sema/SemaExpand.cpp
new file mode 100644
index 0000000000000..8647276149d9a
--- /dev/null
+++ b/clang/lib/Sema/SemaExpand.cpp
@@ -0,0 +1,641 @@
+//===-- SemaExpand.cpp - Semantic Analysis for Expansion Statements--------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements semantic analysis for C++26 expansion statements,
+// aka 'template for'.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/StmtCXX.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Sema/EnterExpressionEvaluationContext.h"
+#include "clang/Sema/Lookup.h"
+#include "clang/Sema/Overload.h"
+#include "clang/Sema/Sema.h"
+#include "clang/Sema/Template.h"
+#include "llvm/ADT/ScopeExit.h"
+
+using namespace clang;
+
+namespace {
+struct IterableExpansionStmtData {
+ /// When we try to determine whether an expansion statement is iterating, the
+ /// result can be
+ ///
+ /// 1. it is not iterating (it could still be destructuring; we don't
+ /// know that yet);
+ ///
+ /// 2. there was a hard error (e.g. we determined that it is iterating,
+ /// but begin() is deleted);
+ ///
+ /// 3. it is iterating, and there was no error.
+ enum class IsIterableResult {
+ NotIterable, ///< Not iterable, but also not invalid.
+ Error, ///< Ill-formed.
+ Iterable, ///< Iterable (and there was no error).
+ };
+
+ DeclStmt *RangeDecl = nullptr;
+ DeclStmt *BeginDecl = nullptr;
+ DeclStmt *IterDecl = nullptr;
+ IsIterableResult TheState = IsIterableResult::NotIterable;
+
+ bool isIterable() const { return TheState == IsIterableResult::Iterable; }
+ bool hasError() { return TheState == IsIterableResult::Error; }
+};
+} // namespace
+
+// Build a 'DeclRefExpr' designating the template parameter that is used as
+// the expansion index
+static DeclRefExpr *BuildIndexDRE(Sema &S, CXXExpansionStmtDecl *ESD) {
+ return S.BuildDeclRefExpr(ESD->getIndexTemplateParm(),
+ S.Context.getPointerDiffType(), VK_PRValue,
+ ESD->getBeginLoc());
+}
+
+static bool FinalizeExpansionVar(Sema &S, VarDecl *ExpansionVar,
+ ExprResult Initializer) {
+ if (Initializer.isInvalid()) {
+ S.ActOnInitializerError(ExpansionVar);
+ return true;
+ }
+
+ S.AddInitializerToDecl(ExpansionVar, Initializer.get(), /*DirectInit=*/false);
+ return ExpansionVar->isInvalidDecl();
+}
+
+static auto InitListContainsPack(const InitListExpr *ILE) {
+ return llvm::any_of(ILE->inits(),
+ [](const Expr *E) { return isa<PackExpansionExpr>(E); });
+}
+
+static bool HasDependentSize(const DeclContext *CurContext,
+ const CXXExpansionStmtPattern *Pattern) {
+ switch (Pattern->getKind()) {
+ case CXXExpansionStmtPattern::ExpansionStmtKind::Enumerating: {
+ auto *SelectExpr = cast<CXXExpansionSelectExpr>(
+ Pattern->getExpansionVariable()->getInit());
+ return InitListContainsPack(SelectExpr->getRangeExpr());
+ }
+
+ case CXXExpansionStmtPattern::ExpansionStmtKind::Iterating:
+ // Even if the size isn't technically dependent, delay expansion until
+ // we're no longer in a template since evaluating a lambda declared in
+ // a template doesn't work too well.
+ assert(CurContext->isExpansionStmt());
+ return CurContext->getParent()->isDependentContext();
+
+ case CXXExpansionStmtPattern::ExpansionStmtKind::Dependent:
+ return true;
+
+ case CXXExpansionStmtPattern::ExpansionStmtKind::Destructuring:
+ return false;
+ }
+
+ llvm_unreachable("invalid pattern kind");
+}
+
+static IterableExpansionStmtData TryBuildIterableExpansionStmtInitializer(
+ Sema &S, Expr *ExpansionInitializer, Expr *Index, SourceLocation ColonLoc,
+ bool VarIsConstexpr,
+ ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps) {
+ IterableExpansionStmtData Data;
+
+ // C++26 [stmt.expand]p3: An expression is expansion-iterable if it does not
+ // have array type [...]
+ QualType Ty = ExpansionInitializer->getType().getNonReferenceType();
+ if (Ty->isArrayType())
+ return Data;
+
+ // Lookup member and ADL 'begin()'/'end()'. Only check if they exist; even if
+ // they're deleted, inaccessible, etc., this is still an iterating expansion
+ // statement, albeit an ill-formed one.
+ DeclarationNameInfo BeginName(&S.PP.getIdentifierTable().get("begin"),
+ ColonLoc);
+ DeclarationNameInfo EndName(&S.PP.getIdentifierTable().get("end"), ColonLoc);
+
+ bool FoundBeginEnd = false;
+ if (auto *Record = Ty->getAsCXXRecordDecl()) {
+ LookupResult BeginLR(S, BeginName, Sema::LookupMemberName);
+ LookupResult EndLR(S, EndName, Sema::LookupMemberName);
+ FoundBeginEnd = S.LookupQualifiedName(BeginLR, Record) &&
+ S.LookupQualifiedName(EndLR, Record);
+ }
+
+ // If member lookup doesn't yield anything, try ADL.
+ //
+ // If overload resolution for 'begin()' *and* 'end()' succeeds (irrespective
+ // of whether it results in a usable candidate), then assume this is an
+ // iterating expansion statement.
+ auto HasADLCandidate = [&](DeclarationName Name) {
+ OverloadCandidateSet Candidates(ColonLoc, OverloadCandidateSet::CSK_Normal);
+ OverloadCandidateSet::iterator Best;
+
+ S.AddArgumentDependentLookupCandidates(Name, ColonLoc, ExpansionInitializer,
+ /*ExplicitTemplateArgs=*/nullptr,
+ Candidates);
+
+ return Candidates.BestViableFunction(S, ColonLoc, Best) !=
+ OR_No_Viable_Function;
+ };
+
+ if (!FoundBeginEnd && (!HasADLCandidate(BeginName.getName()) ||
+ !HasADLCandidate(EndName.getName())))
+ return Data;
+
+ auto Ctx = Sema::ExpressionEvaluationContext::PotentiallyEvaluated;
+ if (VarIsConstexpr)
+ Ctx = Sema::ExpressionEvaluationContext::ImmediateFunctionContext;
+ EnterExpressionEvaluationContext ExprEvalCtx(S, Ctx);
+
+ // The declarations should be attached to the parent decl context.
+ Sema::ContextRAII CtxGuard(S, S.CurContext->getParent(),
+ /*NewThis=*/false);
+
+ // We know that this is supposed to be an iterable expansion statement;
+ // delegate to the for-range code to build the range/begin/end variables.
+ //
+ // Any failure at this point is a hard error.
+ Data.TheState = IterableExpansionStmtData::IsIterableResult::Error;
+ Scope *Scope = S.getCurScope();
+
+ // By [stmt.expand]p5.2 (CWG3131), the declaration of 'range' is of the form
+ //
+ // constexpr[opt] decltype(auto) range = (expansion-initializer);
+ //
+ // where 'constexpr' is present iff the for-range-declaration is 'constexpr'.
+ StmtResult Var = S.BuildCXXForRangeRangeVar(
+ Scope, S.ActOnParenExpr(ColonLoc, ColonLoc, ExpansionInitializer).get(),
+ S.Context.getAutoType(DeducedKind::Undeduced, QualType(),
+ AutoTypeKeyword::DecltypeAuto),
+ VarIsConstexpr);
+ if (Var.isInvalid())
+ return Data;
+
+ // [stmt.expand]p5.2 (CWG3140): The keyword 'constexpr' is present in the
+ // declarations of 'range', 'begin', and 'iter' if and only if 'constexpr' is
+ // one of the decl-specifiers of the decl-specifier-seq of the
+ // for-range-declaration.
+ //
+ // FIXME: As of CWG3140, we should only create 'begin' here, and not 'end',
+ // but that requires another substantial refactor of the for-range code.
+ auto *RangeVar = cast<DeclStmt>(Var.get());
+ Sema::ForRangeBeginEndInfo Info = S.BuildCXXForRangeBeginEndVars(
+ Scope, cast<VarDecl>(RangeVar->getSingleDecl()), ColonLoc,
+ /*CoawaitLoc=*/{},
+ /*LifetimeExtendTemps=*/{}, Sema::BFRK_Build, VarIsConstexpr);
+
+ if (!Info.isValid())
+ return Data;
+
+ // At runtime, we only need to evaluate 'begin', whereas 'end' is only used at
+ // compile-time; we'll rebuild the latter when we compute the expansion size,
+ // so only build a DeclStmt for 'begin' here.
+ StmtResult BeginStmt = S.ActOnDeclStmt(
+ S.ConvertDeclToDeclGroup(Info.BeginVar), ColonLoc, ColonLoc);
+ if (BeginStmt.isInvalid())
+ return Data;
+
+ // TODO: Build 'constexpr auto iter = begin + decltype(begin - begin){i};'.
+ S.Diag(ColonLoc, diag::err_iterating_expansion_stmt_unsupported);
+ return Data;
+
+#if 0 // This will be used once we support iterating expansion statements.
+ // Store it in a variable.
+ // See also Sema::BuildCXXForRangeBeginEndVars().
+ const auto DepthStr = std::to_string(Scope->getDepth() / 2);
+ IdentifierInfo *Name =
+ S.PP.getIdentifierInfo(std::string("__iter") + DepthStr);
+ VarDecl *IterVar = S.BuildForRangeVarDecl(
+ ColonLoc, S.Context.getAutoDeductType(), Name, VarIsConstexpr);
+ S.AddInitializerToDecl(IterVar, BeginPlusI.get(), /*DirectInit=*/false);
+ if (IterVar->isInvalidDecl())
+ return Data;
+
+ StmtResult IterVarStmt =
+ S.ActOnDeclStmt(S.ConvertDeclToDeclGroup(IterVar), ColonLoc, ColonLoc);
+ if (IterVarStmt.isInvalid())
+ return Data;
+
+ // CWG 3149: Apply lifetime extension to iterating expansion statements.
+ S.ApplyForRangeOrExpansionStatementLifetimeExtension(
+ cast<VarDecl>(RangeVar->getSingleDecl()), LifetimeExtendTemps);
+
+ Data.BeginDecl = BeginStmt.getAs<DeclStmt>();
+ Data.RangeDecl = RangeVar;
+ Data.IterDecl = IterVarStmt.getAs<DeclStmt>();
+ Data.TheState = IterableExpansionStmtData::IsIterableResult::Iterable;
+ return Data;
+#endif
+}
+
+static StmtResult BuildDestructuringDecompositionDecl(
+ Sema &S, Expr *ExpansionInitializer, SourceLocation ColonLoc,
+ bool VarIsConstexpr,
+ ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps) {
+ auto Ctx = Sema::ExpressionEvaluationContext::PotentiallyEvaluated;
+ if (VarIsConstexpr)
+ Ctx = Sema::ExpressionEvaluationContext::ImmediateFunctionContext;
+ EnterExpressionEvaluationContext ExprEvalCtx(S, Ctx);
+
+ // The declarations should be attached to the parent decl context.
+ Sema::ContextRAII CtxGuard(S, S.CurContext->getParent(),
+ /*NewThis=*/false);
+
+ UnsignedOrNone Arity =
+ S.GetDecompositionElementCount(ExpansionInitializer->getType(), ColonLoc);
+
+ if (!Arity)
+ return StmtError();
+
+ QualType AutoRRef = S.Context.getAutoRRefDeductType();
+ SmallVector<BindingDecl *> Bindings;
+ for (unsigned I = 0; I < *Arity; ++I)
+ Bindings.push_back(BindingDecl::Create(
+ S.Context, S.CurContext, ColonLoc,
+ S.getPreprocessor().getIdentifierInfo("__u" + std::to_string(I)),
+ AutoRRef));
+
+ TypeSourceInfo *TSI = S.Context.getTrivialTypeSourceInfo(AutoRRef);
+ auto *DD =
+ DecompositionDecl::Create(S.Context, S.CurContext, ColonLoc, ColonLoc,
+ AutoRRef, TSI, SC_Auto, Bindings);
+
+ if (VarIsConstexpr)
+ DD->setConstexpr(true);
+
+ S.ApplyForRangeOrExpansionStatementLifetimeExtension(DD, LifetimeExtendTemps);
+ S.AddInitializerToDecl(DD, ExpansionInitializer, false);
+ return S.ActOnDeclStmt(S.ConvertDeclToDeclGroup(DD), ColonLoc, ColonLoc);
+}
+
+CXXExpansionStmtDecl *
+Sema::ActOnCXXExpansionStmtDecl(unsigned TemplateDepth,
+ SourceLocation TemplateKWLoc) {
+ // Create a template parameter. This will be used to denote the index
+ // of the element that we're instantiating. This type must be 'ptrdiff_t'
+ // for iterating expansion statements ([stmt.expand]p5.2), so use that in
+ // all cases.
+ QualType ParmTy = Context.getPointerDiffType();
+ TypeSourceInfo *ParmTI =
+ Context.getTrivialTypeSourceInfo(ParmTy, TemplateKWLoc);
+
+ auto *TParam = NonTypeTemplateParmDecl::Create(
+ Context, Context.getTranslationUnitDecl(), TemplateKWLoc, TemplateKWLoc,
+ TemplateDepth, /*Position=*/0, /*Id=*/nullptr, ParmTy, /*ParameterPack=*/false,
+ ParmTI);
+
+ return BuildCXXExpansionStmtDecl(CurContext, TemplateKWLoc, TParam);
+}
+
+CXXExpansionStmtDecl *
+Sema::BuildCXXExpansionStmtDecl(DeclContext *Ctx, SourceLocation TemplateKWLoc,
+ NonTypeTemplateParmDecl *NTTP) {
+ auto *Result =
+ CXXExpansionStmtDecl::Create(Context, Ctx, TemplateKWLoc, NTTP);
+ Ctx->addDecl(Result);
+ return Result;
+}
+
+ExprResult Sema::ActOnCXXExpansionInitList(MultiExprArg SubExprs,
+ SourceLocation LBraceLoc,
+ SourceLocation RBraceLoc) {
+ return new (Context) InitListExpr(Context, LBraceLoc, SubExprs, RBraceLoc);
+}
+
+StmtResult Sema::ActOnCXXExpansionStmtPattern(
+ CXXExpansionStmtDecl *ESD, Stmt *Init, Stmt *ExpansionVarStmt,
+ Expr *ExpansionInitializer, SourceLocation LParenLoc,
+ SourceLocation ColonLoc, SourceLocation RParenLoc,
+ ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps) {
+ if (!ExpansionInitializer || ExpansionInitializer->containsErrors() ||
+ !ExpansionVarStmt)
+ return StmtError();
+
+ assert(CurContext->isExpansionStmt());
+ auto *DS = cast<DeclStmt>(ExpansionVarStmt);
+ if (!DS->isSingleDecl()) {
+ Diag(DS->getBeginLoc(), diag::err_type_defined_in_for_range);
+ return StmtError();
+ }
+
+ VarDecl *ExpansionVar = dyn_cast<VarDecl>(DS->getSingleDecl());
+ if (!ExpansionVar || ExpansionVar->isInvalidDecl())
+ return StmtError();
+
+ // This is an enumerating expansion statement.
+ if (auto *ILE = dyn_cast<InitListExpr>(ExpansionInitializer)) {
+ assert(ILE->isSyntacticForm());
+ ExprResult Initializer =
+ BuildCXXExpansionSelectExpr(ILE, BuildIndexDRE(*this, ESD));
+ if (FinalizeExpansionVar(*this, ExpansionVar, Initializer))
+ return StmtError();
+
+ // TODO: CWG3043 (lifetime extension in enumerating expansion statements).
+ return BuildCXXEnumeratingExpansionStmtPattern(ESD, Init, DS, LParenLoc,
+ ColonLoc, RParenLoc);
+ }
+
+ if (ExpansionInitializer->hasPlaceholderType()) {
+ ExprResult R = CheckPlaceholderExpr(ExpansionInitializer);
+ if (R.isInvalid())
+ return StmtError();
+ ExpansionInitializer = R.get();
+ }
+
+ if (DiagnoseUnexpandedParameterPack(ExpansionInitializer))
+ return StmtError();
+
+ return BuildNonEnumeratingCXXExpansionStmtPattern(
+ ESD, Init, DS, ExpansionInitializer, LParenLoc, ColonLoc, RParenLoc,
+ LifetimeExtendTemps);
+}
+
+StmtResult Sema::BuildCXXEnumeratingExpansionStmtPattern(
+ Decl *ESD, Stmt *Init, Stmt *ExpansionVar, SourceLocation LParenLoc,
+ SourceLocation ColonLoc, SourceLocation RParenLoc) {
+ return CXXExpansionStmtPattern::CreateEnumerating(
+ Context, cast<CXXExpansionStmtDecl>(ESD), Init,
+ cast<DeclStmt>(ExpansionVar), LParenLoc, ColonLoc, RParenLoc);
+}
+
+StmtResult Sema::BuildNonEnumeratingCXXExpansionStmtPattern(
+ CXXExpansionStmtDecl *ESD, Stmt *Init, DeclStmt *ExpansionVarStmt,
+ Expr *ExpansionInitializer, SourceLocation LParenLoc,
+ SourceLocation ColonLoc, SourceLocation RParenLoc,
+ ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps) {
+ VarDecl *ExpansionVar = cast<VarDecl>(ExpansionVarStmt->getSingleDecl());
+
+ // Reject lambdas early.
+ if (auto *RD = ExpansionInitializer->getType()->getAsCXXRecordDecl();
+ RD && RD->isLambda()) {
+ Diag(ExpansionInitializer->getBeginLoc(), diag::err_expansion_stmt_lambda);
+ return StmtError();
+ }
+
+ if (ExpansionInitializer->isTypeDependent()) {
+ ActOnDependentForRangeInitializer(ExpansionVar, BFRK_Build);
+ return CXXExpansionStmtPattern::CreateDependent(
+ Context, ESD, Init, ExpansionVarStmt, ExpansionInitializer, LParenLoc,
+ ColonLoc, RParenLoc);
+ }
+
+ if (RequireCompleteType(ExpansionInitializer->getExprLoc(),
+ ExpansionInitializer->getType(),
+ diag::err_expansion_stmt_incomplete))
+ return StmtError();
+
+ if (ExpansionInitializer->getType()->isVariableArrayType()) {
+ Diag(ExpansionInitializer->getExprLoc(), diag::err_expansion_stmt_vla)
+ << ExpansionInitializer->getType();
+ return StmtError();
+ }
+
+ // Otherwise, if it can be an iterating expansion statement, it is one.
+ DeclRefExpr *Index = BuildIndexDRE(*this, ESD);
+ IterableExpansionStmtData Data = TryBuildIterableExpansionStmtInitializer(
+ *this, ExpansionInitializer, Index, ColonLoc, ExpansionVar->isConstexpr(),
+ LifetimeExtendTemps);
+ if (Data.hasError()) {
+ ActOnInitializerError(ExpansionVar);
+ return StmtError();
+ }
+
+ if (Data.isIterable()) {
+ // Build '*iter'.
+ auto *IterVar = cast<VarDecl>(Data.IterDecl->getSingleDecl());
+ DeclRefExpr *IterDRE = BuildDeclRefExpr(
+ IterVar, IterVar->getType().getNonReferenceType(), VK_LValue, ColonLoc);
+ ExprResult Deref =
+ ActOnUnaryOp(getCurScope(), ColonLoc, tok::star, IterDRE);
+ if (Deref.isInvalid()) {
+ ActOnInitializerError(ExpansionVar);
+ return StmtError();
+ }
+
+ Deref = MaybeCreateExprWithCleanups(Deref.get());
+
+ if (FinalizeExpansionVar(*this, ExpansionVar, Deref.get()))
+ return StmtError();
+
+ return CXXExpansionStmtPattern::CreateIterating(
+ Context, ESD, Init, ExpansionVarStmt, Data.RangeDecl, Data.BeginDecl,
+ Data.IterDecl, LParenLoc, ColonLoc, RParenLoc);
+ }
+
+ // If not, try destructuring.
+ StmtResult DecompDeclStmt = BuildDestructuringDecompositionDecl(
+ *this, ExpansionInitializer, ColonLoc, ExpansionVar->isConstexpr(),
+ LifetimeExtendTemps);
+ if (DecompDeclStmt.isInvalid()) {
+ Diag(ExpansionInitializer->getBeginLoc(),
+ diag::err_expansion_stmt_invalid_init)
+ << ExpansionInitializer->getType()
+ << ExpansionInitializer->getSourceRange();
+
+ ActOnInitializerError(ExpansionVar);
+ return StmtError();
+ }
+
+ auto *DS = DecompDeclStmt.getAs<DeclStmt>();
+ auto *DD = cast<DecompositionDecl>(DS->getSingleDecl());
+ if (DD->isInvalidDecl())
+ return StmtError();
+
+ // Synthesise an InitListExpr to store the bindings; this essentially lets us
+ // desugar the expansion of a destructuring expansion statement to that of an
+ // enumerating expansion statement.
+ SmallVector<Expr *> Bindings;
+ Bindings.reserve(DD->bindings().size());
+ for (BindingDecl *BD : DD->bindings()) {
+ Expr *Element = BuildDeclRefExpr(BD, BD->getType().getNonReferenceType(),
+ VK_LValue, ColonLoc);
+
+ // [stmt.expand]p5.3 (CWG3149): If the expansion-initializer is an lvalue,
+ // then vi is ui; otherwise, vi is static_cast<decltype(ui)&&>(ui).
+ if (!ExpansionInitializer->isLValue()) {
+ QualType Ty =
+ BuildReferenceType(getDecltypeForExpr(Element), /*LValueRef=*/false,
+ ColonLoc, /*Entity=*/DeclarationName());
+ TypeSourceInfo *TSI = Context.getTrivialTypeSourceInfo(Ty);
+ ExprResult Cast = BuildCXXNamedCast(
+ ColonLoc, tok::kw_static_cast, TSI, Element,
+ SourceRange(ColonLoc, ColonLoc), SourceRange(ColonLoc, ColonLoc));
+ assert(!Cast.isInvalid() && "cast to rvalue reference type failed?");
+ Element = Cast.get();
+ }
+
+ Bindings.push_back(Element);
+ }
+
+ ExprResult Select = BuildCXXExpansionSelectExpr(
+ new (Context) InitListExpr(Context, ColonLoc, Bindings, ColonLoc),
+ Index);
+
+ if (Select.isInvalid()) {
+ ActOnInitializerError(ExpansionVar);
+ return StmtError();
+ }
+
+ if (FinalizeExpansionVar(*this, ExpansionVar, Select))
+ return StmtError();
+
+ return CXXExpansionStmtPattern::CreateDestructuring(
+ Context, ESD, Init, ExpansionVarStmt, DS, LParenLoc, ColonLoc, RParenLoc);
+}
+
+StmtResult Sema::FinishCXXExpansionStmt(Stmt *Exp, Stmt *Body) {
+ if (!Exp || !Body)
+ return StmtError();
+
+ auto *Expansion = cast<CXXExpansionStmtPattern>(Exp);
+ assert(!Expansion->getDecl()->getInstantiations() &&
+ "should not rebuild expansion statement after instantiation");
+
+ Expansion->setBody(Body);
+ if (HasDependentSize(CurContext, Expansion))
+ return Expansion;
+
+ // Now that we're expanding this, exit the context of the expansion stmt
+ // so that we no longer treat this as dependent.
+ ContextRAII CtxGuard(*this, CurContext->getParent(),
+ /*NewThis=*/false);
+
+ // This can fail if this is an iterating expansion statement.
+ std::optional<uint64_t> NumInstantiations = ComputeExpansionSize(Expansion);
+ if (!NumInstantiations)
+ return StmtError();
+
+ // Collect preamble statements.
+ //
+ // There are at most 3 of these: for iterating expansion statements, these
+ // consist of the '__range' and '__begin' variables, and for destructuring
+ // expansion statements of the DecompositionDecl whose initializer we're
+ // expanding. Finally, any expansion statement may have an init-statement
+ // as well.
+ SmallVector<Stmt *, 3> Preamble;
+ if (Expansion->getInit())
+ Preamble.push_back(Expansion->getInit());
+
+ if (Expansion->isIterating()) {
+ Preamble.push_back(Expansion->getRangeVarStmt());
+ Preamble.push_back(Expansion->getBeginVarStmt());
+ } else if (Expansion->isDestructuring()) {
+ Preamble.push_back(Expansion->getDecompositionDeclStmt());
+ MarkAnyDeclReferenced(Exp->getBeginLoc(), Expansion->getDecompositionDecl(),
+ true);
+ }
+
+ // Return an empty statement if the range is empty.
+ if (*NumInstantiations == 0) {
+ Expansion->getDecl()->setInstantiations(
+ CXXExpansionStmtInstantiation::Create(Context, Expansion->getDecl(),
+ /*Instantiations=*/{}, Preamble,
+ Expansion->isDestructuring()));
+ return Expansion;
+ }
+
+ // Create a compound statement binding the expansion variable and body,
+ // as well as the 'iter' variable if this is an iterating expansion statement.
+ SmallVector<Stmt *, 3> StmtsToInstantiate;
+ if (Expansion->isIterating())
+ StmtsToInstantiate.push_back(Expansion->getIterVarStmt());
+ StmtsToInstantiate.push_back(Expansion->getExpansionVarStmt());
+ StmtsToInstantiate.push_back(Body);
+ Stmt *CombinedBody =
+ CompoundStmt::Create(Context, StmtsToInstantiate, FPOptionsOverride(),
+ Body->getBeginLoc(), Body->getEndLoc());
+
+ // Expand the body for each instantiation.
+ SmallVector<Stmt *, 4> Instantiations;
+ CXXExpansionStmtDecl *ESD = Expansion->getDecl();
+ for (uint64_t I = 0; I < *NumInstantiations; ++I) {
+ TemplateArgument Arg{Context, llvm::APSInt::get(I),
+ Context.getPointerDiffType()};
+ MultiLevelTemplateArgumentList MTArgList(ESD, Arg, true);
+ MTArgList.addOuterRetainedLevels(
+ Expansion->getDecl()->getIndexTemplateParm()->getDepth());
+
+ LocalInstantiationScope LIScope(*this, /*CombineWithOuterScope=*/true);
+ NonSFINAEContext _(*this);
+ InstantiatingTemplate Inst(*this, Body->getBeginLoc(), Expansion, Arg,
+ Body->getSourceRange());
+
+ StmtResult Instantiation = SubstStmt(CombinedBody, MTArgList);
+ if (Instantiation.isInvalid())
+ return StmtError();
+ Instantiations.push_back(Instantiation.get());
+ }
+
+ auto *InstantiationsStmt = CXXExpansionStmtInstantiation::Create(
+ Context, Expansion->getDecl(), Instantiations, Preamble,
+ Expansion->isDestructuring() || Expansion->isIterating());
+
+ Expansion->getDecl()->setInstantiations(InstantiationsStmt);
+ return Expansion;
+}
+
+ExprResult Sema::BuildCXXExpansionSelectExpr(InitListExpr *Range, Expr *Idx) {
+ if (Idx->isValueDependent() || InitListContainsPack(Range))
+ return new (Context) CXXExpansionSelectExpr(Context, Range, Idx);
+
+ // The index is a DRE to a template parameter; we should never
+ // fail to evaluate it.
+ uint64_t I = Idx->EvaluateKnownConstInt(Context).getZExtValue();
+ return Range->getInit(I);
+}
+
+std::optional<uint64_t>
+Sema::ComputeExpansionSize(CXXExpansionStmtPattern *Expansion) {
+ if (Expansion->isEnumerating())
+ return cast<CXXExpansionSelectExpr>(
+ Expansion->getExpansionVariable()->getInit())
+ ->getRangeExpr()
+ ->getNumInits();
+
+ // [stmt.expand]p5.2 (CWG3131): N is the result of evaluating the expression
+ //
+ // [&] consteval {
+ // std::ptrdiff_t result = 0;
+ // auto b = begin-expr;
+ // auto e = end-expr;
+ // for (; b != e; ++b) ++result;
+ // return result;
+ // }()
+ if (Expansion->isIterating()) {
+ SourceLocation Loc = Expansion->getColonLoc();
+ EnterExpressionEvaluationContext ExprEvalCtx(
+ *this, ExpressionEvaluationContext::ConstantEvaluated);
+
+ // TODO: Build the lambda and evaluate it.
+ Diag(Loc, diag::err_iterating_expansion_stmt_unsupported);
+ return std::nullopt;
+
+#if 0 // This will be used once we support iterating expansion statements.
+ Expr::EvalResult ER;
+ SmallVector<PartialDiagnosticAt, 4> Notes;
+ ER.Diag = &Notes;
+ if (!Call.get()->EvaluateAsInt(ER, Context)) {
+ Diag(Loc, diag::err_expansion_size_expr_not_ice);
+ for (const auto &[Location, PDiag] : Notes)
+ Diag(Location, PDiag);
+ return std::nullopt;
+ }
+
+ // It shouldn't be possible for this to be negative since we compute this
+ // via the built-in '++' on a ptrdiff_t.
+ assert(ER.Val.getInt().isNonNegative());
+ return ER.Val.getInt().getZExtValue();
+#endif
+ }
+
+ assert(Expansion->isDestructuring());
+ return Expansion->getDecompositionDecl()->bindings().size();
+}
diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp
index 9502b440dbe97..ed07395c00e72 100644
--- a/clang/lib/Sema/SemaLookup.cpp
+++ b/clang/lib/Sema/SemaLookup.cpp
@@ -4468,7 +4468,8 @@ LabelDecl *Sema::LookupExistingLabel(IdentifierInfo *II, SourceLocation Loc) {
}
LabelDecl *Sema::LookupOrCreateLabel(IdentifierInfo *II, SourceLocation Loc,
- SourceLocation GnuLabelLoc) {
+ SourceLocation GnuLabelLoc,
+ bool IsLabelStmt) {
if (GnuLabelLoc.isValid()) {
// Local label definitions always shadow existing labels.
auto *Res = LabelDecl::Create(Context, CurContext, Loc, II, GnuLabelLoc);
@@ -4477,15 +4478,43 @@ LabelDecl *Sema::LookupOrCreateLabel(IdentifierInfo *II, SourceLocation Loc,
return cast<LabelDecl>(Res);
}
- // Not a GNU local label.
- LabelDecl *Res = LookupExistingLabel(II, Loc);
- if (!Res) {
- // If not forward referenced or defined already, create the backing decl.
- Res = LabelDecl::Create(Context, CurContext, Loc, II);
- Scope *S = CurScope->getFnParent();
- assert(S && "Not in a function?");
- PushOnScopeChains(Res, S, true);
+ LabelDecl *Existing = LookupExistingLabel(II, Loc);
+
+ // C++26 [stmt.label]p4 An identifier label shall not be enclosed by an
+ // expansion-statement.
+ //
+ // As an extension, we allow GNU local labels since they are logically
+ // scoped to the containing block, which prevents us from ending up with
+ // multiple copies of the same label in a function after instantiation.
+ //
+ // While allowing this is slightly more complicated, it also has the nice
+ // side-effect of avoiding otherwise rather horrible diagnostics you'd get
+ // when trying to use '__label__' if we didn't support this.
+ if (IsLabelStmt && CurContext->isExpansionStmt()) {
+ if (Existing && Existing->isGnuLocal())
+ return Existing;
+
+ // Drop the label from the AST as creating it anyway would cause us to
+ // either issue various unhelpful diagnostics (if we were to declare
+ // it in the function decl context) or shadow a valid label with the
+ // same name outside the expansion statement.
+ Diag(Loc, diag::err_expansion_stmt_label);
+ return nullptr;
}
+
+ if (Existing)
+ return Existing;
+
+ // Declare non-local labels outside any expansion statements; this is required
+ // to support jumping out of an expansion statement.
+ ContextRAII Ctx{*this, CurContext->getEnclosingNonExpansionStatementContext(),
+ /*NewThisContext=*/false};
+
+ // Not a GNU local label. Create the backing decl.
+ auto *Res = LabelDecl::Create(Context, CurContext, Loc, II);
+ Scope *S = CurScope->getFnParent();
+ assert(S && "Not in a function?");
+ PushOnScopeChains(Res, S, true);
return Res;
}
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index b74af55d1bea1..406a6da0903fb 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -528,6 +528,25 @@ Sema::ActOnCaseExpr(SourceLocation CaseLoc, ExprResult Val) {
return CheckAndFinish(Val.get());
}
+static bool DiagnoseSwitchCaseInExpansionStmt(Sema &S, SourceLocation KwLoc,
+ bool IsDefault) {
+ // C++26 [stmt.expand] The compound-statement of an expansion-statement is a
+ // control-flow-limited statement.
+ //
+ // We diagnose this here rather than in JumpDiagnostics because those run
+ // after the expansion statement is instantiated, at which point we will have
+ // have already complained about duplicate case labels, which is not exactly
+ // great QOI.
+ if (S.CurContext->isExpansionStmt() &&
+ S.getCurFunction()->SwitchStack.back().EnclosingDC != S.CurContext) {
+ S.Diag(KwLoc, diag::err_expansion_stmt_case) << IsDefault;
+ S.Diag(S.getCurFunction()->SwitchStack.back().getPointer()->getSwitchLoc(),
+ diag::note_enclosing_switch_statement_here);
+ return true;
+ }
+ return false;
+}
+
StmtResult
Sema::ActOnCaseStmt(SourceLocation CaseLoc, ExprResult LHSVal,
SourceLocation DotDotDotLoc, ExprResult RHSVal,
@@ -547,6 +566,9 @@ Sema::ActOnCaseStmt(SourceLocation CaseLoc, ExprResult LHSVal,
return StmtError();
}
+ if (DiagnoseSwitchCaseInExpansionStmt(*this, CaseLoc, false))
+ return StmtError();
+
if (LangOpts.OpenACC &&
getCurScope()->isInOpenACCComputeConstructScope(Scope::SwitchScope)) {
Diag(CaseLoc, diag::err_acc_branch_in_out_compute_construct)
@@ -572,6 +594,9 @@ Sema::ActOnDefaultStmt(SourceLocation DefaultLoc, SourceLocation ColonLoc,
return SubStmt;
}
+ if (DiagnoseSwitchCaseInExpansionStmt(*this, DefaultLoc, true))
+ return StmtError();
+
if (LangOpts.OpenACC &&
getCurScope()->isInOpenACCComputeConstructScope(Scope::SwitchScope)) {
Diag(DefaultLoc, diag::err_acc_branch_in_out_compute_construct)
@@ -1196,8 +1221,9 @@ StmtResult Sema::ActOnStartOfSwitchStmt(SourceLocation SwitchLoc,
auto *SS = SwitchStmt::Create(Context, InitStmt, Cond.get().first, CondExpr,
LParenLoc, RParenLoc);
+ SS->setSwitchLoc(SwitchLoc);
getCurFunction()->SwitchStack.push_back(
- FunctionScopeInfo::SwitchInfo(SS, false));
+ FunctionScopeInfo::SwitchInfo(SS, CurContext));
return SS;
}
@@ -1313,7 +1339,7 @@ Sema::ActOnFinishSwitchStmt(SourceLocation SwitchLoc, Stmt *Switch,
BodyStmt = new (Context) NullStmt(BodyStmt->getBeginLoc());
}
- SS->setBody(BodyStmt, SwitchLoc);
+ SS->setBody(BodyStmt);
Expr *CondExpr = SS->getCond();
if (!CondExpr) return StmtError();
@@ -2431,27 +2457,54 @@ void NoteForRangeBeginEndFunction(Sema &SemaRef, Expr *E,
SemaRef.Diag(Loc, diag::note_for_range_begin_end)
<< BEF << IsTemplate << Description << E->getType();
}
+} // namespace
/// Build a variable declaration for a for-range statement.
-VarDecl *BuildForRangeVarDecl(Sema &SemaRef, SourceLocation Loc,
- QualType Type, StringRef Name) {
- DeclContext *DC = SemaRef.CurContext;
- IdentifierInfo *II = &SemaRef.PP.getIdentifierTable().get(Name);
- TypeSourceInfo *TInfo = SemaRef.Context.getTrivialTypeSourceInfo(Type, Loc);
- VarDecl *Decl = VarDecl::Create(SemaRef.Context, DC, Loc, Loc, II, Type,
- TInfo, SC_None);
+VarDecl *Sema::BuildForRangeVarDecl(SourceLocation Loc, QualType Type,
+ IdentifierInfo *II, bool IsConstexpr) {
+ // Making the variable constexpr doesn't automatically add 'const' to the
+ // type, so do that now.
+ if (IsConstexpr && !Type->isReferenceType())
+ Type = Type.withConst();
+
+ DeclContext *DC = CurContext;
+ TypeSourceInfo *TInfo = Context.getTrivialTypeSourceInfo(Type, Loc);
+ VarDecl *Decl =
+ VarDecl::Create(Context, DC, Loc, Loc, II, Type, TInfo, SC_None);
Decl->setImplicit();
Decl->setCXXForRangeImplicitVar(true);
+ if (IsConstexpr)
+ // CWG3044 changed this from 'static constexpr' to 'constexpr'.
+ Decl->setConstexpr(true);
return Decl;
}
-}
-
static bool ObjCEnumerationCollection(Expr *Collection) {
return !Collection->isTypeDependent()
&& Collection->getType()->getAs<ObjCObjectPointerType>() != nullptr;
}
+StmtResult Sema::BuildCXXForRangeRangeVar(Scope *S, Expr *Range, QualType Type,
+ bool IsConstexpr) {
+
+ // Divide by 2, since the variables are in the inner scope (loop body).
+ const auto DepthStr = std::to_string(S->getDepth() / 2);
+ IdentifierInfo *Name =
+ PP.getIdentifierInfo(std::string("__range") + DepthStr);
+ SourceLocation RangeLoc = Range->getBeginLoc();
+ VarDecl *RangeVar = BuildForRangeVarDecl(
+ RangeLoc, Type, Name, IsConstexpr);
+ if (FinishForRangeVarDecl(*this, RangeVar, Range, RangeLoc,
+ diag::err_for_range_deduction_failure))
+
+ return StmtError();
+
+ // Claim the type doesn't contain auto: we've already done the checking.
+ DeclGroupPtrTy RangeGroup =
+ BuildDeclaratorGroup(MutableArrayRef<Decl *>((Decl **)&RangeVar, 1));
+ return ActOnDeclStmt(RangeGroup, RangeLoc, RangeLoc);
+}
+
StmtResult Sema::ActOnCXXForRangeStmt(
Scope *S, SourceLocation ForLoc, SourceLocation CoawaitLoc, Stmt *InitStmt,
Stmt *First, SourceLocation ColonLoc, Expr *Range, SourceLocation RParenLoc,
@@ -2496,22 +2549,8 @@ StmtResult Sema::ActOnCXXForRangeStmt(
}
// Build auto && __range = range-init
- // Divide by 2, since the variables are in the inner scope (loop body).
- const auto DepthStr = std::to_string(S->getDepth() / 2);
- SourceLocation RangeLoc = Range->getBeginLoc();
- VarDecl *RangeVar = BuildForRangeVarDecl(*this, RangeLoc,
- Context.getAutoRRefDeductType(),
- std::string("__range") + DepthStr);
- if (FinishForRangeVarDecl(*this, RangeVar, Range, RangeLoc,
- diag::err_for_range_deduction_failure)) {
- ActOnInitializerError(LoopVar);
- return StmtError();
- }
-
- // Claim the type doesn't contain auto: we've already done the checking.
- DeclGroupPtrTy RangeGroup =
- BuildDeclaratorGroup(MutableArrayRef<Decl *>((Decl **)&RangeVar, 1));
- StmtResult RangeDecl = ActOnDeclStmt(RangeGroup, RangeLoc, RangeLoc);
+ auto RangeDecl =
+ BuildCXXForRangeRangeVar(S, Range, Context.getAutoRRefDeductType());
if (RangeDecl.isInvalid()) {
ActOnInitializerError(LoopVar);
return StmtError();
@@ -2710,6 +2749,224 @@ static StmtResult RebuildForRangeWithDereference(Sema &SemaRef, Scope *S,
AdjustedRange.get(), RParenLoc, Sema::BFRK_Rebuild);
}
+void Sema::ApplyForRangeOrExpansionStatementLifetimeExtension(
+ VarDecl *RangeVar, ArrayRef<MaterializeTemporaryExpr *> Temporaries) {
+ if (Temporaries.empty())
+ return;
+
+ InitializedEntity Entity = InitializedEntity::InitializeVariable(RangeVar);
+ for (auto *MTE : Temporaries)
+ MTE->setExtendingDecl(RangeVar, Entity.allocateManglingNumber());
+}
+
+Sema::ForRangeBeginEndInfo Sema::BuildCXXForRangeBeginEndVars(
+ Scope *S, VarDecl *RangeVar, SourceLocation ColonLoc,
+ SourceLocation CoawaitLoc,
+ ArrayRef<MaterializeTemporaryExpr *> LifetimeExtendTemps,
+ BuildForRangeKind Kind, bool IsConstexpr, StmtResult *RebuildResult,
+ llvm::function_ref<StmtResult()> RebuildWithDereference,
+ IdentifierInfo *BeginName, IdentifierInfo *EndName) {
+ QualType RangeVarType = RangeVar->getType();
+ SourceLocation RangeLoc = RangeVar->getLocation();
+ const QualType RangeVarNonRefType = RangeVarType.getNonReferenceType();
+
+ ExprResult BeginRangeRef =
+ BuildDeclRefExpr(RangeVar, RangeVarNonRefType, VK_LValue, ColonLoc);
+ if (BeginRangeRef.isInvalid())
+ return {};
+
+ ExprResult EndRangeRef =
+ BuildDeclRefExpr(RangeVar, RangeVarNonRefType, VK_LValue, ColonLoc);
+ if (EndRangeRef.isInvalid())
+ return {};
+
+ QualType AutoType = Context.getAutoDeductType();
+ Expr *Range = RangeVar->getInit();
+ if (!Range)
+ return {};
+ QualType RangeType = Range->getType();
+
+ if (RequireCompleteType(RangeLoc, RangeType,
+ diag::err_for_range_incomplete_type))
+ return {};
+
+ // Build auto __begin = begin-expr, __end = end-expr.
+ // Divide by 2, since the variables are in the inner scope (loop body).
+ const auto DepthStr = std::to_string(S->getDepth() / 2);
+ if (!BeginName)
+ BeginName = PP.getIdentifierInfo(std::string("__begin") + DepthStr);
+ if (!EndName)
+ EndName = PP.getIdentifierInfo(std::string("__end") + DepthStr);
+ VarDecl *BeginVar = BuildForRangeVarDecl(
+ ColonLoc, AutoType, BeginName, IsConstexpr);
+ VarDecl *EndVar = BuildForRangeVarDecl(
+ ColonLoc, AutoType, EndName, IsConstexpr);
+
+ // Build begin-expr and end-expr and attach to __begin and __end variables.
+ ExprResult BeginExpr, EndExpr;
+ if (const ArrayType *UnqAT = RangeType->getAsArrayTypeUnsafe()) {
+ // - if _RangeT is an array type, begin-expr and end-expr are __range and
+ // __range + __bound, respectively, where __bound is the array bound. If
+ // _RangeT is an array of unknown size or an array of incomplete type,
+ // the program is ill-formed;
+
+ // begin-expr is __range.
+ BeginExpr = BeginRangeRef;
+ if (!CoawaitLoc.isInvalid()) {
+ BeginExpr = ActOnCoawaitExpr(S, ColonLoc, BeginExpr.get());
+ if (BeginExpr.isInvalid())
+ return {};
+ }
+ if (FinishForRangeVarDecl(*this, BeginVar, BeginRangeRef.get(), ColonLoc,
+ diag::err_for_range_iter_deduction_failure)) {
+ NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin);
+ return {};
+ }
+
+ // Find the array bound.
+ ExprResult BoundExpr;
+ if (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(UnqAT))
+ BoundExpr = IntegerLiteral::Create(
+ Context, CAT->getSize(), Context.getPointerDiffType(), RangeLoc);
+ else if (const VariableArrayType *VAT =
+ dyn_cast<VariableArrayType>(UnqAT)) {
+ // For a variably modified type we can't just use the expression within
+ // the array bounds, since we don't want that to be re-evaluated here.
+ // Rather, we need to determine what it was when the array was first
+ // created - so we resort to using sizeof(vla)/sizeof(element).
+ // For e.g.
+ // void f(int b) {
+ // int vla[b];
+ // b = -1; <-- This should not affect the num of iterations below
+ // for (int &c : vla) { .. }
+ // }
+
+ // FIXME: This results in codegen generating IR that recalculates the
+ // run-time number of elements (as opposed to just using the IR Value
+ // that corresponds to the run-time value of each bound that was
+ // generated when the array was created.) If this proves too embarrassing
+ // even for unoptimized IR, consider passing a magic-value/cookie to
+ // codegen that then knows to simply use that initial llvm::Value (that
+ // corresponds to the bound at time of array creation) within
+ // getelementptr. But be prepared to pay the price of increasing a
+ // customized form of coupling between the two components - which could
+ // be hard to maintain as the codebase evolves.
+
+ ExprResult SizeOfVLAExprR = ActOnUnaryExprOrTypeTraitExpr(
+ EndVar->getLocation(), UETT_SizeOf,
+ /*IsType=*/true,
+ CreateParsedType(VAT->desugar(), Context.getTrivialTypeSourceInfo(
+ VAT->desugar(), RangeLoc))
+ .getAsOpaquePtr(),
+ EndVar->getSourceRange());
+ if (SizeOfVLAExprR.isInvalid())
+ return {};
+
+ ExprResult SizeOfEachElementExprR = ActOnUnaryExprOrTypeTraitExpr(
+ EndVar->getLocation(), UETT_SizeOf,
+ /*IsType=*/true,
+ CreateParsedType(VAT->desugar(), Context.getTrivialTypeSourceInfo(
+ VAT->getElementType(), RangeLoc))
+ .getAsOpaquePtr(),
+ EndVar->getSourceRange());
+ if (SizeOfEachElementExprR.isInvalid())
+ return {};
+
+ BoundExpr =
+ ActOnBinOp(S, EndVar->getLocation(), tok::slash, SizeOfVLAExprR.get(),
+ SizeOfEachElementExprR.get());
+ if (BoundExpr.isInvalid())
+ return {};
+
+ } else {
+ // Can't be a DependentSizedArrayType or an IncompleteArrayType since
+ // UnqAT is not incomplete and Range is not type-dependent.
+ llvm_unreachable("Unexpected array type in for-range");
+ }
+
+ // end-expr is __range + __bound.
+ EndExpr =
+ ActOnBinOp(S, ColonLoc, tok::plus, EndRangeRef.get(), BoundExpr.get());
+ if (EndExpr.isInvalid())
+ return {};
+ if (FinishForRangeVarDecl(*this, EndVar, EndExpr.get(), ColonLoc,
+ diag::err_for_range_iter_deduction_failure)) {
+ NoteForRangeBeginEndFunction(*this, EndExpr.get(), BEF_end);
+ return {};
+ }
+ } else {
+ OverloadCandidateSet CandidateSet(RangeLoc,
+ OverloadCandidateSet::CSK_Normal);
+ BeginEndFunction BEFFailure;
+ ForRangeStatus RangeStatus =
+ BuildNonArrayForRange(*this, BeginRangeRef.get(), EndRangeRef.get(),
+ RangeType, BeginVar, EndVar, ColonLoc, CoawaitLoc,
+ &CandidateSet, &BeginExpr, &EndExpr, &BEFFailure);
+
+ if (Kind == BFRK_Build && RangeStatus == FRS_NoViableFunction &&
+ BEFFailure == BEF_begin) {
+ // If the range is being built from an array parameter, emit a
+ // a diagnostic that it is being treated as a pointer.
+ if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Range)) {
+ if (ParmVarDecl *PVD = dyn_cast<ParmVarDecl>(DRE->getDecl())) {
+ QualType ArrayTy = PVD->getOriginalType();
+ QualType PointerTy = PVD->getType();
+ if (PointerTy->isPointerType() && ArrayTy->isArrayType()) {
+ Diag(Range->getBeginLoc(), diag::err_range_on_array_parameter)
+ << RangeLoc << PVD << ArrayTy << PointerTy;
+ Diag(PVD->getLocation(), diag::note_declared_at);
+ return {};
+ }
+ }
+ }
+
+ // If building the range failed, try dereferencing the range expression
+ // unless a diagnostic was issued or the end function is problematic.
+ if (RebuildWithDereference) {
+ assert(RebuildResult);
+ StmtResult SR = RebuildWithDereference();
+ if (SR.isInvalid() || SR.isUsable()) {
+ *RebuildResult = SR;
+ return {};
+ }
+ }
+ }
+
+ // Otherwise, emit diagnostics if we haven't already.
+ if (RangeStatus == FRS_NoViableFunction) {
+ Expr *Range = BEFFailure ? EndRangeRef.get() : BeginRangeRef.get();
+ CandidateSet.NoteCandidates(
+ PartialDiagnosticAt(Range->getBeginLoc(),
+ PDiag(diag::err_for_range_invalid)
+ << RangeLoc << Range->getType()
+ << BEFFailure),
+ *this, OCD_AllCandidates, Range);
+ }
+ // Return an error if no fix was discovered.
+ if (RangeStatus != FRS_Success)
+ return {};
+ }
+
+ assert(!BeginExpr.isInvalid() && !EndExpr.isInvalid() &&
+ "invalid range expression in for loop");
+
+ return {BeginVar, EndVar, BeginExpr.get(), EndExpr.get()};
+}
+
+void Sema::ActOnDependentForRangeInitializer(VarDecl *LoopVar,
+ BuildForRangeKind BFRK) {
+ // Deduce any 'auto's in the loop variable as 'DependentTy'. We'll fill
+ // them in properly when we instantiate the loop.
+ if (!LoopVar->isInvalidDecl() && BFRK != BFRK_Check) {
+ if (auto *DD = dyn_cast<DecompositionDecl>(LoopVar))
+ for (auto *Binding : DD->bindings()) {
+ if (!Binding->isParameterPack())
+ Binding->setType(Context.DependentTy);
+ }
+ LoopVar->setType(SubstAutoTypeDependent(LoopVar->getType()));
+ }
+}
+
StmtResult Sema::BuildCXXForRangeStmt(
SourceLocation ForLoc, SourceLocation CoawaitLoc, Stmt *InitStmt,
SourceLocation ColonLoc, Stmt *RangeDecl, Stmt *Begin, Stmt *End,
@@ -2741,208 +2998,37 @@ StmtResult Sema::BuildCXXForRangeStmt(
if (RangeVarType->isDependentType()) {
// The range is implicitly used as a placeholder when it is dependent.
RangeVar->markUsed(Context);
-
- // Deduce any 'auto's in the loop variable as 'DependentTy'. We'll fill
- // them in properly when we instantiate the loop.
- if (!LoopVar->isInvalidDecl() && Kind != BFRK_Check) {
- if (auto *DD = dyn_cast<DecompositionDecl>(LoopVar))
- for (auto *Binding : DD->bindings()) {
- if (!Binding->isParameterPack())
- Binding->setType(Context.DependentTy);
- }
- LoopVar->setType(SubstAutoTypeDependent(LoopVar->getType()));
- }
+ ActOnDependentForRangeInitializer(LoopVar, Kind);
} else if (!BeginDeclStmt.get()) {
- SourceLocation RangeLoc = RangeVar->getLocation();
-
- const QualType RangeVarNonRefType = RangeVarType.getNonReferenceType();
-
- ExprResult BeginRangeRef = BuildDeclRefExpr(RangeVar, RangeVarNonRefType,
- VK_LValue, ColonLoc);
- if (BeginRangeRef.isInvalid())
- return StmtError();
-
- ExprResult EndRangeRef = BuildDeclRefExpr(RangeVar, RangeVarNonRefType,
- VK_LValue, ColonLoc);
- if (EndRangeRef.isInvalid())
- return StmtError();
+ StmtResult RebuildResult;
+ auto RebuildWithDereference = [&] {
+ return RebuildForRangeWithDereference(
+ *this, S, ForLoc, CoawaitLoc, InitStmt, LoopVarDecl, ColonLoc,
+ RangeVar->getInit(), RangeVar->getLocation(), RParenLoc);
+ };
- QualType AutoType = Context.getAutoDeductType();
- Expr *Range = RangeVar->getInit();
- if (!Range)
- return StmtError();
- QualType RangeType = Range->getType();
+ auto BeginRangeRefTy = RangeVar->getType().getNonReferenceType();
+ ForRangeBeginEndInfo ForRangeInfo = BuildCXXForRangeBeginEndVars(
+ S, RangeVar, ColonLoc, CoawaitLoc, LifetimeExtendTemps, Kind,
+ /*Constexpr=*/false, &RebuildResult, RebuildWithDereference);
- if (RequireCompleteType(RangeLoc, RangeType,
- diag::err_for_range_incomplete_type))
+ if (!RebuildResult.isUnset())
+ return RebuildResult;
+ if (!ForRangeInfo.isValid())
return StmtError();
- // Build auto __begin = begin-expr, __end = end-expr.
- // Divide by 2, since the variables are in the inner scope (loop body).
- const auto DepthStr = std::to_string(S->getDepth() / 2);
- VarDecl *BeginVar = BuildForRangeVarDecl(*this, ColonLoc, AutoType,
- std::string("__begin") + DepthStr);
- VarDecl *EndVar = BuildForRangeVarDecl(*this, ColonLoc, AutoType,
- std::string("__end") + DepthStr);
-
- // Build begin-expr and end-expr and attach to __begin and __end variables.
- ExprResult BeginExpr, EndExpr;
- if (const ArrayType *UnqAT = RangeType->getAsArrayTypeUnsafe()) {
- // - if _RangeT is an array type, begin-expr and end-expr are __range and
- // __range + __bound, respectively, where __bound is the array bound. If
- // _RangeT is an array of unknown size or an array of incomplete type,
- // the program is ill-formed;
-
- // begin-expr is __range.
- BeginExpr = BeginRangeRef;
- if (!CoawaitLoc.isInvalid()) {
- BeginExpr = ActOnCoawaitExpr(S, ColonLoc, BeginExpr.get());
- if (BeginExpr.isInvalid())
- return StmtError();
- }
- if (FinishForRangeVarDecl(*this, BeginVar, BeginRangeRef.get(), ColonLoc,
- diag::err_for_range_iter_deduction_failure)) {
- NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin);
- return StmtError();
- }
-
- // Find the array bound.
- ExprResult BoundExpr;
- if (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(UnqAT))
- BoundExpr = IntegerLiteral::Create(
- Context, CAT->getSize(), Context.getPointerDiffType(), RangeLoc);
- else if (const VariableArrayType *VAT =
- dyn_cast<VariableArrayType>(UnqAT)) {
- // For a variably modified type we can't just use the expression within
- // the array bounds, since we don't want that to be re-evaluated here.
- // Rather, we need to determine what it was when the array was first
- // created - so we resort to using sizeof(vla)/sizeof(element).
- // For e.g.
- // void f(int b) {
- // int vla[b];
- // b = -1; <-- This should not affect the num of iterations below
- // for (int &c : vla) { .. }
- // }
-
- // FIXME: This results in codegen generating IR that recalculates the
- // run-time number of elements (as opposed to just using the IR Value
- // that corresponds to the run-time value of each bound that was
- // generated when the array was created.) If this proves too embarrassing
- // even for unoptimized IR, consider passing a magic-value/cookie to
- // codegen that then knows to simply use that initial llvm::Value (that
- // corresponds to the bound at time of array creation) within
- // getelementptr. But be prepared to pay the price of increasing a
- // customized form of coupling between the two components - which could
- // be hard to maintain as the codebase evolves.
-
- ExprResult SizeOfVLAExprR = ActOnUnaryExprOrTypeTraitExpr(
- EndVar->getLocation(), UETT_SizeOf,
- /*IsType=*/true,
- CreateParsedType(VAT->desugar(), Context.getTrivialTypeSourceInfo(
- VAT->desugar(), RangeLoc))
- .getAsOpaquePtr(),
- EndVar->getSourceRange());
- if (SizeOfVLAExprR.isInvalid())
- return StmtError();
-
- ExprResult SizeOfEachElementExprR = ActOnUnaryExprOrTypeTraitExpr(
- EndVar->getLocation(), UETT_SizeOf,
- /*IsType=*/true,
- CreateParsedType(VAT->desugar(),
- Context.getTrivialTypeSourceInfo(
- VAT->getElementType(), RangeLoc))
- .getAsOpaquePtr(),
- EndVar->getSourceRange());
- if (SizeOfEachElementExprR.isInvalid())
- return StmtError();
-
- BoundExpr =
- ActOnBinOp(S, EndVar->getLocation(), tok::slash,
- SizeOfVLAExprR.get(), SizeOfEachElementExprR.get());
- if (BoundExpr.isInvalid())
- return StmtError();
-
- } else {
- // Can't be a DependentSizedArrayType or an IncompleteArrayType since
- // UnqAT is not incomplete and Range is not type-dependent.
- llvm_unreachable("Unexpected array type in for-range");
- }
-
- // end-expr is __range + __bound.
- EndExpr = ActOnBinOp(S, ColonLoc, tok::plus, EndRangeRef.get(),
- BoundExpr.get());
- if (EndExpr.isInvalid())
- return StmtError();
- if (FinishForRangeVarDecl(*this, EndVar, EndExpr.get(), ColonLoc,
- diag::err_for_range_iter_deduction_failure)) {
- NoteForRangeBeginEndFunction(*this, EndExpr.get(), BEF_end);
- return StmtError();
- }
- } else {
- OverloadCandidateSet CandidateSet(RangeLoc,
- OverloadCandidateSet::CSK_Normal);
- BeginEndFunction BEFFailure;
- ForRangeStatus RangeStatus = BuildNonArrayForRange(
- *this, BeginRangeRef.get(), EndRangeRef.get(), RangeType, BeginVar,
- EndVar, ColonLoc, CoawaitLoc, &CandidateSet, &BeginExpr, &EndExpr,
- &BEFFailure);
-
- if (Kind == BFRK_Build && RangeStatus == FRS_NoViableFunction &&
- BEFFailure == BEF_begin) {
- // If the range is being built from an array parameter, emit a
- // a diagnostic that it is being treated as a pointer.
- if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Range)) {
- if (ParmVarDecl *PVD = dyn_cast<ParmVarDecl>(DRE->getDecl())) {
- QualType ArrayTy = PVD->getOriginalType();
- QualType PointerTy = PVD->getType();
- if (PointerTy->isPointerType() && ArrayTy->isArrayType()) {
- Diag(Range->getBeginLoc(), diag::err_range_on_array_parameter)
- << RangeLoc << PVD << ArrayTy << PointerTy;
- Diag(PVD->getLocation(), diag::note_declared_at);
- return StmtError();
- }
- }
- }
-
- // If building the range failed, try dereferencing the range expression
- // unless a diagnostic was issued or the end function is problematic.
- StmtResult SR = RebuildForRangeWithDereference(*this, S, ForLoc,
- CoawaitLoc, InitStmt,
- LoopVarDecl, ColonLoc,
- Range, RangeLoc,
- RParenLoc);
- if (SR.isInvalid() || SR.isUsable())
- return SR;
- }
-
- // Otherwise, emit diagnostics if we haven't already.
- if (RangeStatus == FRS_NoViableFunction) {
- Expr *Range = BEFFailure ? EndRangeRef.get() : BeginRangeRef.get();
- CandidateSet.NoteCandidates(
- PartialDiagnosticAt(Range->getBeginLoc(),
- PDiag(diag::err_for_range_invalid)
- << RangeLoc << Range->getType()
- << BEFFailure),
- *this, OCD_AllCandidates, Range);
- }
- // Return an error if no fix was discovered.
- if (RangeStatus != FRS_Success)
- return StmtError();
- }
-
- assert(!BeginExpr.isInvalid() && !EndExpr.isInvalid() &&
- "invalid range expression in for loop");
-
// C++11 [dcl.spec.auto]p7: BeginType and EndType must be the same.
// C++1z removes this restriction.
+ auto [BeginVar, EndVar, BeginExpr, EndExpr] = ForRangeInfo;
+ SourceLocation RangeLoc = RangeVar->getLocation();
QualType BeginType = BeginVar->getType(), EndType = EndVar->getType();
if (!Context.hasSameType(BeginType, EndType)) {
Diag(RangeLoc, getLangOpts().CPlusPlus17
? diag::warn_for_range_begin_end_types_differ
: diag::ext_for_range_begin_end_types_differ)
<< BeginType << EndType;
- NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin);
- NoteForRangeBeginEndFunction(*this, EndExpr.get(), BEF_end);
+ NoteForRangeBeginEndFunction(*this, BeginExpr, BEF_begin);
+ NoteForRangeBeginEndFunction(*this, EndExpr, BEF_end);
}
BeginDeclStmt =
@@ -2971,10 +3057,10 @@ StmtResult Sema::BuildCXXForRangeStmt(
ActOnFinishFullExpr(NotEqExpr.get(), /*DiscardedValue*/ false);
if (NotEqExpr.isInvalid()) {
Diag(RangeLoc, diag::note_for_range_invalid_iterator)
- << RangeLoc << 0 << BeginRangeRef.get()->getType();
- NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin);
+ << RangeLoc << 0 << BeginRangeRefTy;
+ NoteForRangeBeginEndFunction(*this, BeginExpr, BEF_begin);
if (!Context.hasSameType(BeginType, EndType))
- NoteForRangeBeginEndFunction(*this, EndExpr.get(), BEF_end);
+ NoteForRangeBeginEndFunction(*this, EndExpr, BEF_end);
return StmtError();
}
@@ -2994,8 +3080,8 @@ StmtResult Sema::BuildCXXForRangeStmt(
IncrExpr = ActOnFinishFullExpr(IncrExpr.get(), /*DiscardedValue*/ false);
if (IncrExpr.isInvalid()) {
Diag(RangeLoc, diag::note_for_range_invalid_iterator)
- << RangeLoc << 2 << BeginRangeRef.get()->getType() ;
- NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin);
+ << RangeLoc << 2 << BeginRangeRefTy;
+ NoteForRangeBeginEndFunction(*this, BeginExpr, BEF_begin);
return StmtError();
}
@@ -3008,8 +3094,8 @@ StmtResult Sema::BuildCXXForRangeStmt(
ExprResult DerefExpr = ActOnUnaryOp(S, ColonLoc, tok::star, BeginRef.get());
if (DerefExpr.isInvalid()) {
Diag(RangeLoc, diag::note_for_range_invalid_iterator)
- << RangeLoc << 1 << BeginRangeRef.get()->getType();
- NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin);
+ << RangeLoc << 1 << BeginRangeRefTy;
+ NoteForRangeBeginEndFunction(*this, BeginExpr, BEF_begin);
return StmtError();
}
@@ -3019,7 +3105,7 @@ StmtResult Sema::BuildCXXForRangeStmt(
AddInitializerToDecl(LoopVar, DerefExpr.get(), /*DirectInit=*/false);
if (LoopVar->isInvalidDecl() ||
(LoopVar->getInit() && LoopVar->getInit()->containsErrors()))
- NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin);
+ NoteForRangeBeginEndFunction(*this, BeginExpr, BEF_begin);
}
}
@@ -3034,11 +3120,9 @@ StmtResult Sema::BuildCXXForRangeStmt(
OpenMP().ActOnOpenMPLoopInitialization(ForLoc, BeginDeclStmt.get());
// P2718R0 - Lifetime extension in range-based for loops.
- if (getLangOpts().CPlusPlus23 && !LifetimeExtendTemps.empty()) {
- InitializedEntity Entity = InitializedEntity::InitializeVariable(RangeVar);
- for (auto *MTE : LifetimeExtendTemps)
- MTE->setExtendingDecl(RangeVar, Entity.allocateManglingNumber());
- }
+ if (getLangOpts().CPlusPlus23)
+ ApplyForRangeOrExpansionStatementLifetimeExtension(RangeVar,
+ LifetimeExtendTemps);
return new (Context) CXXForRangeStmt(
InitStmt, RangeDS, cast_or_null<DeclStmt>(BeginDeclStmt.get()),
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 34ed5dffa11b4..296f0406acbaf 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -572,6 +572,7 @@ bool Sema::CodeSynthesisContext::isInstantiationRecord() const {
case PriorTemplateArgumentSubstitution:
case ConstraintsCheck:
case NestedRequirementConstraintsCheck:
+ case ExpansionStmtInstantiation:
return true;
case RequirementInstantiation:
@@ -760,6 +761,15 @@ Sema::InstantiatingTemplate::InstantiatingTemplate(
PointOfInstantiation, InstantiationRange, /*Entity=*/nullptr,
/*Template=*/nullptr, /*TemplateArgs=*/{}) {}
+Sema::InstantiatingTemplate::InstantiatingTemplate(
+ Sema &SemaRef, SourceLocation PointOfInstantiation,
+ CXXExpansionStmtPattern *ExpansionStmt, ArrayRef<TemplateArgument> TArgs,
+ SourceRange InstantiationRange)
+ : InstantiatingTemplate(
+ SemaRef, CodeSynthesisContext::ExpansionStmtInstantiation,
+ PointOfInstantiation, InstantiationRange, /*Entity=*/nullptr,
+ /*Template=*/nullptr, /*TemplateArgs=*/TArgs) {}
+
Sema::InstantiatingTemplate::InstantiatingTemplate(
Sema &SemaRef, SourceLocation PointOfInstantiation,
concepts::NestedRequirement *Req, ConstraintsCheck,
@@ -1308,6 +1318,9 @@ void Sema::PrintInstantiationStack(InstantiationContextDiagFuncRef DiagFunc) {
Active->NumCallArgs)));
break;
}
+ case CodeSynthesisContext::ExpansionStmtInstantiation:
+ Diags.Report(Active->PointOfInstantiation,
+ diag::note_expansion_stmt_instantiation_here);
}
}
}
@@ -1918,6 +1931,12 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) {
maybeInstantiateFunctionParameterToScope(PVD))
return nullptr;
+ if (isa<CXXExpansionStmtDecl>(D)) {
+ assert(SemaRef.CurrentInstantiationScope);
+ return cast<Decl *>(
+ *SemaRef.CurrentInstantiationScope->findInstantiationOf(D));
+ }
+
return SemaRef.FindInstantiatedDecl(Loc, cast<NamedDecl>(D), TemplateArgs);
}
@@ -2322,9 +2341,13 @@ ExprResult
TemplateInstantiator::TransformFunctionParmPackRefExpr(DeclRefExpr *E,
ValueDecl *PD) {
typedef LocalInstantiationScope::DeclArgumentPack DeclArgumentPack;
- llvm::PointerUnion<Decl *, DeclArgumentPack *> *Found
- = getSema().CurrentInstantiationScope->findInstantiationOf(PD);
- assert(Found && "no instantiation for parameter pack");
+ llvm::PointerUnion<Decl *, DeclArgumentPack *> *Found =
+ getSema().CurrentInstantiationScope->getInstantiationOfIfExists(PD);
+
+ // This can happen when instantiating an expansion statement that contains
+ // a pack (e.g. `template for (auto x : {{ts...}})`).
+ if (!Found)
+ return E;
Decl *TransformedDecl;
if (DeclArgumentPack *Pack = dyn_cast<DeclArgumentPack *>(*Found)) {
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index d057476a012db..04d79991d4663 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -2103,7 +2103,37 @@ Decl *TemplateDeclInstantiator::VisitStaticAssertDecl(StaticAssertDecl *D) {
Decl *TemplateDeclInstantiator::VisitCXXExpansionStmtDecl(
CXXExpansionStmtDecl *OldESD) {
- llvm_unreachable("TODO");
+ Decl *Index = VisitNonTypeTemplateParmDecl(OldESD->getIndexTemplateParm());
+ CXXExpansionStmtDecl *NewESD = SemaRef.BuildCXXExpansionStmtDecl(
+ Owner, OldESD->getBeginLoc(), cast<NonTypeTemplateParmDecl>(Index));
+ SemaRef.CurrentInstantiationScope->InstantiatedLocal(OldESD, NewESD);
+
+ // If this was already expanded, only instantiate the expansion and
+ // don't touch the unexpanded expansion statement.
+ if (CXXExpansionStmtInstantiation *OldInst = OldESD->getInstantiations()) {
+ StmtResult NewInst = SemaRef.SubstStmt(OldInst, TemplateArgs);
+ if (NewInst.isInvalid())
+ return nullptr;
+
+ NewESD->setInstantiations(NewInst.getAs<CXXExpansionStmtInstantiation>());
+ NewESD->setExpansionPattern(OldESD->getExpansionPattern());
+ return NewESD;
+ }
+
+ // Enter the scope of this expansion statement; don't do this if we've
+ // already expanded it, as in that case we no longer want to treat its
+ // content as dependent.
+ Sema::ContextRAII Context(SemaRef, NewESD, /*NewThis=*/false);
+
+ StmtResult Expansion =
+ SemaRef.SubstStmt(OldESD->getExpansionPattern(), TemplateArgs);
+ if (Expansion.isInvalid())
+ return nullptr;
+
+ // The code that handles CXXExpansionStmtPattern takes care of calling
+ // setInstantiation() on the ESD if there was an expansion.
+ NewESD->setExpansionPattern(cast<CXXExpansionStmtPattern>(Expansion.get()));
+ return NewESD;
}
Decl *TemplateDeclInstantiator::VisitEnumDecl(EnumDecl *D) {
@@ -7108,6 +7138,12 @@ NamedDecl *Sema::FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D,
// anonymous unions in class templates).
}
+ if (CurrentInstantiationScope) {
+ if (auto Found = CurrentInstantiationScope->getInstantiationOfIfExists(D))
+ if (auto *FD = dyn_cast<NamedDecl>(cast<Decl *>(*Found)))
+ return FD;
+ }
+
if (!ParentDependsOnArgs)
return D;
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index 5b1aad3fa8470..b61148bf95738 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -912,10 +912,14 @@ bool Sema::CheckParameterPacksForExpansion(
unsigned NewPackSize, PendingPackExpansionSize = 0;
if (IsVarDeclPack) {
// Figure out whether we're instantiating to an argument pack or not.
+ //
+ // The instantiation may not exist; this can happen when instantiating an
+ // expansion statement that contains a pack (e.g.
+ // `template for (auto x : {{ts...}})`).
llvm::PointerUnion<Decl *, DeclArgumentPack *> *Instantiation =
- CurrentInstantiationScope->findInstantiationOf(
+ CurrentInstantiationScope->getInstantiationOfIfExists(
cast<NamedDecl *>(ParmPack.first));
- if (isa<DeclArgumentPack *>(*Instantiation)) {
+ if (Instantiation && isa<DeclArgumentPack *>(*Instantiation)) {
// We could expand this function parameter pack.
NewPackSize = cast<DeclArgumentPack *>(*Instantiation)->size();
} else {
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index ad8e0b76f1dcb..bf80bc1110ec5 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -9372,19 +9372,201 @@ TreeTransform<Derived>::TransformCXXForRangeStmt(CXXForRangeStmt *S) {
template <typename Derived>
StmtResult TreeTransform<Derived>::TransformCXXExpansionStmtPattern(
CXXExpansionStmtPattern *S) {
- llvm_unreachable("TOOD");
+ assert(SemaRef.CurContext->isExpansionStmt());
+
+ Decl *ESD =
+ getDerived().TransformDecl(S->getDecl()->getLocation(), S->getDecl());
+ if (!ESD || ESD->isInvalidDecl())
+ return StmtError();
+ CXXExpansionStmtDecl *NewESD = cast<CXXExpansionStmtDecl>(ESD);
+
+ // This is required because some parts of an expansion statement (e.g. the
+ // init-statement) are not in a dependent context and must thus be transformed
+ // in the parent context.
+ auto TransformStmtInParentContext = [&] (Stmt *SubStmt) -> StmtResult {
+ Sema::ContextRAII CtxGuard(SemaRef, SemaRef.CurContext->getParent(),
+ /*NewThis=*/false);
+ return getDerived().TransformStmt(SubStmt);
+ };
+
+ Stmt *Init = S->getInit();
+ if (Init) {
+ StmtResult SR = TransformStmtInParentContext(Init);
+ if (SR.isInvalid())
+ return StmtError();
+ Init = SR.get();
+ }
+
+ // Collect lifetime-extended temporaries in case this ends up being a
+ // destructuring or iterating expansion statement.
+ //
+ // CWG 3140: Additionally, for iterating expansions statements, we need to
+ // apply lifetime extension to the initializer of the range.
+ ExprResult ExpansionInitializer;
+ StmtResult Range;
+ SmallVector<MaterializeTemporaryExpr *, 8> LifetimeExtendTemps;
+ if (S->isDependent() || S->isIterating()) {
+ EnterExpressionEvaluationContext ExprEvalCtx(
+ SemaRef, SemaRef.currentEvaluationContext().Context);
+ SemaRef.currentEvaluationContext().InLifetimeExtendingContext = true;
+ SemaRef.currentEvaluationContext().RebuildDefaultArgOrDefaultInit = true;
+
+ if (S->isDependent()) {
+ // The expansion initializer should not be in the context of the expansion
+ // statement because it isn't instantiated when the expansion statement is
+ // expanded.
+ Sema::ContextRAII CtxGuard(SemaRef, SemaRef.CurContext->getParent(),
+ /*NewThis=*/false);
+ ExpansionInitializer =
+ getDerived().TransformExpr(S->getExpansionInitializer());
+ if (ExpansionInitializer.isInvalid())
+ return StmtError();
+ } else if (S->isIterating()) {
+ Range = TransformStmtInParentContext(S->getRangeVarStmt());
+ if (Range.isInvalid())
+ return StmtError();
+ }
+
+ ExpansionInitializer =
+ SemaRef.MaybeCreateExprWithCleanups(ExpansionInitializer);
+
+ LifetimeExtendTemps =
+ SemaRef.currentEvaluationContext().ForRangeLifetimeExtendTemps;
+ }
+
+ CXXExpansionStmtPattern *NewPattern = nullptr;
+ if (S->isEnumerating()) {
+ StmtResult ExpansionVar =
+ getDerived().TransformStmt(S->getExpansionVarStmt());
+ if (ExpansionVar.isInvalid())
+ return StmtError();
+
+ NewPattern = CXXExpansionStmtPattern::CreateEnumerating(
+ SemaRef.Context, NewESD, Init, ExpansionVar.getAs<DeclStmt>(),
+ S->getLParenLoc(), S->getColonLoc(), S->getRParenLoc());
+ } else if (S->isIterating()) {
+ StmtResult Begin = TransformStmtInParentContext(S->getBeginVarStmt());
+ StmtResult Iter = TransformStmtInParentContext(S->getIterVarStmt());
+ if (Begin.isInvalid() || Iter.isInvalid())
+ return StmtError();
+
+ // The expansion variable is part of the pattern only and never ends
+ // up in the instantiations, so keep it in the expansion statement's
+ // DeclContext.
+ StmtResult ExpansionVar =
+ getDerived().TransformStmt(S->getExpansionVarStmt());
+ if (ExpansionVar.isInvalid())
+ return StmtError();
+
+ NewPattern = CXXExpansionStmtPattern::CreateIterating(
+ SemaRef.Context, NewESD, Init, ExpansionVar.getAs<DeclStmt>(),
+ Range.getAs<DeclStmt>(), Begin.getAs<DeclStmt>(),
+ Iter.getAs<DeclStmt>(), S->getLParenLoc(), S->getColonLoc(),
+ S->getRParenLoc());
+
+ SemaRef.ApplyForRangeOrExpansionStatementLifetimeExtension(
+ NewPattern->getRangeVar(), LifetimeExtendTemps);
+ } else if (S->isDependent()) {
+ StmtResult ExpansionVar =
+ getDerived().TransformStmt(S->getExpansionVarStmt());
+ if (ExpansionVar.isInvalid())
+ return StmtError();
+
+ StmtResult Res = SemaRef.BuildNonEnumeratingCXXExpansionStmtPattern(
+ NewESD, Init, ExpansionVar.getAs<DeclStmt>(),
+ ExpansionInitializer.get(), S->getLParenLoc(), S->getColonLoc(),
+ S->getRParenLoc(), LifetimeExtendTemps);
+
+ if (Res.isInvalid())
+ return StmtError();
+
+ NewPattern = cast<CXXExpansionStmtPattern>(Res.get());
+ } else {
+ // The only time we instantiate an expansion statement is if its expansion
+ // size is dependent (otherwise, we only instantiate the expansions and
+ // leave the underlying CXXExpansionStmtPattern as-is). Since destructuring
+ // expansion statements never have a dependent size, we should never get
+ // here.
+ llvm_unreachable("destructuring pattern should never be instantiated");
+ }
+
+ StmtResult Body = getDerived().TransformStmt(S->getBody());
+ if (Body.isInvalid())
+ return StmtError();
+
+ return SemaRef.FinishCXXExpansionStmt(NewPattern, Body.get());
}
template <typename Derived>
StmtResult TreeTransform<Derived>::TransformCXXExpansionStmtInstantiation(
CXXExpansionStmtInstantiation *S) {
- llvm_unreachable("TOOD");
+ bool SubStmtChanged = false;
+ auto TransformStmts = [&](SmallVectorImpl<Stmt *> &NewStmts,
+ ArrayRef<Stmt *> OldStmts) {
+ for (Stmt *OldDS : OldStmts) {
+ StmtResult NewDS = getDerived().TransformStmt(OldDS);
+ if (NewDS.isInvalid())
+ return true;
+
+ SubStmtChanged |= NewDS.get() != OldDS;
+ NewStmts.push_back(NewDS.get());
+ }
+
+ return false;
+ };
+
+ Decl *ESD =
+ getDerived().TransformDecl(S->getParent()->getLocation(), S->getParent());
+ if (!ESD || ESD->isInvalidDecl())
+ return StmtError();
+ CXXExpansionStmtDecl *NewESD = cast<CXXExpansionStmtDecl>(ESD);
+
+ SmallVector<Stmt *> PreambleStmts;
+ SmallVector<Stmt *> Instantiations;
+
+ // Apply lifetime extension to the preamble statements if this was a
+ // destructuring expansion statement.
+ {
+ EnterExpressionEvaluationContext ExprEvalCtx(
+ SemaRef, SemaRef.currentEvaluationContext().Context);
+ SemaRef.currentEvaluationContext().InLifetimeExtendingContext = true;
+ SemaRef.currentEvaluationContext().RebuildDefaultArgOrDefaultInit = true;
+ if (TransformStmts(PreambleStmts, S->getPreambleStmts()))
+ return StmtError();
+
+ if (S->shouldApplyLifetimeExtensionToPreamble()) {
+ auto *VD =
+ cast<VarDecl>(cast<DeclStmt>(PreambleStmts.front())->getSingleDecl());
+ SemaRef.ApplyForRangeOrExpansionStatementLifetimeExtension(
+ VD, SemaRef.currentEvaluationContext().ForRangeLifetimeExtendTemps);
+ }
+ }
+
+ if (TransformStmts(Instantiations, S->getInstantiations()))
+ return StmtError();
+
+ if (!getDerived().AlwaysRebuild() && !SubStmtChanged)
+ return S;
+
+ return CXXExpansionStmtInstantiation::Create(
+ SemaRef.Context, NewESD, Instantiations, PreambleStmts,
+ S->shouldApplyLifetimeExtensionToPreamble());
}
template <typename Derived>
ExprResult TreeTransform<Derived>::TransformCXXExpansionSelectExpr(
CXXExpansionSelectExpr *E) {
- llvm_unreachable("TOOD");
+ ExprResult Range = getDerived().TransformExpr(E->getRangeExpr());
+ ExprResult Idx = getDerived().TransformExpr(E->getIndexExpr());
+ if (Range.isInvalid() || Idx.isInvalid())
+ return ExprError();
+
+ if (!getDerived().AlwaysRebuild() && Range.get() == E->getRangeExpr() &&
+ Idx.get() == E->getIndexExpr())
+ return E;
+
+ return SemaRef.BuildCXXExpansionSelectExpr(Range.getAs<InitListExpr>(),
+ Idx.get());
}
template<typename Derived>
@@ -15826,7 +16008,7 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
// will be deemed as dependent even if there are no dependent template
// arguments.
// (A ClassTemplateSpecializationDecl is always a dependent context.)
- while (DC->isRequiresExprBody())
+ while (DC->isRequiresExprBody() || isa<CXXExpansionStmtDecl>(DC))
DC = DC->getParent();
if ((getSema().isUnevaluatedContext() ||
getSema().isConstantEvaluatedContext()) &&
diff --git a/clang/test/AST/ast-dump-expansion-stmt.cpp b/clang/test/AST/ast-dump-expansion-stmt.cpp
new file mode 100644
index 0000000000000..94f0ee1449f17
--- /dev/null
+++ b/clang/test/AST/ast-dump-expansion-stmt.cpp
@@ -0,0 +1,54 @@
+// Test without serialization:
+// RUN: %clang_cc1 -std=c++26 -triple x86_64-unknown-unknown -ast-dump %s
+//
+// Test with serialization:
+// RUN: %clang_cc1 -std=c++26 -triple x86_64-unknown-unknown -emit-pch -o %t %s
+// RUN: %clang_cc1 -x c++ -std=c++26 -triple x86_64-unknown-unknown -include-pch %t -ast-dump-all /dev/null \
+// RUN: | sed -e "s/ <undeserialized declarations>//" -e "s/ imported//"
+
+#if 0 // Disabled until we support iterating expansion statements.
+template <typename T, __SIZE_TYPE__ size>
+struct Array {
+ T data[size]{};
+ constexpr const T* begin() const { return data; }
+ constexpr const T* end() const { return data + size; }
+};
+#endif // 0
+
+void foo(int);
+
+template <typename T>
+void test(T t) {
+ // CHECK: CXXExpansionStmtDecl
+ // CHECK-NEXT: CXXExpansionStmtPattern {{.*}} enumerating
+ // CHECK: CXXExpansionStmtInstantiation
+ template for (auto x : {1, 2, 3}) {
+ foo(x);
+ }
+
+#if 0 // Disabled until we support iterating expansion statements.
+ // NOTE: Remove 'DISABLED-' when the '#if 0' is removed.
+ // DISABLED-CHECK: CXXExpansionStmtDecl
+ // DISABLED-CHECK-NEXT: CXXExpansionStmtPattern {{.*}} iterating
+ // DISABLED-CHECK: CXXExpansionStmtInstantiation
+ static constexpr Array<int, 3> a;
+ template for (auto x : a) {
+ foo(x);
+ }
+#endif
+
+ // CHECK: CXXExpansionStmtDecl
+ // CHECK-NEXT: CXXExpansionStmtPattern {{.*}} destructuring
+ // CHECK: CXXExpansionStmtInstantiation
+ int arr[3]{1, 2, 3};
+ template for (auto x : arr) {
+ foo(x);
+ }
+
+ // CHECK: CXXExpansionStmtDecl
+ // CHECK-NEXT: CXXExpansionStmtPattern {{.*}} dependent
+ // CHECK-NOT: CXXExpansionStmtInstantiation
+ template for (auto x : t) {
+ foo(x);
+ }
+}
diff --git a/clang/test/AST/ast-print-expansion-stmts.cpp b/clang/test/AST/ast-print-expansion-stmts.cpp
new file mode 100644
index 0000000000000..880e17a9d3e2f
--- /dev/null
+++ b/clang/test/AST/ast-print-expansion-stmts.cpp
@@ -0,0 +1,113 @@
+// Without serialization:
+// RUN: %clang_cc1 -std=c++26 -ast-print %s | FileCheck %s
+//
+// With serialization:
+// RUN: %clang_cc1 -std=c++26 -emit-pch -o %t %s
+// RUN: %clang_cc1 -x c++ -std=c++26 -include-pch %t -ast-print /dev/null | FileCheck %s
+
+#if 0 // Disabled until we support iterating expansion statements.
+template <typename T, __SIZE_TYPE__ size>
+struct Array {
+ T data[size]{};
+ constexpr const T* begin() const { return data; }
+ constexpr const T* end() const { return data + size; }
+};
+#endif // 0
+
+// CHECK: void foo(int);
+void foo(int);
+
+// CHECK: template <typename T> void test(T t) {
+template <typename T>
+void test(T t) {
+ // Enumerating expansion statement.
+ //
+ // CHECK: template for (auto x : {1, 2, 3}) {
+ // CHECK-NEXT: foo(x);
+ // CHECK-NEXT: }
+ template for (auto x : {1, 2, 3}) {
+ foo(x);
+ }
+
+#if 0 // Disabled until we support iterating expansion statements.
+ // Iterating expansion statement.
+ //
+ // NOTE: Remove 'DISABLED-' when the '#if 0' is removed.
+ // DISABLED-CHECK: static constexpr Array<int, 3> a;
+ // DISABLED-CHECK-NEXT: template for (auto x : (a)) {
+ // DISABLED-CHECK-NEXT: foo(x);
+ // DISABLED-CHECK-NEXT: }
+ static constexpr Array<int, 3> a;
+ template for (auto x : a) {
+ foo(x);
+ }
+#endif // 0
+
+ // Destructuring expansion statement.
+ //
+ // CHECK: int arr[3]{1, 2, 3};
+ // CHECK-NEXT: template for (auto x : arr) {
+ // CHECK-NEXT: foo(x);
+ // CHECK-NEXT: }
+ int arr[3]{1, 2, 3};
+ template for (auto x : arr) {
+ foo(x);
+ }
+
+ // Dependent expansion statement.
+ //
+ // CHECK: template for (auto x : t) {
+ // CHECK-NEXT: foo(x);
+ // CHECK-NEXT: }
+ template for (auto x : t) {
+ foo(x);
+ }
+}
+
+// CHECK: template <typename T> void test2(T t) {
+template <typename T>
+void test2(T t) {
+ // Enumerating expansion statement.
+ //
+ // CHECK: template for (int x : {1, 2, 3}) {
+ // CHECK-NEXT: foo(x);
+ // CHECK-NEXT: }
+ template for (int x : {1, 2, 3}) {
+ foo(x);
+ }
+
+#if 0 // Disabled until we support iterating expansion statements.
+ // Iterating expansion statement.
+ //
+ // NOTE: Remove 'DISABLED-' when the '#if 0' is removed.
+ // DISABLED-CHECK: static constexpr Array<int, 3> a;
+ // DISABLED-CHECK-NEXT: template for (int x : (a)) {
+ // DISABLED-CHECK-NEXT: foo(x);
+ // DISABLED-CHECK-NEXT: }
+
+ static constexpr Array<int, 3> a;
+ template for (int x : a) {
+ foo(x);
+ }
+#endif // 0
+
+ // Destructuring expansion statement.
+ //
+ // CHECK: int arr[3]{1, 2, 3};
+ // CHECK-NEXT: template for (int x : arr) {
+ // CHECK-NEXT: foo(x);
+ // CHECK-NEXT: }
+ int arr[3]{1, 2, 3};
+ template for (int x : arr) {
+ foo(x);
+ }
+
+ // Dependent expansion statement.
+ //
+ // CHECK: template for (int x : t) {
+ // CHECK-NEXT: foo(x);
+ // CHECK-NEXT: }
+ template for (int x : t) {
+ foo(x);
+ }
+}
diff --git a/clang/test/CodeGenCXX/cxx2c-destructuring-expansion-stmt.cpp b/clang/test/CodeGenCXX/cxx2c-destructuring-expansion-stmt.cpp
new file mode 100644
index 0000000000000..64a3b20f81c6a
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx2c-destructuring-expansion-stmt.cpp
@@ -0,0 +1,532 @@
+// RUN: %clang_cc1 -std=c++2c -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s
+
+struct A {};
+struct B { int x = 1; };
+struct C { int a = 1, b = 2, c = 3; };
+
+void g(int);
+
+int references_destructuring() {
+ C c;
+ template for (auto& x : c) { ++x; }
+ template for (auto&& x : c) { ++x; }
+ return c.a + c.b + c.c;
+}
+
+template <auto v>
+int destructure() {
+ int sum = 0;
+ template for (auto x : v) sum += x;
+ template for (constexpr auto x : v) sum += x;
+ return sum;
+}
+
+void f() {
+ destructure<B{10}>();
+ destructure<C{}>();
+ destructure<C{3, 4, 5}>();
+}
+
+void empty() {
+ static constexpr A a;
+ template for (auto x : A()) g(x);
+ template for (auto& x : a) g(x);
+ template for (auto&& x : A()) g(x);
+ template for (constexpr auto x : a) g(x);
+}
+
+namespace apply_lifetime_extension {
+struct T {
+ int& x;
+ T(int& x) noexcept : x(x) {}
+ ~T() noexcept { x = 42; }
+};
+
+const T& f(const T& t) noexcept { return t; }
+T g(int& x) noexcept { return T(x); }
+
+// CWG 3043:
+//
+// Lifetime extension only applies to destructuring expansion statements
+// (enumerating statements don't have a range variable, and the range variable
+// of iterating statements is constexpr).
+int lifetime_extension() {
+ int x = 5;
+ int sum = 0;
+ template for (auto e : f(g(x))) {
+ sum += x;
+ }
+ return sum + x;
+}
+
+template <typename T>
+int lifetime_extension_instantiate_expansions() {
+ int x = 5;
+ int sum = 0;
+ template for (T e : f(g(x))) {
+ sum += x;
+ }
+ return sum + x;
+}
+
+template <typename T>
+int lifetime_extension_dependent_expansion_stmt() {
+ int x = 5;
+ int sum = 0;
+ template for (int e : f(g((T&)x))) {
+ sum += x;
+ }
+ return sum + x;
+}
+
+template <typename U>
+struct foo {
+ template <typename T>
+ int lifetime_extension_multiple_instantiations() {
+ int x = 5;
+ int sum = 0;
+ template for (T e : f(g((U&)x))) {
+ sum += x;
+ }
+ return sum + x;
+ }
+};
+
+void instantiate() {
+ lifetime_extension_instantiate_expansions<int>();
+ lifetime_extension_dependent_expansion_stmt<int>();
+ foo<int>().lifetime_extension_multiple_instantiations<int>();
+}
+}
+
+struct Volatile {
+ volatile int x;
+};
+
+int volatile_member(Volatile* v) {
+ int sum = 0;
+ template for (auto& x : *v) {
+ sum += x;
+ x = 4;
+ }
+ return sum;
+}
+
+// CHECK: $_ZN1CC1Ev = comdat any
+// CHECK: $_Z11destructureITnDaXtl1BLi10EEEEiv = comdat any
+// CHECK: $_Z11destructureITnDaXtl1CLi1ELi2ELi3EEEEiv = comdat any
+// CHECK: $_Z11destructureITnDaXtl1CLi3ELi4ELi5EEEEiv = comdat any
+// CHECK: $_ZN24apply_lifetime_extension1TC1ERi = comdat any
+// CHECK: $_ZN24apply_lifetime_extension1TD1Ev = comdat any
+// CHECK: $_ZN24apply_lifetime_extension41lifetime_extension_instantiate_expansionsIiEEiv = comdat any
+// CHECK: $_ZN24apply_lifetime_extension43lifetime_extension_dependent_expansion_stmtIiEEiv = comdat any
+// CHECK: $_ZN24apply_lifetime_extension3fooIiE42lifetime_extension_multiple_instantiationsIiEEiv = comdat any
+// CHECK: $_ZN1CC2Ev = comdat any
+// CHECK: $_ZN24apply_lifetime_extension1TC2ERi = comdat any
+// CHECK: $_ZN24apply_lifetime_extension1TD2Ev = comdat any
+// CHECK: $_ZTAXtl1BLi10EEE = comdat any
+// CHECK: $_ZTAXtl1CLi1ELi2ELi3EEE = comdat any
+// CHECK: $_ZTAXtl1CLi3ELi4ELi5EEE = comdat any
+// CHECK: @_ZZ5emptyvE1a = internal constant %struct.A zeroinitializer, align 1
+// CHECK: @_ZTAXtl1BLi10EEE = {{.*}} constant %struct.B { i32 10 }, comdat
+// CHECK: @_ZTAXtl1CLi1ELi2ELi3EEE = {{.*}} constant %struct.C { i32 1, i32 2, i32 3 }, comdat
+// CHECK: @_ZTAXtl1CLi3ELi4ELi5EEE = {{.*}} constant %struct.C { i32 3, i32 4, i32 5 }, comdat
+
+
+// CHECK-LABEL: define {{.*}} i32 @_Z24references_destructuringv()
+// CHECK: entry:
+// CHECK-NEXT: %c = alloca %struct.C, align 4
+// CHECK-NEXT: %0 = alloca ptr, align 8
+// CHECK-NEXT: %x = alloca ptr, align 8
+// CHECK-NEXT: %x1 = alloca ptr, align 8
+// CHECK-NEXT: %x4 = alloca ptr, align 8
+// CHECK-NEXT: %1 = alloca ptr, align 8
+// CHECK-NEXT: %x7 = alloca ptr, align 8
+// CHECK-NEXT: %x11 = alloca ptr, align 8
+// CHECK-NEXT: %x15 = alloca ptr, align 8
+// CHECK-NEXT: call void @_ZN1CC1Ev(ptr {{.*}} %c)
+// CHECK-NEXT: store ptr %c, ptr %0, align 8
+// CHECK-NEXT: %2 = load ptr, ptr %0, align 8
+// CHECK-NEXT: %a = getelementptr inbounds nuw %struct.C, ptr %2, i32 0, i32 0
+// CHECK-NEXT: store ptr %a, ptr %x, align 8
+// CHECK-NEXT: %3 = load ptr, ptr %x, align 8
+// CHECK-NEXT: %4 = load i32, ptr %3, align 4
+// CHECK-NEXT: %inc = add nsw i32 %4, 1
+// CHECK-NEXT: store i32 %inc, ptr %3, align 4
+// CHECK-NEXT: br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT: %5 = load ptr, ptr %0, align 8
+// CHECK-NEXT: %b = getelementptr inbounds nuw %struct.C, ptr %5, i32 0, i32 1
+// CHECK-NEXT: store ptr %b, ptr %x1, align 8
+// CHECK-NEXT: %6 = load ptr, ptr %x1, align 8
+// CHECK-NEXT: %7 = load i32, ptr %6, align 4
+// CHECK-NEXT: %inc2 = add nsw i32 %7, 1
+// CHECK-NEXT: store i32 %inc2, ptr %6, align 4
+// CHECK-NEXT: br label %expand.next3
+// CHECK: expand.next3:
+// CHECK-NEXT: %8 = load ptr, ptr %0, align 8
+// CHECK-NEXT: %c5 = getelementptr inbounds nuw %struct.C, ptr %8, i32 0, i32 2
+// CHECK-NEXT: store ptr %c5, ptr %x4, align 8
+// CHECK-NEXT: %9 = load ptr, ptr %x4, align 8
+// CHECK-NEXT: %10 = load i32, ptr %9, align 4
+// CHECK-NEXT: %inc6 = add nsw i32 %10, 1
+// CHECK-NEXT: store i32 %inc6, ptr %9, align 4
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: store ptr %c, ptr %1, align 8
+// CHECK-NEXT: %11 = load ptr, ptr %1, align 8
+// CHECK-NEXT: %a8 = getelementptr inbounds nuw %struct.C, ptr %11, i32 0, i32 0
+// CHECK-NEXT: store ptr %a8, ptr %x7, align 8
+// CHECK-NEXT: %12 = load ptr, ptr %x7, align 8
+// CHECK-NEXT: %13 = load i32, ptr %12, align 4
+// CHECK-NEXT: %inc9 = add nsw i32 %13, 1
+// CHECK-NEXT: store i32 %inc9, ptr %12, align 4
+// CHECK-NEXT: br label %expand.next10
+// CHECK: expand.next10:
+// CHECK-NEXT: %14 = load ptr, ptr %1, align 8
+// CHECK-NEXT: %b12 = getelementptr inbounds nuw %struct.C, ptr %14, i32 0, i32 1
+// CHECK-NEXT: store ptr %b12, ptr %x11, align 8
+// CHECK-NEXT: %15 = load ptr, ptr %x11, align 8
+// CHECK-NEXT: %16 = load i32, ptr %15, align 4
+// CHECK-NEXT: %inc13 = add nsw i32 %16, 1
+// CHECK-NEXT: store i32 %inc13, ptr %15, align 4
+// CHECK-NEXT: br label %expand.next14
+// CHECK: expand.next14:
+// CHECK-NEXT: %17 = load ptr, ptr %1, align 8
+// CHECK-NEXT: %c16 = getelementptr inbounds nuw %struct.C, ptr %17, i32 0, i32 2
+// CHECK-NEXT: store ptr %c16, ptr %x15, align 8
+// CHECK-NEXT: %18 = load ptr, ptr %x15, align 8
+// CHECK-NEXT: %19 = load i32, ptr %18, align 4
+// CHECK-NEXT: %inc17 = add nsw i32 %19, 1
+// CHECK-NEXT: store i32 %inc17, ptr %18, align 4
+// CHECK-NEXT: br label %expand.end18
+// CHECK: expand.end18:
+// CHECK-NEXT: %a19 = getelementptr inbounds nuw %struct.C, ptr %c, i32 0, i32 0
+// CHECK-NEXT: %20 = load i32, ptr %a19, align 4
+// CHECK-NEXT: %b20 = getelementptr inbounds nuw %struct.C, ptr %c, i32 0, i32 1
+// CHECK-NEXT: %21 = load i32, ptr %b20, align 4
+// CHECK-NEXT: %add = add nsw i32 %20, %21
+// CHECK-NEXT: %c21 = getelementptr inbounds nuw %struct.C, ptr %c, i32 0, i32 2
+// CHECK-NEXT: %22 = load i32, ptr %c21, align 4
+// CHECK-NEXT: %add22 = add nsw i32 %add, %22
+// CHECK-NEXT: ret i32 %add22
+
+
+// CHECK-LABEL: define {{.*}} void @_Z1fv()
+// CHECK: entry:
+// CHECK-NEXT: %call = call {{.*}} i32 @_Z11destructureITnDaXtl1BLi10EEEEiv()
+// CHECK-NEXT: %call1 = call {{.*}} i32 @_Z11destructureITnDaXtl1CLi1ELi2ELi3EEEEiv()
+// CHECK-NEXT: %call2 = call {{.*}} i32 @_Z11destructureITnDaXtl1CLi3ELi4ELi5EEEEiv()
+// CHECK-NEXT: ret void
+
+
+// CHECK-LABEL: define {{.*}} i32 @_Z11destructureITnDaXtl1BLi10EEEEiv()
+// CHECK: entry:
+// CHECK-NEXT: %sum = alloca i32, align 4
+// CHECK-NEXT: %0 = alloca ptr, align 8
+// CHECK-NEXT: %x = alloca i32, align 4
+// CHECK-NEXT: %1 = alloca ptr, align 8
+// CHECK-NEXT: %x1 = alloca i32, align 4
+// CHECK-NEXT: store i32 0, ptr %sum, align 4
+// CHECK-NEXT: store ptr @_ZTAXtl1BLi10EEE, ptr %0, align 8
+// CHECK-NEXT: %2 = load i32, ptr @_ZTAXtl1BLi10EEE, align 4
+// CHECK-NEXT: store i32 %2, ptr %x, align 4
+// CHECK-NEXT: %3 = load i32, ptr %x, align 4
+// CHECK-NEXT: %4 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add = add nsw i32 %4, %3
+// CHECK-NEXT: store i32 %add, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: store ptr @_ZTAXtl1BLi10EEE, ptr %1, align 8
+// CHECK-NEXT: store i32 10, ptr %x1, align 4
+// CHECK-NEXT: %5 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add2 = add nsw i32 %5, 10
+// CHECK-NEXT: store i32 %add2, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.end3
+// CHECK: expand.end3:
+// CHECK-NEXT: %6 = load i32, ptr %sum, align 4
+// CHECK-NEXT: ret i32 %6
+
+
+// CHECK-LABEL: define {{.*}} i32 @_Z11destructureITnDaXtl1CLi1ELi2ELi3EEEEiv()
+// CHECK: entry:
+// CHECK-NEXT: %sum = alloca i32, align 4
+// CHECK-NEXT: %0 = alloca ptr, align 8
+// CHECK-NEXT: %x = alloca i32, align 4
+// CHECK-NEXT: %x1 = alloca i32, align 4
+// CHECK-NEXT: %x4 = alloca i32, align 4
+// CHECK-NEXT: %1 = alloca ptr, align 8
+// CHECK-NEXT: %x6 = alloca i32, align 4
+// CHECK-NEXT: %x9 = alloca i32, align 4
+// CHECK-NEXT: %x12 = alloca i32, align 4
+// CHECK-NEXT: store i32 0, ptr %sum, align 4
+// CHECK-NEXT: store ptr @_ZTAXtl1CLi1ELi2ELi3EEE, ptr %0, align 8
+// CHECK-NEXT: %2 = load i32, ptr @_ZTAXtl1CLi1ELi2ELi3EEE, align 4
+// CHECK-NEXT: store i32 %2, ptr %x, align 4
+// CHECK-NEXT: %3 = load i32, ptr %x, align 4
+// CHECK-NEXT: %4 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add = add nsw i32 %4, %3
+// CHECK-NEXT: store i32 %add, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT: %5 = load i32, ptr getelementptr inbounds nuw (i8, ptr @_ZTAXtl1CLi1ELi2ELi3EEE, i64 4), align 4
+// CHECK-NEXT: store i32 %5, ptr %x1, align 4
+// CHECK-NEXT: %6 = load i32, ptr %x1, align 4
+// CHECK-NEXT: %7 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add2 = add nsw i32 %7, %6
+// CHECK-NEXT: store i32 %add2, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next3
+// CHECK: expand.next3:
+// CHECK-NEXT: %8 = load i32, ptr getelementptr inbounds nuw (i8, ptr @_ZTAXtl1CLi1ELi2ELi3EEE, i64 8), align 4
+// CHECK-NEXT: store i32 %8, ptr %x4, align 4
+// CHECK-NEXT: %9 = load i32, ptr %x4, align 4
+// CHECK-NEXT: %10 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add5 = add nsw i32 %10, %9
+// CHECK-NEXT: store i32 %add5, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: store ptr @_ZTAXtl1CLi1ELi2ELi3EEE, ptr %1, align 8
+// CHECK-NEXT: store i32 1, ptr %x6, align 4
+// CHECK-NEXT: %11 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add7 = add nsw i32 %11, 1
+// CHECK-NEXT: store i32 %add7, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next8
+// CHECK: expand.next8:
+// CHECK-NEXT: store i32 2, ptr %x9, align 4
+// CHECK-NEXT: %12 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add10 = add nsw i32 %12, 2
+// CHECK-NEXT: store i32 %add10, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next11
+// CHECK: expand.next11:
+// CHECK-NEXT: store i32 3, ptr %x12, align 4
+// CHECK-NEXT: %13 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add13 = add nsw i32 %13, 3
+// CHECK-NEXT: store i32 %add13, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.end14
+// CHECK: expand.end14:
+// CHECK-NEXT: %14 = load i32, ptr %sum, align 4
+// CHECK-NEXT: ret i32 %14
+
+
+// CHECK-LABEL: define {{.*}} i32 @_Z11destructureITnDaXtl1CLi3ELi4ELi5EEEEiv()
+// CHECK: entry:
+// CHECK-NEXT: %sum = alloca i32, align 4
+// CHECK-NEXT: %0 = alloca ptr, align 8
+// CHECK-NEXT: %x = alloca i32, align 4
+// CHECK-NEXT: %x1 = alloca i32, align 4
+// CHECK-NEXT: %x4 = alloca i32, align 4
+// CHECK-NEXT: %1 = alloca ptr, align 8
+// CHECK-NEXT: %x6 = alloca i32, align 4
+// CHECK-NEXT: %x9 = alloca i32, align 4
+// CHECK-NEXT: %x12 = alloca i32, align 4
+// CHECK-NEXT: store i32 0, ptr %sum, align 4
+// CHECK-NEXT: store ptr @_ZTAXtl1CLi3ELi4ELi5EEE, ptr %0, align 8
+// CHECK-NEXT: %2 = load i32, ptr @_ZTAXtl1CLi3ELi4ELi5EEE, align 4
+// CHECK-NEXT: store i32 %2, ptr %x, align 4
+// CHECK-NEXT: %3 = load i32, ptr %x, align 4
+// CHECK-NEXT: %4 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add = add nsw i32 %4, %3
+// CHECK-NEXT: store i32 %add, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT: %5 = load i32, ptr getelementptr inbounds nuw (i8, ptr @_ZTAXtl1CLi3ELi4ELi5EEE, i64 4), align 4
+// CHECK-NEXT: store i32 %5, ptr %x1, align 4
+// CHECK-NEXT: %6 = load i32, ptr %x1, align 4
+// CHECK-NEXT: %7 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add2 = add nsw i32 %7, %6
+// CHECK-NEXT: store i32 %add2, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next3
+// CHECK: expand.next3:
+// CHECK-NEXT: %8 = load i32, ptr getelementptr inbounds nuw (i8, ptr @_ZTAXtl1CLi3ELi4ELi5EEE, i64 8), align 4
+// CHECK-NEXT: store i32 %8, ptr %x4, align 4
+// CHECK-NEXT: %9 = load i32, ptr %x4, align 4
+// CHECK-NEXT: %10 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add5 = add nsw i32 %10, %9
+// CHECK-NEXT: store i32 %add5, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: store ptr @_ZTAXtl1CLi3ELi4ELi5EEE, ptr %1, align 8
+// CHECK-NEXT: store i32 3, ptr %x6, align 4
+// CHECK-NEXT: %11 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add7 = add nsw i32 %11, 3
+// CHECK-NEXT: store i32 %add7, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next8
+// CHECK: expand.next8:
+// CHECK-NEXT: store i32 4, ptr %x9, align 4
+// CHECK-NEXT: %12 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add10 = add nsw i32 %12, 4
+// CHECK-NEXT: store i32 %add10, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next11
+// CHECK: expand.next11:
+// CHECK-NEXT: store i32 5, ptr %x12, align 4
+// CHECK-NEXT: %13 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add13 = add nsw i32 %13, 5
+// CHECK-NEXT: store i32 %add13, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.end14
+// CHECK: expand.end14:
+// CHECK-NEXT: %14 = load i32, ptr %sum, align 4
+// CHECK-NEXT: ret i32 %14
+
+
+// CHECK-LABEL: define {{.*}} void @_Z5emptyv()
+// CHECK: entry:
+// CHECK-NEXT: %0 = alloca ptr, align 8
+// CHECK-NEXT: %ref.tmp = alloca %struct.A, align 1
+// CHECK-NEXT: %1 = alloca ptr, align 8
+// CHECK-NEXT: %2 = alloca ptr, align 8
+// CHECK-NEXT: %ref.tmp1 = alloca %struct.A, align 1
+// CHECK-NEXT: %3 = alloca ptr, align 8
+// CHECK-NEXT: store ptr %ref.tmp, ptr %0, align 8
+// CHECK-NEXT: store ptr @_ZZ5emptyvE1a, ptr %1, align 8
+// CHECK-NEXT: store ptr %ref.tmp1, ptr %2, align 8
+// CHECK-NEXT: store ptr @_ZZ5emptyvE1a, ptr %3, align 8
+// CHECK-NEXT: ret void
+
+
+// CHECK-LABEL: define {{.*}} i32 @_ZN24apply_lifetime_extension18lifetime_extensionEv()
+// CHECK: entry:
+// CHECK-NEXT: %x = alloca i32, align 4
+// CHECK-NEXT: %sum = alloca i32, align 4
+// CHECK-NEXT: %0 = alloca ptr, align 8
+// CHECK: %ref.tmp = alloca %"struct.apply_lifetime_extension::T", align 8
+// CHECK-NEXT: %e = alloca i32, align 4
+// CHECK-NEXT: store i32 5, ptr %x, align 4
+// CHECK-NEXT: store i32 0, ptr %sum, align 4
+// CHECK: call void @_ZN24apply_lifetime_extension1gERi(ptr dead_on_unwind writable sret(%"struct.apply_lifetime_extension::T") align 8 %ref.tmp, ptr {{.*}} %x)
+// CHECK-NEXT: %call = call {{.*}} ptr @_ZN24apply_lifetime_extension1fERKNS_1TE(ptr {{.*}} %ref.tmp)
+// CHECK-NEXT: store ptr %call, ptr %0, align 8
+// CHECK-NEXT: %1 = load ptr, ptr %0, align 8
+// CHECK: %x1 = getelementptr inbounds nuw %"struct.apply_lifetime_extension::T", ptr %1, i32 0, i32 0
+// CHECK-NEXT: %2 = load ptr, ptr %x1, align 8
+// CHECK-NEXT: %3 = load i32, ptr %2, align 4
+// CHECK-NEXT: store i32 %3, ptr %e, align 4
+// CHECK-NEXT: %4 = load i32, ptr %x, align 4
+// CHECK-NEXT: %5 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add = add nsw i32 %5, %4
+// CHECK-NEXT: store i32 %add, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: call void @_ZN24apply_lifetime_extension1TD1Ev(ptr {{.*}} %ref.tmp)
+// CHECK-NEXT: %6 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %7 = load i32, ptr %x, align 4
+// CHECK-NEXT: %add2 = add nsw i32 %6, %7
+// CHECK-NEXT: ret i32 %add2
+
+
+// CHECK-LABEL: define {{.*}} i32 @_ZN24apply_lifetime_extension41lifetime_extension_instantiate_expansionsIiEEiv()
+// CHECK: entry:
+// CHECK-NEXT: %x = alloca i32, align 4
+// CHECK-NEXT: %sum = alloca i32, align 4
+// CHECK-NEXT: %0 = alloca ptr, align 8
+// CHECK: %ref.tmp = alloca %"struct.apply_lifetime_extension::T", align 8
+// CHECK-NEXT: %e = alloca i32, align 4
+// CHECK-NEXT: store i32 5, ptr %x, align 4
+// CHECK-NEXT: store i32 0, ptr %sum, align 4
+// CHECK: call void @_ZN24apply_lifetime_extension1gERi(ptr dead_on_unwind writable sret(%"struct.apply_lifetime_extension::T") align 8 %ref.tmp, ptr {{.*}} %x)
+// CHECK-NEXT: %call = call {{.*}} ptr @_ZN24apply_lifetime_extension1fERKNS_1TE(ptr {{.*}} %ref.tmp)
+// CHECK-NEXT: store ptr %call, ptr %0, align 8
+// CHECK-NEXT: %1 = load ptr, ptr %0, align 8
+// CHECK: %x1 = getelementptr inbounds nuw %"struct.apply_lifetime_extension::T", ptr %1, i32 0, i32 0
+// CHECK-NEXT: %2 = load ptr, ptr %x1, align 8
+// CHECK-NEXT: %3 = load i32, ptr %2, align 4
+// CHECK-NEXT: store i32 %3, ptr %e, align 4
+// CHECK-NEXT: %4 = load i32, ptr %x, align 4
+// CHECK-NEXT: %5 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add = add nsw i32 %5, %4
+// CHECK-NEXT: store i32 %add, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: call void @_ZN24apply_lifetime_extension1TD1Ev(ptr {{.*}} %ref.tmp)
+// CHECK-NEXT: %6 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %7 = load i32, ptr %x, align 4
+// CHECK-NEXT: %add2 = add nsw i32 %6, %7
+// CHECK-NEXT: ret i32 %add2
+
+
+// CHECK-LABEL: define {{.*}} i32 @_ZN24apply_lifetime_extension43lifetime_extension_dependent_expansion_stmtIiEEiv()
+// CHECK: entry:
+// CHECK-NEXT: %x = alloca i32, align 4
+// CHECK-NEXT: %sum = alloca i32, align 4
+// CHECK-NEXT: %0 = alloca ptr, align 8
+// CHECK: %ref.tmp = alloca %"struct.apply_lifetime_extension::T", align 8
+// CHECK-NEXT: %e = alloca i32, align 4
+// CHECK-NEXT: store i32 5, ptr %x, align 4
+// CHECK-NEXT: store i32 0, ptr %sum, align 4
+// CHECK: call void @_ZN24apply_lifetime_extension1gERi(ptr dead_on_unwind writable sret(%"struct.apply_lifetime_extension::T") align 8 %ref.tmp, ptr {{.*}} %x)
+// CHECK-NEXT: %call = call {{.*}} ptr @_ZN24apply_lifetime_extension1fERKNS_1TE(ptr {{.*}} %ref.tmp)
+// CHECK-NEXT: store ptr %call, ptr %0, align 8
+// CHECK-NEXT: %1 = load ptr, ptr %0, align 8
+// CHECK: %x1 = getelementptr inbounds nuw %"struct.apply_lifetime_extension::T", ptr %1, i32 0, i32 0
+// CHECK-NEXT: %2 = load ptr, ptr %x1, align 8
+// CHECK-NEXT: %3 = load i32, ptr %2, align 4
+// CHECK-NEXT: store i32 %3, ptr %e, align 4
+// CHECK-NEXT: %4 = load i32, ptr %x, align 4
+// CHECK-NEXT: %5 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add = add nsw i32 %5, %4
+// CHECK-NEXT: store i32 %add, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: call void @_ZN24apply_lifetime_extension1TD1Ev(ptr {{.*}} %ref.tmp)
+// CHECK-NEXT: %6 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %7 = load i32, ptr %x, align 4
+// CHECK-NEXT: %add2 = add nsw i32 %6, %7
+// CHECK-NEXT: ret i32 %add2
+
+
+// CHECK-LABEL: define {{.*}} i32 @_ZN24apply_lifetime_extension3fooIiE42lifetime_extension_multiple_instantiationsIiEEiv(ptr {{.*}} %this)
+// CHECK: entry:
+// CHECK-NEXT: %this.addr = alloca ptr, align 8
+// CHECK-NEXT: %x = alloca i32, align 4
+// CHECK-NEXT: %sum = alloca i32, align 4
+// CHECK-NEXT: %0 = alloca ptr, align 8
+// CHECK: %ref.tmp = alloca %"struct.apply_lifetime_extension::T", align 8
+// CHECK-NEXT: %e = alloca i32, align 4
+// CHECK-NEXT: store ptr %this, ptr %this.addr, align 8
+// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-NEXT: store i32 5, ptr %x, align 4
+// CHECK-NEXT: store i32 0, ptr %sum, align 4
+// CHECK: call void @_ZN24apply_lifetime_extension1gERi(ptr {{.*}} %ref.tmp, ptr {{.*}} %x)
+// CHECK-NEXT: %call = call {{.*}} ptr @_ZN24apply_lifetime_extension1fERKNS_1TE(ptr {{.*}} %ref.tmp)
+// CHECK-NEXT: store ptr %call, ptr %0, align 8
+// CHECK-NEXT: %1 = load ptr, ptr %0, align 8
+// CHECK: %x2 = getelementptr inbounds nuw %"struct.apply_lifetime_extension::T", ptr %1, i32 0, i32 0
+// CHECK-NEXT: %2 = load ptr, ptr %x2, align 8
+// CHECK-NEXT: %3 = load i32, ptr %2, align 4
+// CHECK-NEXT: store i32 %3, ptr %e, align 4
+// CHECK-NEXT: %4 = load i32, ptr %x, align 4
+// CHECK-NEXT: %5 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add = add nsw i32 %5, %4
+// CHECK-NEXT: store i32 %add, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: call void @_ZN24apply_lifetime_extension1TD1Ev(ptr {{.*}} %ref.tmp)
+// CHECK-NEXT: %6 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %7 = load i32, ptr %x, align 4
+// CHECK-NEXT: %add3 = add nsw i32 %6, %7
+// CHECK-NEXT: ret i32 %add3
+
+
+// CHECK-LABEL: define {{.*}} i32 @_Z15volatile_memberP8Volatile(ptr {{.*}} %v)
+// CHECK: entry:
+// CHECK-NEXT: %v.addr = alloca ptr, align 8
+// CHECK-NEXT: %sum = alloca i32, align 4
+// CHECK-NEXT: %0 = alloca ptr, align 8
+// CHECK-NEXT: %x = alloca ptr, align 8
+// CHECK-NEXT: store ptr %v, ptr %v.addr, align 8
+// CHECK-NEXT: store i32 0, ptr %sum, align 4
+// CHECK-NEXT: %1 = load ptr, ptr %v.addr, align 8
+// CHECK-NEXT: store ptr %1, ptr %0, align 8
+// CHECK-NEXT: %2 = load ptr, ptr %0, align 8
+// CHECK-NEXT: %x1 = getelementptr inbounds nuw %struct.Volatile, ptr %2, i32 0, i32 0
+// CHECK-NEXT: store ptr %x1, ptr %x, align 8
+// CHECK-NEXT: %3 = load ptr, ptr %x, align 8
+// CHECK-NEXT: %4 = load volatile i32, ptr %3, align 4
+// CHECK-NEXT: %5 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add = add nsw i32 %5, %4
+// CHECK-NEXT: store i32 %add, ptr %sum, align 4
+// CHECK-NEXT: %6 = load ptr, ptr %x, align 8
+// CHECK-NEXT: store volatile i32 4, ptr %6, align 4
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: %7 = load i32, ptr %sum, align 4
+// CHECK-NEXT: ret i32 %7
diff --git a/clang/test/CodeGenCXX/cxx2c-enumerating-expansion-statements.cpp b/clang/test/CodeGenCXX/cxx2c-enumerating-expansion-statements.cpp
new file mode 100644
index 0000000000000..c82b345de206b
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx2c-enumerating-expansion-statements.cpp
@@ -0,0 +1,1518 @@
+// RUN: %clang_cc1 -std=c++2c -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s
+
+struct S {
+ int x;
+ constexpr S(int x) : x{x} {}
+};
+
+void g(int);
+void g(long);
+void g(const char*);
+void g(S);
+
+template <int n> constexpr int tg() { return n; }
+
+void h(int, int);
+
+void f1() {
+ template for (auto x : {1, 2, 3}) g(x);
+}
+
+void f2() {
+ template for (auto x : {1, "123", S(45)}) g(x);
+}
+
+void f3() {
+ template for (auto x : {}) g(x);
+}
+
+void f4() {
+ template for (auto x : {1, 2})
+ template for (auto y : {3, 4})
+ h(x, y);
+}
+
+void f5() {
+ template for (auto x : {}) static_assert(false, "discarded");
+ template for (constexpr auto x : {}) static_assert(false, "discarded");
+ template for (auto x : {1}) g(x);
+ template for (auto x : {2, 3, 4}) g(x);
+ template for (constexpr auto x : {5}) g(x);
+ template for (constexpr auto x : {6, 7, 8}) g(x);
+ template for (constexpr auto x : {9}) tg<x>();
+ template for (constexpr auto x : {10, 11, 12})
+ static_assert(tg<x>());
+
+ template for (int x : {13, 14, 15}) g(x);
+ template for (S x : {16, 17, 18}) g(x.x);
+ template for (constexpr S x : {19, 20, 21}) tg<x.x>();
+}
+
+template <typename T>
+void t1() {
+ template for (T x : {}) g(x);
+ template for (constexpr T x : {}) g(x);
+ template for (auto x : {}) g(x);
+ template for (constexpr auto x : {}) g(x);
+ template for (T x : {1, 2}) g(x);
+ template for (T x : {T(3), T(4)}) g(x);
+ template for (auto x : {T(5), T(6)}) g(x);
+ template for (constexpr T x : {T(7), T(8)}) static_assert(tg<x>());
+ template for (constexpr auto x : {T(9), T(10)}) static_assert(tg<x>());
+}
+
+template <typename U>
+struct s1 {
+ template <typename T>
+ void tf() {
+ template for (T x : {}) g(x);
+ template for (constexpr T x : {}) g(x);
+ template for (U x : {}) g(x);
+ template for (constexpr U x : {}) g(x);
+ template for (auto x : {}) g(x);
+ template for (constexpr auto x : {}) g(x);
+ template for (T x : {1, 2}) g(x);
+ template for (U x : {3, 4}) g(x);
+ template for (U x : {T(5), T(6)}) g(x);
+ template for (T x : {U(7), U(8)}) g(x);
+ template for (auto x : {T(9), T(10)}) g(x);
+ template for (auto x : {U(11), T(12)}) g(x);
+ template for (constexpr U x : {T(13), T(14)}) static_assert(tg<x>());
+ template for (constexpr T x : {U(15), U(16)}) static_assert(tg<x>());
+ template for (constexpr auto x : {T(17), U(18)}) static_assert(tg<x>());
+ }
+};
+
+template <typename T>
+void t2() {
+ template for (T x : {}) g(x);
+}
+
+void f6() {
+ t1<int>();
+ t1<long>();
+ s1<long>().tf<long>();
+ s1<int>().tf<int>();
+ s1<int>().tf<long>();
+ s1<long>().tf<int>();
+ t2<S>();
+ t2<S[1231]>();
+ t2<S***>();
+}
+
+struct X {
+ int a, b, c;
+};
+
+template <typename ...Ts>
+void t3(Ts... ts) {
+ template for (auto x : {ts...}) g(x);
+ template for (auto x : {1, ts..., 2, ts..., 3}) g(x);
+ template for (auto x : {4, ts..., ts..., 5}) g(x);
+ template for (X x : {{ts...}, {ts...}, {6, 7, 8}}) g(x.a);
+ template for (X x : {X{ts...}}) g(x.a);
+}
+
+template <int ...is>
+void t4() {
+ template for (constexpr auto x : {is...}) {
+ g(x);
+ tg<x>();
+ }
+
+ template for (constexpr auto x : {1, is..., 2, is..., 3}) {
+ g(x);
+ tg<x>();
+ }
+
+ template for (constexpr auto x : {4, is..., is..., 5}) {
+ g(x);
+ tg<x>();
+ }
+
+ template for (constexpr X x : {{is...}, {is...}, {6, 7, 8}}) {
+ g(x.a);
+ tg<x.a>();
+ }
+
+ template for (constexpr X x : {X{is...}}) {
+ g(x.a);
+ tg<x.a>();
+ }
+}
+
+template <int ...is>
+struct s2 {
+ template <int ...js>
+ void tf() {
+ template for (auto x : {is..., js...}) g(x);
+ template for (X x : {{is...}, {js...}}) g(x.a);
+ template for (constexpr auto x : {is..., js...}) tg<x>();
+ template for (constexpr X x : {{is...}, {js...}}) tg<x.a>();
+ }
+};
+
+void f7() {
+ t3(42, 43, 44);
+ t4<42, 43, 44>();
+ s2<1, 2, 3>().tf<4, 5, 6>();
+}
+
+template <int ...is>
+void t5() {
+ ([] {
+ template for (constexpr auto x : {is}) {
+ g(x);
+ tg<x>();
+ }
+ }(), ...);
+}
+
+void f8() {
+ t5<1, 2, 3>();
+}
+
+int references_enumerating() {
+ int x = 1, y = 2, z = 3;
+ template for (auto& v : {x, y, z}) { ++v; }
+ template for (auto&& v : {x, y, z}) { ++v; }
+ return x + y + z;
+}
+
+// CHECK-LABEL: define {{.*}} void @_Z2f1v()
+// CHECK: entry:
+// CHECK-NEXT: %x = alloca i32, align 4
+// CHECK-NEXT: %x1 = alloca i32, align 4
+// CHECK-NEXT: %x3 = alloca i32, align 4
+// CHECK-NEXT: store i32 1, ptr %x, align 4
+// CHECK-NEXT: %0 = load i32, ptr %x, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %0)
+// CHECK-NEXT: br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT: store i32 2, ptr %x1, align 4
+// CHECK-NEXT: %1 = load i32, ptr %x1, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %1)
+// CHECK-NEXT: br label %expand.next2
+// CHECK: expand.next2:
+// CHECK-NEXT: store i32 3, ptr %x3, align 4
+// CHECK-NEXT: %2 = load i32, ptr %x3, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %2)
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_Z2f2v()
+// CHECK: entry:
+// CHECK-NEXT: %x = alloca i32, align 4
+// CHECK-NEXT: %x1 = alloca ptr, align 8
+// CHECK-NEXT: %x3 = alloca %struct.S, align 4
+// CHECK-NEXT: %agg.tmp = alloca %struct.S, align 4
+// CHECK-NEXT: store i32 1, ptr %x, align 4
+// CHECK-NEXT: %0 = load i32, ptr %x, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %0)
+// CHECK-NEXT: br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT: store ptr @.str, ptr %x1, align 8
+// CHECK-NEXT: %1 = load ptr, ptr %x1, align 8
+// CHECK-NEXT: call void @_Z1gPKc(ptr {{.*}} %1)
+// CHECK-NEXT: br label %expand.next2
+// CHECK: expand.next2:
+// CHECK-NEXT: call void @_ZN1SC1Ei(ptr {{.*}} %x3, i32 {{.*}} 45)
+// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %agg.tmp, ptr align 4 %x3, i64 4, i1 false)
+// CHECK-NEXT: %coerce.dive = getelementptr inbounds nuw %struct.S, ptr %agg.tmp, i32 0, i32 0
+// CHECK-NEXT: %2 = load i32, ptr %coerce.dive, align 4
+// CHECK-NEXT: call void @_Z1g1S(i32 %2)
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_Z2f3v()
+// CHECK: entry:
+// CHECK-NEXT: ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_Z2f4v()
+// CHECK: entry:
+// CHECK-NEXT: %x = alloca i32, align 4
+// CHECK-NEXT: %y = alloca i32, align 4
+// CHECK-NEXT: %y1 = alloca i32, align 4
+// CHECK-NEXT: %x3 = alloca i32, align 4
+// CHECK-NEXT: %y4 = alloca i32, align 4
+// CHECK-NEXT: %y6 = alloca i32, align 4
+// CHECK-NEXT: store i32 1, ptr %x, align 4
+// CHECK-NEXT: store i32 3, ptr %y, align 4
+// CHECK-NEXT: %0 = load i32, ptr %x, align 4
+// CHECK-NEXT: %1 = load i32, ptr %y, align 4
+// CHECK-NEXT: call void @_Z1hii(i32 {{.*}} %0, i32 {{.*}} %1)
+// CHECK-NEXT: br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT: store i32 4, ptr %y1, align 4
+// CHECK-NEXT: %2 = load i32, ptr %x, align 4
+// CHECK-NEXT: %3 = load i32, ptr %y1, align 4
+// CHECK-NEXT: call void @_Z1hii(i32 {{.*}} %2, i32 {{.*}} %3)
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: br label %expand.next2
+// CHECK: expand.next2:
+// CHECK-NEXT: store i32 2, ptr %x3, align 4
+// CHECK-NEXT: store i32 3, ptr %y4, align 4
+// CHECK-NEXT: %4 = load i32, ptr %x3, align 4
+// CHECK-NEXT: %5 = load i32, ptr %y4, align 4
+// CHECK-NEXT: call void @_Z1hii(i32 {{.*}} %4, i32 {{.*}} %5)
+// CHECK-NEXT: br label %expand.next5
+// CHECK: expand.next5:
+// CHECK-NEXT: store i32 4, ptr %y6, align 4
+// CHECK-NEXT: %6 = load i32, ptr %x3, align 4
+// CHECK-NEXT: %7 = load i32, ptr %y6, align 4
+// CHECK-NEXT: call void @_Z1hii(i32 {{.*}} %6, i32 {{.*}} %7)
+// CHECK-NEXT: br label %expand.end7
+// CHECK: expand.end7:
+// CHECK-NEXT: br label %expand.end8
+// CHECK: expand.end8:
+// CHECK-NEXT: ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_Z2f5v()
+// CHECK: entry:
+// CHECK-NEXT: %x = alloca i32, align 4
+// CHECK-NEXT: %x1 = alloca i32, align 4
+// CHECK-NEXT: %x2 = alloca i32, align 4
+// CHECK-NEXT: %x4 = alloca i32, align 4
+// CHECK-NEXT: %x6 = alloca i32, align 4
+// CHECK-NEXT: %x8 = alloca i32, align 4
+// CHECK-NEXT: %x10 = alloca i32, align 4
+// CHECK-NEXT: %x12 = alloca i32, align 4
+// CHECK-NEXT: %x14 = alloca i32, align 4
+// CHECK-NEXT: %x16 = alloca i32, align 4
+// CHECK-NEXT: %x18 = alloca i32, align 4
+// CHECK-NEXT: %x20 = alloca i32, align 4
+// CHECK-NEXT: %x22 = alloca i32, align 4
+// CHECK-NEXT: %x24 = alloca i32, align 4
+// CHECK-NEXT: %x26 = alloca i32, align 4
+// CHECK-NEXT: %x28 = alloca %struct.S, align 4
+// CHECK-NEXT: %x31 = alloca %struct.S, align 4
+// CHECK-NEXT: %x34 = alloca %struct.S, align 4
+// CHECK-NEXT: %x37 = alloca %struct.S, align 4
+// CHECK-NEXT: %x40 = alloca %struct.S, align 4
+// CHECK-NEXT: %x43 = alloca %struct.S, align 4
+// CHECK-NEXT: store i32 1, ptr %x, align 4
+// CHECK-NEXT: %0 = load i32, ptr %x, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %0)
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: store i32 2, ptr %x1, align 4
+// CHECK-NEXT: %1 = load i32, ptr %x1, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %1)
+// CHECK-NEXT: br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT: store i32 3, ptr %x2, align 4
+// CHECK-NEXT: %2 = load i32, ptr %x2, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %2)
+// CHECK-NEXT: br label %expand.next3
+// CHECK: expand.next3:
+// CHECK-NEXT: store i32 4, ptr %x4, align 4
+// CHECK-NEXT: %3 = load i32, ptr %x4, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %3)
+// CHECK-NEXT: br label %expand.end5
+// CHECK: expand.end5:
+// CHECK-NEXT: store i32 5, ptr %x6, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 5)
+// CHECK-NEXT: br label %expand.end7
+// CHECK: expand.end7:
+// CHECK-NEXT: store i32 6, ptr %x8, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 6)
+// CHECK-NEXT: br label %expand.next9
+// CHECK: expand.next9:
+// CHECK-NEXT: store i32 7, ptr %x10, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 7)
+// CHECK-NEXT: br label %expand.next11
+// CHECK: expand.next11:
+// CHECK-NEXT: store i32 8, ptr %x12, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 8)
+// CHECK-NEXT: br label %expand.end13
+// CHECK: expand.end13:
+// CHECK-NEXT: store i32 9, ptr %x14, align 4
+// CHECK-NEXT: %call = call {{.*}} i32 @_Z2tgILi9EEiv()
+// CHECK-NEXT: br label %expand.end15
+// CHECK: expand.end15:
+// CHECK-NEXT: store i32 10, ptr %x16, align 4
+// CHECK-NEXT: br label %expand.next17
+// CHECK: expand.next17:
+// CHECK-NEXT: store i32 11, ptr %x18, align 4
+// CHECK-NEXT: br label %expand.next19
+// CHECK: expand.next19:
+// CHECK-NEXT: store i32 12, ptr %x20, align 4
+// CHECK-NEXT: br label %expand.end21
+// CHECK: expand.end21:
+// CHECK-NEXT: store i32 13, ptr %x22, align 4
+// CHECK-NEXT: %4 = load i32, ptr %x22, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %4)
+// CHECK-NEXT: br label %expand.next23
+// CHECK: expand.next23:
+// CHECK-NEXT: store i32 14, ptr %x24, align 4
+// CHECK-NEXT: %5 = load i32, ptr %x24, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %5)
+// CHECK-NEXT: br label %expand.next25
+// CHECK: expand.next25:
+// CHECK-NEXT: store i32 15, ptr %x26, align 4
+// CHECK-NEXT: %6 = load i32, ptr %x26, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %6)
+// CHECK-NEXT: br label %expand.end27
+// CHECK: expand.end27:
+// CHECK-NEXT: call void @_ZN1SC1Ei(ptr {{.*}} %x28, i32 {{.*}} 16)
+// CHECK-NEXT: %x29 = getelementptr inbounds nuw %struct.S, ptr %x28, i32 0, i32 0
+// CHECK-NEXT: %7 = load i32, ptr %x29, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %7)
+// CHECK-NEXT: br label %expand.next30
+// CHECK: expand.next30:
+// CHECK-NEXT: call void @_ZN1SC1Ei(ptr {{.*}} %x31, i32 {{.*}} 17)
+// CHECK-NEXT: %x32 = getelementptr inbounds nuw %struct.S, ptr %x31, i32 0, i32 0
+// CHECK-NEXT: %8 = load i32, ptr %x32, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %8)
+// CHECK-NEXT: br label %expand.next33
+// CHECK: expand.next33:
+// CHECK-NEXT: call void @_ZN1SC1Ei(ptr {{.*}} %x34, i32 {{.*}} 18)
+// CHECK-NEXT: %x35 = getelementptr inbounds nuw %struct.S, ptr %x34, i32 0, i32 0
+// CHECK-NEXT: %9 = load i32, ptr %x35, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %9)
+// CHECK-NEXT: br label %expand.end36
+// CHECK: expand.end36:
+// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x37, ptr align 4 @__const._Z2f5v.x, i64 4, i1 false)
+// CHECK-NEXT: %call38 = call {{.*}} i32 @_Z2tgILi19EEiv()
+// CHECK-NEXT: br label %expand.next39
+// CHECK: expand.next39:
+// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x40, ptr align 4 @__const._Z2f5v.x.1, i64 4, i1 false)
+// CHECK-NEXT: %call41 = call {{.*}} i32 @_Z2tgILi20EEiv()
+// CHECK-NEXT: br label %expand.next42
+// CHECK: expand.next42:
+// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x43, ptr align 4 @__const._Z2f5v.x.2, i64 4, i1 false)
+// CHECK-NEXT: %call44 = call {{.*}} i32 @_Z2tgILi21EEiv()
+// CHECK-NEXT: br label %expand.end45
+// CHECK: expand.end45:
+// CHECK-NEXT: ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_Z2f6v()
+// CHECK: entry:
+// CHECK-NEXT: %ref.tmp = alloca %struct.s1, align 1
+// CHECK-NEXT: %ref.tmp1 = alloca %struct.s1.0, align 1
+// CHECK-NEXT: %ref.tmp2 = alloca %struct.s1.0, align 1
+// CHECK-NEXT: %ref.tmp3 = alloca %struct.s1, align 1
+// CHECK-NEXT: call void @_Z2t1IiEvv()
+// CHECK-NEXT: call void @_Z2t1IlEvv()
+// CHECK-NEXT: call void @_ZN2s1IlE2tfIlEEvv(ptr {{.*}} %ref.tmp)
+// CHECK-NEXT: call void @_ZN2s1IiE2tfIiEEvv(ptr {{.*}} %ref.tmp1)
+// CHECK-NEXT: call void @_ZN2s1IiE2tfIlEEvv(ptr {{.*}} %ref.tmp2)
+// CHECK-NEXT: call void @_ZN2s1IlE2tfIiEEvv(ptr {{.*}} %ref.tmp3)
+// CHECK-NEXT: call void @_Z2t2I1SEvv()
+// CHECK-NEXT: call void @_Z2t2IA1231_1SEvv()
+// CHECK-NEXT: call void @_Z2t2IPPP1SEvv()
+// CHECK-NEXT: ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_Z2t1IiEvv()
+// CHECK: entry:
+// CHECK-NEXT: %x = alloca i32, align 4
+// CHECK-NEXT: %x1 = alloca i32, align 4
+// CHECK-NEXT: %x2 = alloca i32, align 4
+// CHECK-NEXT: %x4 = alloca i32, align 4
+// CHECK-NEXT: %x6 = alloca i32, align 4
+// CHECK-NEXT: %x8 = alloca i32, align 4
+// CHECK-NEXT: %x10 = alloca i32, align 4
+// CHECK-NEXT: %x12 = alloca i32, align 4
+// CHECK-NEXT: %x14 = alloca i32, align 4
+// CHECK-NEXT: %x16 = alloca i32, align 4
+// CHECK-NEXT: store i32 1, ptr %x, align 4
+// CHECK-NEXT: %0 = load i32, ptr %x, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %0)
+// CHECK-NEXT: br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT: store i32 2, ptr %x1, align 4
+// CHECK-NEXT: %1 = load i32, ptr %x1, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %1)
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: store i32 3, ptr %x2, align 4
+// CHECK-NEXT: %2 = load i32, ptr %x2, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %2)
+// CHECK-NEXT: br label %expand.next3
+// CHECK: expand.next3:
+// CHECK-NEXT: store i32 4, ptr %x4, align 4
+// CHECK-NEXT: %3 = load i32, ptr %x4, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %3)
+// CHECK-NEXT: br label %expand.end5
+// CHECK: expand.end5:
+// CHECK-NEXT: store i32 5, ptr %x6, align 4
+// CHECK-NEXT: %4 = load i32, ptr %x6, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %4)
+// CHECK-NEXT: br label %expand.next7
+// CHECK: expand.next7:
+// CHECK-NEXT: store i32 6, ptr %x8, align 4
+// CHECK-NEXT: %5 = load i32, ptr %x8, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %5)
+// CHECK-NEXT: br label %expand.end9
+// CHECK: expand.end9:
+// CHECK-NEXT: store i32 7, ptr %x10, align 4
+// CHECK-NEXT: br label %expand.next11
+// CHECK: expand.next11:
+// CHECK-NEXT: store i32 8, ptr %x12, align 4
+// CHECK-NEXT: br label %expand.end13
+// CHECK: expand.end13:
+// CHECK-NEXT: store i32 9, ptr %x14, align 4
+// CHECK-NEXT: br label %expand.next15
+// CHECK: expand.next15:
+// CHECK-NEXT: store i32 10, ptr %x16, align 4
+// CHECK-NEXT: br label %expand.end17
+// CHECK: expand.end17:
+// CHECK-NEXT: ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_Z2t1IlEvv()
+// CHECK: entry:
+// CHECK-NEXT: %x = alloca i64, align 8
+// CHECK-NEXT: %x1 = alloca i64, align 8
+// CHECK-NEXT: %x2 = alloca i64, align 8
+// CHECK-NEXT: %x4 = alloca i64, align 8
+// CHECK-NEXT: %x6 = alloca i64, align 8
+// CHECK-NEXT: %x8 = alloca i64, align 8
+// CHECK-NEXT: %x10 = alloca i64, align 8
+// CHECK-NEXT: %x12 = alloca i64, align 8
+// CHECK-NEXT: %x14 = alloca i64, align 8
+// CHECK-NEXT: %x16 = alloca i64, align 8
+// CHECK-NEXT: store i64 1, ptr %x, align 8
+// CHECK-NEXT: %0 = load i64, ptr %x, align 8
+// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %0)
+// CHECK-NEXT: br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT: store i64 2, ptr %x1, align 8
+// CHECK-NEXT: %1 = load i64, ptr %x1, align 8
+// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %1)
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: store i64 3, ptr %x2, align 8
+// CHECK-NEXT: %2 = load i64, ptr %x2, align 8
+// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %2)
+// CHECK-NEXT: br label %expand.next3
+// CHECK: expand.next3:
+// CHECK-NEXT: store i64 4, ptr %x4, align 8
+// CHECK-NEXT: %3 = load i64, ptr %x4, align 8
+// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %3)
+// CHECK-NEXT: br label %expand.end5
+// CHECK: expand.end5:
+// CHECK-NEXT: store i64 5, ptr %x6, align 8
+// CHECK-NEXT: %4 = load i64, ptr %x6, align 8
+// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %4)
+// CHECK-NEXT: br label %expand.next7
+// CHECK: expand.next7:
+// CHECK-NEXT: store i64 6, ptr %x8, align 8
+// CHECK-NEXT: %5 = load i64, ptr %x8, align 8
+// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %5)
+// CHECK-NEXT: br label %expand.end9
+// CHECK: expand.end9:
+// CHECK-NEXT: store i64 7, ptr %x10, align 8
+// CHECK-NEXT: br label %expand.next11
+// CHECK: expand.next11:
+// CHECK-NEXT: store i64 8, ptr %x12, align 8
+// CHECK-NEXT: br label %expand.end13
+// CHECK: expand.end13:
+// CHECK-NEXT: store i64 9, ptr %x14, align 8
+// CHECK-NEXT: br label %expand.next15
+// CHECK: expand.next15:
+// CHECK-NEXT: store i64 10, ptr %x16, align 8
+// CHECK-NEXT: br label %expand.end17
+// CHECK: expand.end17:
+// CHECK-NEXT: ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_ZN2s1IlE2tfIlEEvv(ptr {{.*}} %this)
+// CHECK: entry:
+// CHECK-NEXT: %this.addr = alloca ptr, align 8
+// CHECK-NEXT: %x = alloca i64, align 8
+// CHECK-NEXT: %x2 = alloca i64, align 8
+// CHECK-NEXT: %x3 = alloca i64, align 8
+// CHECK-NEXT: %x5 = alloca i64, align 8
+// CHECK-NEXT: %x7 = alloca i64, align 8
+// CHECK-NEXT: %x9 = alloca i64, align 8
+// CHECK-NEXT: %x11 = alloca i64, align 8
+// CHECK-NEXT: %x13 = alloca i64, align 8
+// CHECK-NEXT: %x15 = alloca i64, align 8
+// CHECK-NEXT: %x17 = alloca i64, align 8
+// CHECK-NEXT: %x19 = alloca i64, align 8
+// CHECK-NEXT: %x21 = alloca i64, align 8
+// CHECK-NEXT: %x23 = alloca i64, align 8
+// CHECK-NEXT: %x25 = alloca i64, align 8
+// CHECK-NEXT: %x27 = alloca i64, align 8
+// CHECK-NEXT: %x29 = alloca i64, align 8
+// CHECK-NEXT: %x31 = alloca i64, align 8
+// CHECK-NEXT: %x33 = alloca i64, align 8
+// CHECK-NEXT: store ptr %this, ptr %this.addr, align 8
+// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-NEXT: store i64 1, ptr %x, align 8
+// CHECK-NEXT: %0 = load i64, ptr %x, align 8
+// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %0)
+// CHECK-NEXT: br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT: store i64 2, ptr %x2, align 8
+// CHECK-NEXT: %1 = load i64, ptr %x2, align 8
+// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %1)
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: store i64 3, ptr %x3, align 8
+// CHECK-NEXT: %2 = load i64, ptr %x3, align 8
+// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %2)
+// CHECK-NEXT: br label %expand.next4
+// CHECK: expand.next4:
+// CHECK-NEXT: store i64 4, ptr %x5, align 8
+// CHECK-NEXT: %3 = load i64, ptr %x5, align 8
+// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %3)
+// CHECK-NEXT: br label %expand.end6
+// CHECK: expand.end6:
+// CHECK-NEXT: store i64 5, ptr %x7, align 8
+// CHECK-NEXT: %4 = load i64, ptr %x7, align 8
+// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %4)
+// CHECK-NEXT: br label %expand.next8
+// CHECK: expand.next8:
+// CHECK-NEXT: store i64 6, ptr %x9, align 8
+// CHECK-NEXT: %5 = load i64, ptr %x9, align 8
+// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %5)
+// CHECK-NEXT: br label %expand.end10
+// CHECK: expand.end10:
+// CHECK-NEXT: store i64 7, ptr %x11, align 8
+// CHECK-NEXT: %6 = load i64, ptr %x11, align 8
+// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %6)
+// CHECK-NEXT: br label %expand.next12
+// CHECK: expand.next12:
+// CHECK-NEXT: store i64 8, ptr %x13, align 8
+// CHECK-NEXT: %7 = load i64, ptr %x13, align 8
+// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %7)
+// CHECK-NEXT: br label %expand.end14
+// CHECK: expand.end14:
+// CHECK-NEXT: store i64 9, ptr %x15, align 8
+// CHECK-NEXT: %8 = load i64, ptr %x15, align 8
+// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %8)
+// CHECK-NEXT: br label %expand.next16
+// CHECK: expand.next16:
+// CHECK-NEXT: store i64 10, ptr %x17, align 8
+// CHECK-NEXT: %9 = load i64, ptr %x17, align 8
+// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %9)
+// CHECK-NEXT: br label %expand.end18
+// CHECK: expand.end18:
+// CHECK-NEXT: store i64 11, ptr %x19, align 8
+// CHECK-NEXT: %10 = load i64, ptr %x19, align 8
+// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %10)
+// CHECK-NEXT: br label %expand.next20
+// CHECK: expand.next20:
+// CHECK-NEXT: store i64 12, ptr %x21, align 8
+// CHECK-NEXT: %11 = load i64, ptr %x21, align 8
+// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %11)
+// CHECK-NEXT: br label %expand.end22
+// CHECK: expand.end22:
+// CHECK-NEXT: store i64 13, ptr %x23, align 8
+// CHECK-NEXT: br label %expand.next24
+// CHECK: expand.next24:
+// CHECK-NEXT: store i64 14, ptr %x25, align 8
+// CHECK-NEXT: br label %expand.end26
+// CHECK: expand.end26:
+// CHECK-NEXT: store i64 15, ptr %x27, align 8
+// CHECK-NEXT: br label %expand.next28
+// CHECK: expand.next28:
+// CHECK-NEXT: store i64 16, ptr %x29, align 8
+// CHECK-NEXT: br label %expand.end30
+// CHECK: expand.end30:
+// CHECK-NEXT: store i64 17, ptr %x31, align 8
+// CHECK-NEXT: br label %expand.next32
+// CHECK: expand.next32:
+// CHECK-NEXT: store i64 18, ptr %x33, align 8
+// CHECK-NEXT: br label %expand.end34
+// CHECK: expand.end34:
+// CHECK-NEXT: ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_ZN2s1IiE2tfIiEEvv(ptr {{.*}} %this)
+// CHECK: entry:
+// CHECK-NEXT: %this.addr = alloca ptr, align 8
+// CHECK-NEXT: %x = alloca i32, align 4
+// CHECK-NEXT: %x2 = alloca i32, align 4
+// CHECK-NEXT: %x3 = alloca i32, align 4
+// CHECK-NEXT: %x5 = alloca i32, align 4
+// CHECK-NEXT: %x7 = alloca i32, align 4
+// CHECK-NEXT: %x9 = alloca i32, align 4
+// CHECK-NEXT: %x11 = alloca i32, align 4
+// CHECK-NEXT: %x13 = alloca i32, align 4
+// CHECK-NEXT: %x15 = alloca i32, align 4
+// CHECK-NEXT: %x17 = alloca i32, align 4
+// CHECK-NEXT: %x19 = alloca i32, align 4
+// CHECK-NEXT: %x21 = alloca i32, align 4
+// CHECK-NEXT: %x23 = alloca i32, align 4
+// CHECK-NEXT: %x25 = alloca i32, align 4
+// CHECK-NEXT: %x27 = alloca i32, align 4
+// CHECK-NEXT: %x29 = alloca i32, align 4
+// CHECK-NEXT: %x31 = alloca i32, align 4
+// CHECK-NEXT: %x33 = alloca i32, align 4
+// CHECK-NEXT: store ptr %this, ptr %this.addr, align 8
+// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-NEXT: store i32 1, ptr %x, align 4
+// CHECK-NEXT: %0 = load i32, ptr %x, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %0)
+// CHECK-NEXT: br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT: store i32 2, ptr %x2, align 4
+// CHECK-NEXT: %1 = load i32, ptr %x2, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %1)
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: store i32 3, ptr %x3, align 4
+// CHECK-NEXT: %2 = load i32, ptr %x3, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %2)
+// CHECK-NEXT: br label %expand.next4
+// CHECK: expand.next4:
+// CHECK-NEXT: store i32 4, ptr %x5, align 4
+// CHECK-NEXT: %3 = load i32, ptr %x5, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %3)
+// CHECK-NEXT: br label %expand.end6
+// CHECK: expand.end6:
+// CHECK-NEXT: store i32 5, ptr %x7, align 4
+// CHECK-NEXT: %4 = load i32, ptr %x7, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %4)
+// CHECK-NEXT: br label %expand.next8
+// CHECK: expand.next8:
+// CHECK-NEXT: store i32 6, ptr %x9, align 4
+// CHECK-NEXT: %5 = load i32, ptr %x9, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %5)
+// CHECK-NEXT: br label %expand.end10
+// CHECK: expand.end10:
+// CHECK-NEXT: store i32 7, ptr %x11, align 4
+// CHECK-NEXT: %6 = load i32, ptr %x11, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %6)
+// CHECK-NEXT: br label %expand.next12
+// CHECK: expand.next12:
+// CHECK-NEXT: store i32 8, ptr %x13, align 4
+// CHECK-NEXT: %7 = load i32, ptr %x13, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %7)
+// CHECK-NEXT: br label %expand.end14
+// CHECK: expand.end14:
+// CHECK-NEXT: store i32 9, ptr %x15, align 4
+// CHECK-NEXT: %8 = load i32, ptr %x15, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %8)
+// CHECK-NEXT: br label %expand.next16
+// CHECK: expand.next16:
+// CHECK-NEXT: store i32 10, ptr %x17, align 4
+// CHECK-NEXT: %9 = load i32, ptr %x17, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %9)
+// CHECK-NEXT: br label %expand.end18
+// CHECK: expand.end18:
+// CHECK-NEXT: store i32 11, ptr %x19, align 4
+// CHECK-NEXT: %10 = load i32, ptr %x19, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %10)
+// CHECK-NEXT: br label %expand.next20
+// CHECK: expand.next20:
+// CHECK-NEXT: store i32 12, ptr %x21, align 4
+// CHECK-NEXT: %11 = load i32, ptr %x21, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %11)
+// CHECK-NEXT: br label %expand.end22
+// CHECK: expand.end22:
+// CHECK-NEXT: store i32 13, ptr %x23, align 4
+// CHECK-NEXT: br label %expand.next24
+// CHECK: expand.next24:
+// CHECK-NEXT: store i32 14, ptr %x25, align 4
+// CHECK-NEXT: br label %expand.end26
+// CHECK: expand.end26:
+// CHECK-NEXT: store i32 15, ptr %x27, align 4
+// CHECK-NEXT: br label %expand.next28
+// CHECK: expand.next28:
+// CHECK-NEXT: store i32 16, ptr %x29, align 4
+// CHECK-NEXT: br label %expand.end30
+// CHECK: expand.end30:
+// CHECK-NEXT: store i32 17, ptr %x31, align 4
+// CHECK-NEXT: br label %expand.next32
+// CHECK: expand.next32:
+// CHECK-NEXT: store i32 18, ptr %x33, align 4
+// CHECK-NEXT: br label %expand.end34
+// CHECK: expand.end34:
+// CHECK-NEXT: ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_ZN2s1IiE2tfIlEEvv(ptr {{.*}} %this)
+// CHECK: entry:
+// CHECK-NEXT: %this.addr = alloca ptr, align 8
+// CHECK-NEXT: %x = alloca i64, align 8
+// CHECK-NEXT: %x2 = alloca i64, align 8
+// CHECK-NEXT: %x3 = alloca i32, align 4
+// CHECK-NEXT: %x5 = alloca i32, align 4
+// CHECK-NEXT: %x7 = alloca i32, align 4
+// CHECK-NEXT: %x9 = alloca i32, align 4
+// CHECK-NEXT: %x11 = alloca i64, align 8
+// CHECK-NEXT: %x13 = alloca i64, align 8
+// CHECK-NEXT: %x15 = alloca i64, align 8
+// CHECK-NEXT: %x17 = alloca i64, align 8
+// CHECK-NEXT: %x19 = alloca i32, align 4
+// CHECK-NEXT: %x21 = alloca i64, align 8
+// CHECK-NEXT: %x23 = alloca i32, align 4
+// CHECK-NEXT: %x25 = alloca i32, align 4
+// CHECK-NEXT: %x27 = alloca i64, align 8
+// CHECK-NEXT: %x29 = alloca i64, align 8
+// CHECK-NEXT: %x31 = alloca i64, align 8
+// CHECK-NEXT: %x33 = alloca i32, align 4
+// CHECK-NEXT: store ptr %this, ptr %this.addr, align 8
+// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-NEXT: store i64 1, ptr %x, align 8
+// CHECK-NEXT: %0 = load i64, ptr %x, align 8
+// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %0)
+// CHECK-NEXT: br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT: store i64 2, ptr %x2, align 8
+// CHECK-NEXT: %1 = load i64, ptr %x2, align 8
+// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %1)
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: store i32 3, ptr %x3, align 4
+// CHECK-NEXT: %2 = load i32, ptr %x3, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %2)
+// CHECK-NEXT: br label %expand.next4
+// CHECK: expand.next4:
+// CHECK-NEXT: store i32 4, ptr %x5, align 4
+// CHECK-NEXT: %3 = load i32, ptr %x5, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %3)
+// CHECK-NEXT: br label %expand.end6
+// CHECK: expand.end6:
+// CHECK-NEXT: store i32 5, ptr %x7, align 4
+// CHECK-NEXT: %4 = load i32, ptr %x7, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %4)
+// CHECK-NEXT: br label %expand.next8
+// CHECK: expand.next8:
+// CHECK-NEXT: store i32 6, ptr %x9, align 4
+// CHECK-NEXT: %5 = load i32, ptr %x9, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %5)
+// CHECK-NEXT: br label %expand.end10
+// CHECK: expand.end10:
+// CHECK-NEXT: store i64 7, ptr %x11, align 8
+// CHECK-NEXT: %6 = load i64, ptr %x11, align 8
+// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %6)
+// CHECK-NEXT: br label %expand.next12
+// CHECK: expand.next12:
+// CHECK-NEXT: store i64 8, ptr %x13, align 8
+// CHECK-NEXT: %7 = load i64, ptr %x13, align 8
+// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %7)
+// CHECK-NEXT: br label %expand.end14
+// CHECK: expand.end14:
+// CHECK-NEXT: store i64 9, ptr %x15, align 8
+// CHECK-NEXT: %8 = load i64, ptr %x15, align 8
+// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %8)
+// CHECK-NEXT: br label %expand.next16
+// CHECK: expand.next16:
+// CHECK-NEXT: store i64 10, ptr %x17, align 8
+// CHECK-NEXT: %9 = load i64, ptr %x17, align 8
+// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %9)
+// CHECK-NEXT: br label %expand.end18
+// CHECK: expand.end18:
+// CHECK-NEXT: store i32 11, ptr %x19, align 4
+// CHECK-NEXT: %10 = load i32, ptr %x19, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %10)
+// CHECK-NEXT: br label %expand.next20
+// CHECK: expand.next20:
+// CHECK-NEXT: store i64 12, ptr %x21, align 8
+// CHECK-NEXT: %11 = load i64, ptr %x21, align 8
+// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %11)
+// CHECK-NEXT: br label %expand.end22
+// CHECK: expand.end22:
+// CHECK-NEXT: store i32 13, ptr %x23, align 4
+// CHECK-NEXT: br label %expand.next24
+// CHECK: expand.next24:
+// CHECK-NEXT: store i32 14, ptr %x25, align 4
+// CHECK-NEXT: br label %expand.end26
+// CHECK: expand.end26:
+// CHECK-NEXT: store i64 15, ptr %x27, align 8
+// CHECK-NEXT: br label %expand.next28
+// CHECK: expand.next28:
+// CHECK-NEXT: store i64 16, ptr %x29, align 8
+// CHECK-NEXT: br label %expand.end30
+// CHECK: expand.end30:
+// CHECK-NEXT: store i64 17, ptr %x31, align 8
+// CHECK-NEXT: br label %expand.next32
+// CHECK: expand.next32:
+// CHECK-NEXT: store i32 18, ptr %x33, align 4
+// CHECK-NEXT: br label %expand.end34
+// CHECK: expand.end34:
+// CHECK-NEXT: ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_ZN2s1IlE2tfIiEEvv(ptr {{.*}} %this)
+// CHECK: entry:
+// CHECK-NEXT: %this.addr = alloca ptr, align 8
+// CHECK-NEXT: %x = alloca i32, align 4
+// CHECK-NEXT: %x2 = alloca i32, align 4
+// CHECK-NEXT: %x3 = alloca i64, align 8
+// CHECK-NEXT: %x5 = alloca i64, align 8
+// CHECK-NEXT: %x7 = alloca i64, align 8
+// CHECK-NEXT: %x9 = alloca i64, align 8
+// CHECK-NEXT: %x11 = alloca i32, align 4
+// CHECK-NEXT: %x13 = alloca i32, align 4
+// CHECK-NEXT: %x15 = alloca i32, align 4
+// CHECK-NEXT: %x17 = alloca i32, align 4
+// CHECK-NEXT: %x19 = alloca i64, align 8
+// CHECK-NEXT: %x21 = alloca i32, align 4
+// CHECK-NEXT: %x23 = alloca i64, align 8
+// CHECK-NEXT: %x25 = alloca i64, align 8
+// CHECK-NEXT: %x27 = alloca i32, align 4
+// CHECK-NEXT: %x29 = alloca i32, align 4
+// CHECK-NEXT: %x31 = alloca i32, align 4
+// CHECK-NEXT: %x33 = alloca i64, align 8
+// CHECK-NEXT: store ptr %this, ptr %this.addr, align 8
+// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-NEXT: store i32 1, ptr %x, align 4
+// CHECK-NEXT: %0 = load i32, ptr %x, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %0)
+// CHECK-NEXT: br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT: store i32 2, ptr %x2, align 4
+// CHECK-NEXT: %1 = load i32, ptr %x2, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %1)
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: store i64 3, ptr %x3, align 8
+// CHECK-NEXT: %2 = load i64, ptr %x3, align 8
+// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %2)
+// CHECK-NEXT: br label %expand.next4
+// CHECK: expand.next4:
+// CHECK-NEXT: store i64 4, ptr %x5, align 8
+// CHECK-NEXT: %3 = load i64, ptr %x5, align 8
+// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %3)
+// CHECK-NEXT: br label %expand.end6
+// CHECK: expand.end6:
+// CHECK-NEXT: store i64 5, ptr %x7, align 8
+// CHECK-NEXT: %4 = load i64, ptr %x7, align 8
+// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %4)
+// CHECK-NEXT: br label %expand.next8
+// CHECK: expand.next8:
+// CHECK-NEXT: store i64 6, ptr %x9, align 8
+// CHECK-NEXT: %5 = load i64, ptr %x9, align 8
+// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %5)
+// CHECK-NEXT: br label %expand.end10
+// CHECK: expand.end10:
+// CHECK-NEXT: store i32 7, ptr %x11, align 4
+// CHECK-NEXT: %6 = load i32, ptr %x11, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %6)
+// CHECK-NEXT: br label %expand.next12
+// CHECK: expand.next12:
+// CHECK-NEXT: store i32 8, ptr %x13, align 4
+// CHECK-NEXT: %7 = load i32, ptr %x13, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %7)
+// CHECK-NEXT: br label %expand.end14
+// CHECK: expand.end14:
+// CHECK-NEXT: store i32 9, ptr %x15, align 4
+// CHECK-NEXT: %8 = load i32, ptr %x15, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %8)
+// CHECK-NEXT: br label %expand.next16
+// CHECK: expand.next16:
+// CHECK-NEXT: store i32 10, ptr %x17, align 4
+// CHECK-NEXT: %9 = load i32, ptr %x17, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %9)
+// CHECK-NEXT: br label %expand.end18
+// CHECK: expand.end18:
+// CHECK-NEXT: store i64 11, ptr %x19, align 8
+// CHECK-NEXT: %10 = load i64, ptr %x19, align 8
+// CHECK-NEXT: call void @_Z1gl(i64 {{.*}} %10)
+// CHECK-NEXT: br label %expand.next20
+// CHECK: expand.next20:
+// CHECK-NEXT: store i32 12, ptr %x21, align 4
+// CHECK-NEXT: %11 = load i32, ptr %x21, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %11)
+// CHECK-NEXT: br label %expand.end22
+// CHECK: expand.end22:
+// CHECK-NEXT: store i64 13, ptr %x23, align 8
+// CHECK-NEXT: br label %expand.next24
+// CHECK: expand.next24:
+// CHECK-NEXT: store i64 14, ptr %x25, align 8
+// CHECK-NEXT: br label %expand.end26
+// CHECK: expand.end26:
+// CHECK-NEXT: store i32 15, ptr %x27, align 4
+// CHECK-NEXT: br label %expand.next28
+// CHECK: expand.next28:
+// CHECK-NEXT: store i32 16, ptr %x29, align 4
+// CHECK-NEXT: br label %expand.end30
+// CHECK: expand.end30:
+// CHECK-NEXT: store i32 17, ptr %x31, align 4
+// CHECK-NEXT: br label %expand.next32
+// CHECK: expand.next32:
+// CHECK-NEXT: store i64 18, ptr %x33, align 8
+// CHECK-NEXT: br label %expand.end34
+// CHECK: expand.end34:
+// CHECK-NEXT: ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_Z2f7v()
+// CHECK: entry:
+// CHECK-NEXT: %ref.tmp = alloca %struct.s2, align 1
+// CHECK-NEXT: call void @_Z2t3IJiiiEEvDpT_(i32 {{.*}} 42, i32 {{.*}} 43, i32 {{.*}} 44)
+// CHECK-NEXT: call void @_Z2t4IJLi42ELi43ELi44EEEvv()
+// CHECK-NEXT: call void @_ZN2s2IJLi1ELi2ELi3EEE2tfIJLi4ELi5ELi6EEEEvv(ptr {{.*}} %ref.tmp)
+// CHECK-NEXT: ret void
+
+// CHECK-LABEL: define {{.*}} void @_Z2t3IJiiiEEvDpT_(i32 {{.*}} %ts, i32 {{.*}} %ts1, i32 {{.*}} %ts3)
+// CHECK: entry:
+// CHECK-NEXT: %ts.addr = alloca i32, align 4
+// CHECK-NEXT: %ts.addr2 = alloca i32, align 4
+// CHECK-NEXT: %ts.addr4 = alloca i32, align 4
+// CHECK-NEXT: %x = alloca i32, align 4
+// CHECK-NEXT: %x5 = alloca i32, align 4
+// CHECK-NEXT: %x7 = alloca i32, align 4
+// CHECK-NEXT: %x8 = alloca i32, align 4
+// CHECK-NEXT: %x10 = alloca i32, align 4
+// CHECK-NEXT: %x12 = alloca i32, align 4
+// CHECK-NEXT: %x14 = alloca i32, align 4
+// CHECK-NEXT: %x16 = alloca i32, align 4
+// CHECK-NEXT: %x18 = alloca i32, align 4
+// CHECK-NEXT: %x20 = alloca i32, align 4
+// CHECK-NEXT: %x22 = alloca i32, align 4
+// CHECK-NEXT: %x24 = alloca i32, align 4
+// CHECK-NEXT: %x26 = alloca i32, align 4
+// CHECK-NEXT: %x28 = alloca i32, align 4
+// CHECK-NEXT: %x30 = alloca i32, align 4
+// CHECK-NEXT: %x32 = alloca i32, align 4
+// CHECK-NEXT: %x34 = alloca i32, align 4
+// CHECK-NEXT: %x36 = alloca i32, align 4
+// CHECK-NEXT: %x38 = alloca i32, align 4
+// CHECK-NEXT: %x40 = alloca i32, align 4
+// CHECK-NEXT: %x42 = alloca %struct.X, align 4
+// CHECK-NEXT: %x45 = alloca %struct.X, align 4
+// CHECK-NEXT: %x51 = alloca %struct.X, align 4
+// CHECK-NEXT: %x54 = alloca %struct.X, align 4
+// CHECK-NEXT: store i32 %ts, ptr %ts.addr, align 4
+// CHECK-NEXT: store i32 %ts1, ptr %ts.addr2, align 4
+// CHECK-NEXT: store i32 %ts3, ptr %ts.addr4, align 4
+// CHECK-NEXT: %0 = load i32, ptr %ts.addr, align 4
+// CHECK-NEXT: store i32 %0, ptr %x, align 4
+// CHECK-NEXT: %1 = load i32, ptr %x, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %1)
+// CHECK-NEXT: br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT: %2 = load i32, ptr %ts.addr2, align 4
+// CHECK-NEXT: store i32 %2, ptr %x5, align 4
+// CHECK-NEXT: %3 = load i32, ptr %x5, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %3)
+// CHECK-NEXT: br label %expand.next6
+// CHECK: expand.next6:
+// CHECK-NEXT: %4 = load i32, ptr %ts.addr4, align 4
+// CHECK-NEXT: store i32 %4, ptr %x7, align 4
+// CHECK-NEXT: %5 = load i32, ptr %x7, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %5)
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: store i32 1, ptr %x8, align 4
+// CHECK-NEXT: %6 = load i32, ptr %x8, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %6)
+// CHECK-NEXT: br label %expand.next9
+// CHECK: expand.next9:
+// CHECK-NEXT: %7 = load i32, ptr %ts.addr, align 4
+// CHECK-NEXT: store i32 %7, ptr %x10, align 4
+// CHECK-NEXT: %8 = load i32, ptr %x10, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %8)
+// CHECK-NEXT: br label %expand.next11
+// CHECK: expand.next11:
+// CHECK-NEXT: %9 = load i32, ptr %ts.addr2, align 4
+// CHECK-NEXT: store i32 %9, ptr %x12, align 4
+// CHECK-NEXT: %10 = load i32, ptr %x12, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %10)
+// CHECK-NEXT: br label %expand.next13
+// CHECK: expand.next13:
+// CHECK-NEXT: %11 = load i32, ptr %ts.addr4, align 4
+// CHECK-NEXT: store i32 %11, ptr %x14, align 4
+// CHECK-NEXT: %12 = load i32, ptr %x14, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %12)
+// CHECK-NEXT: br label %expand.next15
+// CHECK: expand.next15:
+// CHECK-NEXT: store i32 2, ptr %x16, align 4
+// CHECK-NEXT: %13 = load i32, ptr %x16, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %13)
+// CHECK-NEXT: br label %expand.next17
+// CHECK: expand.next17:
+// CHECK-NEXT: %14 = load i32, ptr %ts.addr, align 4
+// CHECK-NEXT: store i32 %14, ptr %x18, align 4
+// CHECK-NEXT: %15 = load i32, ptr %x18, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %15)
+// CHECK-NEXT: br label %expand.next19
+// CHECK: expand.next19:
+// CHECK-NEXT: %16 = load i32, ptr %ts.addr2, align 4
+// CHECK-NEXT: store i32 %16, ptr %x20, align 4
+// CHECK-NEXT: %17 = load i32, ptr %x20, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %17)
+// CHECK-NEXT: br label %expand.next21
+// CHECK: expand.next21:
+// CHECK-NEXT: %18 = load i32, ptr %ts.addr4, align 4
+// CHECK-NEXT: store i32 %18, ptr %x22, align 4
+// CHECK-NEXT: %19 = load i32, ptr %x22, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %19)
+// CHECK-NEXT: br label %expand.next23
+// CHECK: expand.next23:
+// CHECK-NEXT: store i32 3, ptr %x24, align 4
+// CHECK-NEXT: %20 = load i32, ptr %x24, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %20)
+// CHECK-NEXT: br label %expand.end25
+// CHECK: expand.end25:
+// CHECK-NEXT: store i32 4, ptr %x26, align 4
+// CHECK-NEXT: %21 = load i32, ptr %x26, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %21)
+// CHECK-NEXT: br label %expand.next27
+// CHECK: expand.next27:
+// CHECK-NEXT: %22 = load i32, ptr %ts.addr, align 4
+// CHECK-NEXT: store i32 %22, ptr %x28, align 4
+// CHECK-NEXT: %23 = load i32, ptr %x28, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %23)
+// CHECK-NEXT: br label %expand.next29
+// CHECK: expand.next29:
+// CHECK-NEXT: %24 = load i32, ptr %ts.addr2, align 4
+// CHECK-NEXT: store i32 %24, ptr %x30, align 4
+// CHECK-NEXT: %25 = load i32, ptr %x30, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %25)
+// CHECK-NEXT: br label %expand.next31
+// CHECK: expand.next31:
+// CHECK-NEXT: %26 = load i32, ptr %ts.addr4, align 4
+// CHECK-NEXT: store i32 %26, ptr %x32, align 4
+// CHECK-NEXT: %27 = load i32, ptr %x32, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %27)
+// CHECK-NEXT: br label %expand.next33
+// CHECK: expand.next33:
+// CHECK-NEXT: %28 = load i32, ptr %ts.addr, align 4
+// CHECK-NEXT: store i32 %28, ptr %x34, align 4
+// CHECK-NEXT: %29 = load i32, ptr %x34, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %29)
+// CHECK-NEXT: br label %expand.next35
+// CHECK: expand.next35:
+// CHECK-NEXT: %30 = load i32, ptr %ts.addr2, align 4
+// CHECK-NEXT: store i32 %30, ptr %x36, align 4
+// CHECK-NEXT: %31 = load i32, ptr %x36, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %31)
+// CHECK-NEXT: br label %expand.next37
+// CHECK: expand.next37:
+// CHECK-NEXT: %32 = load i32, ptr %ts.addr4, align 4
+// CHECK-NEXT: store i32 %32, ptr %x38, align 4
+// CHECK-NEXT: %33 = load i32, ptr %x38, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %33)
+// CHECK-NEXT: br label %expand.next39
+// CHECK: expand.next39:
+// CHECK-NEXT: store i32 5, ptr %x40, align 4
+// CHECK-NEXT: %34 = load i32, ptr %x40, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %34)
+// CHECK-NEXT: br label %expand.end41
+// CHECK: expand.end41:
+// CHECK-NEXT: %a = getelementptr inbounds nuw %struct.X, ptr %x42, i32 0, i32 0
+// CHECK-NEXT: %35 = load i32, ptr %ts.addr, align 4
+// CHECK-NEXT: store i32 %35, ptr %a, align 4
+// CHECK-NEXT: %b = getelementptr inbounds nuw %struct.X, ptr %x42, i32 0, i32 1
+// CHECK-NEXT: %36 = load i32, ptr %ts.addr2, align 4
+// CHECK-NEXT: store i32 %36, ptr %b, align 4
+// CHECK-NEXT: %c = getelementptr inbounds nuw %struct.X, ptr %x42, i32 0, i32 2
+// CHECK-NEXT: %37 = load i32, ptr %ts.addr4, align 4
+// CHECK-NEXT: store i32 %37, ptr %c, align 4
+// CHECK-NEXT: %a43 = getelementptr inbounds nuw %struct.X, ptr %x42, i32 0, i32 0
+// CHECK-NEXT: %38 = load i32, ptr %a43, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %38)
+// CHECK-NEXT: br label %expand.next44
+// CHECK: expand.next44:
+// CHECK-NEXT: %a46 = getelementptr inbounds nuw %struct.X, ptr %x45, i32 0, i32 0
+// CHECK-NEXT: %39 = load i32, ptr %ts.addr, align 4
+// CHECK-NEXT: store i32 %39, ptr %a46, align 4
+// CHECK-NEXT: %b47 = getelementptr inbounds nuw %struct.X, ptr %x45, i32 0, i32 1
+// CHECK-NEXT: %40 = load i32, ptr %ts.addr2, align 4
+// CHECK-NEXT: store i32 %40, ptr %b47, align 4
+// CHECK-NEXT: %c48 = getelementptr inbounds nuw %struct.X, ptr %x45, i32 0, i32 2
+// CHECK-NEXT: %41 = load i32, ptr %ts.addr4, align 4
+// CHECK-NEXT: store i32 %41, ptr %c48, align 4
+// CHECK-NEXT: %a49 = getelementptr inbounds nuw %struct.X, ptr %x45, i32 0, i32 0
+// CHECK-NEXT: %42 = load i32, ptr %a49, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %42)
+// CHECK-NEXT: br label %expand.next50
+// CHECK: expand.next50:
+// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x51, ptr align 4 @__const._Z2t3IJiiiEEvDpT_.x, i64 12, i1 false)
+// CHECK-NEXT: %a52 = getelementptr inbounds nuw %struct.X, ptr %x51, i32 0, i32 0
+// CHECK-NEXT: %43 = load i32, ptr %a52, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %43)
+// CHECK-NEXT: br label %expand.end53
+// CHECK: expand.end53:
+// CHECK-NEXT: %a55 = getelementptr inbounds nuw %struct.X, ptr %x54, i32 0, i32 0
+// CHECK-NEXT: %44 = load i32, ptr %ts.addr, align 4
+// CHECK-NEXT: store i32 %44, ptr %a55, align 4
+// CHECK-NEXT: %b56 = getelementptr inbounds nuw %struct.X, ptr %x54, i32 0, i32 1
+// CHECK-NEXT: %45 = load i32, ptr %ts.addr2, align 4
+// CHECK-NEXT: store i32 %45, ptr %b56, align 4
+// CHECK-NEXT: %c57 = getelementptr inbounds nuw %struct.X, ptr %x54, i32 0, i32 2
+// CHECK-NEXT: %46 = load i32, ptr %ts.addr4, align 4
+// CHECK-NEXT: store i32 %46, ptr %c57, align 4
+// CHECK-NEXT: %a58 = getelementptr inbounds nuw %struct.X, ptr %x54, i32 0, i32 0
+// CHECK-NEXT: %47 = load i32, ptr %a58, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %47)
+// CHECK-NEXT: br label %expand.end59
+// CHECK: expand.end59:
+// CHECK-NEXT: ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_Z2t4IJLi42ELi43ELi44EEEvv()
+// CHECK: entry:
+// CHECK-NEXT: %x = alloca i32, align 4
+// CHECK-NEXT: %x1 = alloca i32, align 4
+// CHECK-NEXT: %x4 = alloca i32, align 4
+// CHECK-NEXT: %x6 = alloca i32, align 4
+// CHECK-NEXT: %x9 = alloca i32, align 4
+// CHECK-NEXT: %x12 = alloca i32, align 4
+// CHECK-NEXT: %x15 = alloca i32, align 4
+// CHECK-NEXT: %x18 = alloca i32, align 4
+// CHECK-NEXT: %x21 = alloca i32, align 4
+// CHECK-NEXT: %x24 = alloca i32, align 4
+// CHECK-NEXT: %x27 = alloca i32, align 4
+// CHECK-NEXT: %x30 = alloca i32, align 4
+// CHECK-NEXT: %x33 = alloca i32, align 4
+// CHECK-NEXT: %x36 = alloca i32, align 4
+// CHECK-NEXT: %x39 = alloca i32, align 4
+// CHECK-NEXT: %x42 = alloca i32, align 4
+// CHECK-NEXT: %x45 = alloca i32, align 4
+// CHECK-NEXT: %x48 = alloca i32, align 4
+// CHECK-NEXT: %x51 = alloca i32, align 4
+// CHECK-NEXT: %x54 = alloca i32, align 4
+// CHECK-NEXT: %x57 = alloca %struct.X, align 4
+// CHECK-NEXT: %x60 = alloca %struct.X, align 4
+// CHECK-NEXT: %x63 = alloca %struct.X, align 4
+// CHECK-NEXT: %x66 = alloca %struct.X, align 4
+// CHECK-NEXT: store i32 42, ptr %x, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 42)
+// CHECK-NEXT: %call = call {{.*}} i32 @_Z2tgILi42EEiv()
+// CHECK-NEXT: br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT: store i32 43, ptr %x1, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 43)
+// CHECK-NEXT: %call2 = call {{.*}} i32 @_Z2tgILi43EEiv()
+// CHECK-NEXT: br label %expand.next3
+// CHECK: expand.next3:
+// CHECK-NEXT: store i32 44, ptr %x4, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 44)
+// CHECK-NEXT: %call5 = call {{.*}} i32 @_Z2tgILi44EEiv()
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: store i32 1, ptr %x6, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 1)
+// CHECK-NEXT: %call7 = call {{.*}} i32 @_Z2tgILi1EEiv()
+// CHECK-NEXT: br label %expand.next8
+// CHECK: expand.next8:
+// CHECK-NEXT: store i32 42, ptr %x9, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 42)
+// CHECK-NEXT: %call10 = call {{.*}} i32 @_Z2tgILi42EEiv()
+// CHECK-NEXT: br label %expand.next11
+// CHECK: expand.next11:
+// CHECK-NEXT: store i32 43, ptr %x12, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 43)
+// CHECK-NEXT: %call13 = call {{.*}} i32 @_Z2tgILi43EEiv()
+// CHECK-NEXT: br label %expand.next14
+// CHECK: expand.next14:
+// CHECK-NEXT: store i32 44, ptr %x15, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 44)
+// CHECK-NEXT: %call16 = call {{.*}} i32 @_Z2tgILi44EEiv()
+// CHECK-NEXT: br label %expand.next17
+// CHECK: expand.next17:
+// CHECK-NEXT: store i32 2, ptr %x18, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 2)
+// CHECK-NEXT: %call19 = call {{.*}} i32 @_Z2tgILi2EEiv()
+// CHECK-NEXT: br label %expand.next20
+// CHECK: expand.next20:
+// CHECK-NEXT: store i32 42, ptr %x21, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 42)
+// CHECK-NEXT: %call22 = call {{.*}} i32 @_Z2tgILi42EEiv()
+// CHECK-NEXT: br label %expand.next23
+// CHECK: expand.next23:
+// CHECK-NEXT: store i32 43, ptr %x24, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 43)
+// CHECK-NEXT: %call25 = call {{.*}} i32 @_Z2tgILi43EEiv()
+// CHECK-NEXT: br label %expand.next26
+// CHECK: expand.next26:
+// CHECK-NEXT: store i32 44, ptr %x27, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 44)
+// CHECK-NEXT: %call28 = call {{.*}} i32 @_Z2tgILi44EEiv()
+// CHECK-NEXT: br label %expand.next29
+// CHECK: expand.next29:
+// CHECK-NEXT: store i32 3, ptr %x30, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 3)
+// CHECK-NEXT: %call31 = call {{.*}} i32 @_Z2tgILi3EEiv()
+// CHECK-NEXT: br label %expand.end32
+// CHECK: expand.end32:
+// CHECK-NEXT: store i32 4, ptr %x33, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 4)
+// CHECK-NEXT: %call34 = call {{.*}} i32 @_Z2tgILi4EEiv()
+// CHECK-NEXT: br label %expand.next35
+// CHECK: expand.next35:
+// CHECK-NEXT: store i32 42, ptr %x36, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 42)
+// CHECK-NEXT: %call37 = call {{.*}} i32 @_Z2tgILi42EEiv()
+// CHECK-NEXT: br label %expand.next38
+// CHECK: expand.next38:
+// CHECK-NEXT: store i32 43, ptr %x39, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 43)
+// CHECK-NEXT: %call40 = call {{.*}} i32 @_Z2tgILi43EEiv()
+// CHECK-NEXT: br label %expand.next41
+// CHECK: expand.next41:
+// CHECK-NEXT: store i32 44, ptr %x42, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 44)
+// CHECK-NEXT: %call43 = call {{.*}} i32 @_Z2tgILi44EEiv()
+// CHECK-NEXT: br label %expand.next44
+// CHECK: expand.next44:
+// CHECK-NEXT: store i32 42, ptr %x45, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 42)
+// CHECK-NEXT: %call46 = call {{.*}} i32 @_Z2tgILi42EEiv()
+// CHECK-NEXT: br label %expand.next47
+// CHECK: expand.next47:
+// CHECK-NEXT: store i32 43, ptr %x48, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 43)
+// CHECK-NEXT: %call49 = call {{.*}} i32 @_Z2tgILi43EEiv()
+// CHECK-NEXT: br label %expand.next50
+// CHECK: expand.next50:
+// CHECK-NEXT: store i32 44, ptr %x51, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 44)
+// CHECK-NEXT: %call52 = call {{.*}} i32 @_Z2tgILi44EEiv()
+// CHECK-NEXT: br label %expand.next53
+// CHECK: expand.next53:
+// CHECK-NEXT: store i32 5, ptr %x54, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 5)
+// CHECK-NEXT: %call55 = call {{.*}} i32 @_Z2tgILi5EEiv()
+// CHECK-NEXT: br label %expand.end56
+// CHECK: expand.end56:
+// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x57, ptr align 4 @__const._Z2t4IJLi42ELi43ELi44EEEvv.x, i64 12, i1 false)
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 42)
+// CHECK-NEXT: %call58 = call {{.*}} i32 @_Z2tgILi42EEiv()
+// CHECK-NEXT: br label %expand.next59
+// CHECK: expand.next59:
+// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x60, ptr align 4 @__const._Z2t4IJLi42ELi43ELi44EEEvv.x.3, i64 12, i1 false)
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 42)
+// CHECK-NEXT: %call61 = call {{.*}} i32 @_Z2tgILi42EEiv()
+// CHECK-NEXT: br label %expand.next62
+// CHECK: expand.next62:
+// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x63, ptr align 4 @__const._Z2t4IJLi42ELi43ELi44EEEvv.x.4, i64 12, i1 false)
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 6)
+// CHECK-NEXT: %call64 = call {{.*}} i32 @_Z2tgILi6EEiv()
+// CHECK-NEXT: br label %expand.end65
+// CHECK: expand.end65:
+// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x66, ptr align 4 @__const._Z2t4IJLi42ELi43ELi44EEEvv.x.5, i64 12, i1 false)
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 42)
+// CHECK-NEXT: %call67 = call {{.*}} i32 @_Z2tgILi42EEiv()
+// CHECK-NEXT: br label %expand.end68
+// CHECK: expand.end68:
+// CHECK-NEXT: ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_ZN2s2IJLi1ELi2ELi3EEE2tfIJLi4ELi5ELi6EEEEvv(ptr {{.*}} %this)
+// CHECK: entry:
+// CHECK-NEXT: %this.addr = alloca ptr, align 8
+// CHECK-NEXT: %x = alloca i32, align 4
+// CHECK-NEXT: %x2 = alloca i32, align 4
+// CHECK-NEXT: %x4 = alloca i32, align 4
+// CHECK-NEXT: %x6 = alloca i32, align 4
+// CHECK-NEXT: %x8 = alloca i32, align 4
+// CHECK-NEXT: %x10 = alloca i32, align 4
+// CHECK-NEXT: %x11 = alloca %struct.X, align 4
+// CHECK-NEXT: %x13 = alloca %struct.X, align 4
+// CHECK-NEXT: %x16 = alloca i32, align 4
+// CHECK-NEXT: %x18 = alloca i32, align 4
+// CHECK-NEXT: %x21 = alloca i32, align 4
+// CHECK-NEXT: %x24 = alloca i32, align 4
+// CHECK-NEXT: %x27 = alloca i32, align 4
+// CHECK-NEXT: %x30 = alloca i32, align 4
+// CHECK-NEXT: %x33 = alloca %struct.X, align 4
+// CHECK-NEXT: %x36 = alloca %struct.X, align 4
+// CHECK-NEXT: store ptr %this, ptr %this.addr, align 8
+// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-NEXT: store i32 1, ptr %x, align 4
+// CHECK-NEXT: %0 = load i32, ptr %x, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %0)
+// CHECK-NEXT: br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT: store i32 2, ptr %x2, align 4
+// CHECK-NEXT: %1 = load i32, ptr %x2, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %1)
+// CHECK-NEXT: br label %expand.next3
+// CHECK: expand.next3:
+// CHECK-NEXT: store i32 3, ptr %x4, align 4
+// CHECK-NEXT: %2 = load i32, ptr %x4, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %2)
+// CHECK-NEXT: br label %expand.next5
+// CHECK: expand.next5:
+// CHECK-NEXT: store i32 4, ptr %x6, align 4
+// CHECK-NEXT: %3 = load i32, ptr %x6, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %3)
+// CHECK-NEXT: br label %expand.next7
+// CHECK: expand.next7:
+// CHECK-NEXT: store i32 5, ptr %x8, align 4
+// CHECK-NEXT: %4 = load i32, ptr %x8, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %4)
+// CHECK-NEXT: br label %expand.next9
+// CHECK: expand.next9:
+// CHECK-NEXT: store i32 6, ptr %x10, align 4
+// CHECK-NEXT: %5 = load i32, ptr %x10, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %5)
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x11, ptr align 4 @__const._ZN2s2IJLi1ELi2ELi3EEE2tfIJLi4ELi5ELi6EEEEvv.x, i64 12, i1 false)
+// CHECK-NEXT: %a = getelementptr inbounds nuw %struct.X, ptr %x11, i32 0, i32 0
+// CHECK-NEXT: %6 = load i32, ptr %a, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %6)
+// CHECK-NEXT: br label %expand.next12
+// CHECK: expand.next12:
+// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x13, ptr align 4 @__const._ZN2s2IJLi1ELi2ELi3EEE2tfIJLi4ELi5ELi6EEEEvv.x.6, i64 12, i1 false)
+// CHECK-NEXT: %a14 = getelementptr inbounds nuw %struct.X, ptr %x13, i32 0, i32 0
+// CHECK-NEXT: %7 = load i32, ptr %a14, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} %7)
+// CHECK-NEXT: br label %expand.end15
+// CHECK: expand.end15:
+// CHECK-NEXT: store i32 1, ptr %x16, align 4
+// CHECK-NEXT: %call = call {{.*}} i32 @_Z2tgILi1EEiv()
+// CHECK-NEXT: br label %expand.next17
+// CHECK: expand.next17:
+// CHECK-NEXT: store i32 2, ptr %x18, align 4
+// CHECK-NEXT: %call19 = call {{.*}} i32 @_Z2tgILi2EEiv()
+// CHECK-NEXT: br label %expand.next20
+// CHECK: expand.next20:
+// CHECK-NEXT: store i32 3, ptr %x21, align 4
+// CHECK-NEXT: %call22 = call {{.*}} i32 @_Z2tgILi3EEiv()
+// CHECK-NEXT: br label %expand.next23
+// CHECK: expand.next23:
+// CHECK-NEXT: store i32 4, ptr %x24, align 4
+// CHECK-NEXT: %call25 = call {{.*}} i32 @_Z2tgILi4EEiv()
+// CHECK-NEXT: br label %expand.next26
+// CHECK: expand.next26:
+// CHECK-NEXT: store i32 5, ptr %x27, align 4
+// CHECK-NEXT: %call28 = call {{.*}} i32 @_Z2tgILi5EEiv()
+// CHECK-NEXT: br label %expand.next29
+// CHECK: expand.next29:
+// CHECK-NEXT: store i32 6, ptr %x30, align 4
+// CHECK-NEXT: %call31 = call {{.*}} i32 @_Z2tgILi6EEiv()
+// CHECK-NEXT: br label %expand.end32
+// CHECK: expand.end32:
+// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x33, ptr align 4 @__const._ZN2s2IJLi1ELi2ELi3EEE2tfIJLi4ELi5ELi6EEEEvv.x.7, i64 12, i1 false)
+// CHECK-NEXT: %call34 = call {{.*}} i32 @_Z2tgILi1EEiv()
+// CHECK-NEXT: br label %expand.next35
+// CHECK: expand.next35:
+// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x36, ptr align 4 @__const._ZN2s2IJLi1ELi2ELi3EEE2tfIJLi4ELi5ELi6EEEEvv.x.8, i64 12, i1 false)
+// CHECK-NEXT: %call37 = call {{.*}} i32 @_Z2tgILi4EEiv()
+// CHECK-NEXT: br label %expand.end38
+// CHECK: expand.end38:
+// CHECK-NEXT: ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_Z2f8v()
+// CHECK: entry:
+// CHECK-NEXT: call void @_Z2t5IJLi1ELi2ELi3EEEvv()
+// CHECK-NEXT: ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_Z2t5IJLi1ELi2ELi3EEEvv()
+// CHECK: entry:
+// CHECK-NEXT: %ref.tmp = alloca %class.anon, align 1
+// CHECK-NEXT: %ref.tmp1 = alloca %class.anon.1, align 1
+// CHECK-NEXT: %ref.tmp2 = alloca %class.anon.3, align 1
+// CHECK-NEXT: call void @_ZZ2t5IJLi1ELi2ELi3EEEvvENKUlvE1_clEv(ptr {{.*}} %ref.tmp)
+// CHECK-NEXT: call void @_ZZ2t5IJLi1ELi2ELi3EEEvvENKUlvE0_clEv(ptr {{.*}} %ref.tmp1)
+// CHECK-NEXT: call void @_ZZ2t5IJLi1ELi2ELi3EEEvvENKUlvE_clEv(ptr {{.*}} %ref.tmp2)
+// CHECK-NEXT: ret void
+
+
+// CHECK-LABEL: define {{.*}} i32 @_Z22references_enumeratingv()
+// CHECK: entry:
+// CHECK-NEXT: %x = alloca i32, align 4
+// CHECK-NEXT: %y = alloca i32, align 4
+// CHECK-NEXT: %z = alloca i32, align 4
+// CHECK-NEXT: %v = alloca ptr, align 8
+// CHECK-NEXT: %v1 = alloca ptr, align 8
+// CHECK-NEXT: %v4 = alloca ptr, align 8
+// CHECK-NEXT: %v6 = alloca ptr, align 8
+// CHECK-NEXT: %v9 = alloca ptr, align 8
+// CHECK-NEXT: %v12 = alloca ptr, align 8
+// CHECK-NEXT: store i32 1, ptr %x, align 4
+// CHECK-NEXT: store i32 2, ptr %y, align 4
+// CHECK-NEXT: store i32 3, ptr %z, align 4
+// CHECK-NEXT: store ptr %x, ptr %v, align 8
+// CHECK-NEXT: %0 = load ptr, ptr %v, align 8
+// CHECK-NEXT: %1 = load i32, ptr %0, align 4
+// CHECK-NEXT: %inc = add nsw i32 %1, 1
+// CHECK-NEXT: store i32 %inc, ptr %0, align 4
+// CHECK-NEXT: br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT: store ptr %y, ptr %v1, align 8
+// CHECK-NEXT: %2 = load ptr, ptr %v1, align 8
+// CHECK-NEXT: %3 = load i32, ptr %2, align 4
+// CHECK-NEXT: %inc2 = add nsw i32 %3, 1
+// CHECK-NEXT: store i32 %inc2, ptr %2, align 4
+// CHECK-NEXT: br label %expand.next3
+// CHECK: expand.next3:
+// CHECK-NEXT: store ptr %z, ptr %v4, align 8
+// CHECK-NEXT: %4 = load ptr, ptr %v4, align 8
+// CHECK-NEXT: %5 = load i32, ptr %4, align 4
+// CHECK-NEXT: %inc5 = add nsw i32 %5, 1
+// CHECK-NEXT: store i32 %inc5, ptr %4, align 4
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: store ptr %x, ptr %v6, align 8
+// CHECK-NEXT: %6 = load ptr, ptr %v6, align 8
+// CHECK-NEXT: %7 = load i32, ptr %6, align 4
+// CHECK-NEXT: %inc7 = add nsw i32 %7, 1
+// CHECK-NEXT: store i32 %inc7, ptr %6, align 4
+// CHECK-NEXT: br label %expand.next8
+// CHECK: expand.next8:
+// CHECK-NEXT: store ptr %y, ptr %v9, align 8
+// CHECK-NEXT: %8 = load ptr, ptr %v9, align 8
+// CHECK-NEXT: %9 = load i32, ptr %8, align 4
+// CHECK-NEXT: %inc10 = add nsw i32 %9, 1
+// CHECK-NEXT: store i32 %inc10, ptr %8, align 4
+// CHECK-NEXT: br label %expand.next11
+// CHECK: expand.next11:
+// CHECK-NEXT: store ptr %z, ptr %v12, align 8
+// CHECK-NEXT: %10 = load ptr, ptr %v12, align 8
+// CHECK-NEXT: %11 = load i32, ptr %10, align 4
+// CHECK-NEXT: %inc13 = add nsw i32 %11, 1
+// CHECK-NEXT: store i32 %inc13, ptr %10, align 4
+// CHECK-NEXT: br label %expand.end14
+// CHECK: expand.end14:
+// CHECK-NEXT: %12 = load i32, ptr %x, align 4
+// CHECK-NEXT: %13 = load i32, ptr %y, align 4
+// CHECK-NEXT: %add = add nsw i32 %12, %13
+// CHECK-NEXT: %14 = load i32, ptr %z, align 4
+// CHECK-NEXT: %add15 = add nsw i32 %add, %14
+// CHECK-NEXT: ret i32 %add15
+
+
+// CHECK-LABEL: define {{.*}} void @_ZZ2t5IJLi1ELi2ELi3EEEvvENKUlvE1_clEv(ptr {{.*}} %this)
+// CHECK: entry:
+// CHECK-NEXT: %this.addr = alloca ptr, align 8
+// CHECK-NEXT: %x = alloca i32, align 4
+// CHECK-NEXT: store ptr %this, ptr %this.addr, align 8
+// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-NEXT: store i32 1, ptr %x, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 1)
+// CHECK-NEXT: %call = call {{.*}} i32 @_Z2tgILi1EEiv()
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_ZZ2t5IJLi1ELi2ELi3EEEvvENKUlvE0_clEv(ptr {{.*}} %this)
+// CHECK: entry:
+// CHECK-NEXT: %this.addr = alloca ptr, align 8
+// CHECK-NEXT: %x = alloca i32, align 4
+// CHECK-NEXT: store ptr %this, ptr %this.addr, align 8
+// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-NEXT: store i32 2, ptr %x, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 2)
+// CHECK-NEXT: %call = call {{.*}} i32 @_Z2tgILi2EEiv()
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_ZZ2t5IJLi1ELi2ELi3EEEvvENKUlvE_clEv(ptr {{.*}} %this)
+// CHECK: entry:
+// CHECK-NEXT: %this.addr = alloca ptr, align 8
+// CHECK-NEXT: %x = alloca i32, align 4
+// CHECK-NEXT: store ptr %this, ptr %this.addr, align 8
+// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-NEXT: store i32 3, ptr %x, align 4
+// CHECK-NEXT: call void @_Z1gi(i32 {{.*}} 3)
+// CHECK-NEXT: %call = call {{.*}} i32 @_Z2tgILi3EEiv()
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: ret void
diff --git a/clang/test/CodeGenCXX/cxx2c-expansion-stmts-control-flow.cpp b/clang/test/CodeGenCXX/cxx2c-expansion-stmts-control-flow.cpp
new file mode 100644
index 0000000000000..7bcaf56adf652
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx2c-expansion-stmts-control-flow.cpp
@@ -0,0 +1,430 @@
+// RUN: %clang_cc1 -std=c++2c -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s
+
+void h(int, int);
+
+void break_continue() {
+ template for (auto x : {1, 2}) {
+ break;
+ h(1, x);
+ }
+
+ template for (auto x : {3, 4}) {
+ continue;
+ h(2, x);
+ }
+
+ template for (auto x : {5, 6}) {
+ if (x == 2) break;
+ h(3, x);
+ }
+
+ template for (auto x : {7, 8}) {
+ if (x == 2) continue;
+ h(4, x);
+ }
+}
+
+int break_continue_nested() {
+ int sum = 0;
+
+ template for (auto x : {1, 2}) {
+ template for (auto y : {3, 4}) {
+ if (x == 2) break;
+ sum += y;
+ }
+ sum += x;
+ }
+
+ template for (auto x : {5, 6}) {
+ template for (auto y : {7, 8}) {
+ if (x == 6) continue;
+ sum += y;
+ }
+ sum += x;
+ }
+
+ return sum;
+}
+
+void label() {
+ // Only local labels are allowed in expansion statements.
+ template for (auto x : {1, 2, 3}) {
+ __label__ a;
+ if (x == 1) goto a;
+ h(1, x);
+ a:;
+ }
+}
+
+void nested_label() {
+ template for (auto x : {1, 2}) {
+ __label__ a;
+ template for (auto y : {3, 4}) {
+ if (y == 3) goto a;
+ if (y == 4) goto end;
+ h(x, y);
+ }
+ a:;
+ }
+ end:
+}
+
+
+// CHECK-LABEL: define {{.*}} void @_Z14break_continuev()
+// CHECK: entry:
+// CHECK-NEXT: %x = alloca i32, align 4
+// CHECK-NEXT: %x1 = alloca i32, align 4
+// CHECK-NEXT: %x2 = alloca i32, align 4
+// CHECK-NEXT: %x3 = alloca i32, align 4
+// CHECK-NEXT: %x5 = alloca i32, align 4
+// CHECK-NEXT: %x7 = alloca i32, align 4
+// CHECK-NEXT: %x12 = alloca i32, align 4
+// CHECK-NEXT: %x17 = alloca i32, align 4
+// CHECK-NEXT: store i32 1, ptr %x, align 4
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: store i32 3, ptr %x2, align 4
+// CHECK-NEXT: br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT: store i32 4, ptr %x3, align 4
+// CHECK-NEXT: br label %expand.end4
+// CHECK: expand.end4:
+// CHECK-NEXT: store i32 5, ptr %x5, align 4
+// CHECK-NEXT: %0 = load i32, ptr %x5, align 4
+// CHECK-NEXT: %cmp = icmp eq i32 %0, 2
+// CHECK-NEXT: br i1 %cmp, label %if.then, label %if.end
+// CHECK: if.then:
+// CHECK-NEXT: br label %expand.end11
+// CHECK: if.end:
+// CHECK-NEXT: %1 = load i32, ptr %x5, align 4
+// CHECK-NEXT: call void @_Z1hii(i32 {{.*}} 3, i32 {{.*}} %1)
+// CHECK-NEXT: br label %expand.next6
+// CHECK: expand.next6:
+// CHECK-NEXT: store i32 6, ptr %x7, align 4
+// CHECK-NEXT: %2 = load i32, ptr %x7, align 4
+// CHECK-NEXT: %cmp8 = icmp eq i32 %2, 2
+// CHECK-NEXT: br i1 %cmp8, label %if.then9, label %if.end10
+// CHECK: if.then9:
+// CHECK-NEXT: br label %expand.end11
+// CHECK: if.end10:
+// CHECK-NEXT: %3 = load i32, ptr %x7, align 4
+// CHECK-NEXT: call void @_Z1hii(i32 {{.*}} 3, i32 {{.*}} %3)
+// CHECK-NEXT: br label %expand.end11
+// CHECK: expand.end11:
+// CHECK-NEXT: store i32 7, ptr %x12, align 4
+// CHECK-NEXT: %4 = load i32, ptr %x12, align 4
+// CHECK-NEXT: %cmp13 = icmp eq i32 %4, 2
+// CHECK-NEXT: br i1 %cmp13, label %if.then14, label %if.end15
+// CHECK: if.then14:
+// CHECK-NEXT: br label %expand.next16
+// CHECK: if.end15:
+// CHECK-NEXT: %5 = load i32, ptr %x12, align 4
+// CHECK-NEXT: call void @_Z1hii(i32 {{.*}} 4, i32 {{.*}} %5)
+// CHECK-NEXT: br label %expand.next16
+// CHECK: expand.next16:
+// CHECK-NEXT: store i32 8, ptr %x17, align 4
+// CHECK-NEXT: %6 = load i32, ptr %x17, align 4
+// CHECK-NEXT: %cmp18 = icmp eq i32 %6, 2
+// CHECK-NEXT: br i1 %cmp18, label %if.then19, label %if.end20
+// CHECK: if.then19:
+// CHECK-NEXT: br label %expand.end21
+// CHECK: if.end20:
+// CHECK-NEXT: %7 = load i32, ptr %x17, align 4
+// CHECK-NEXT: call void @_Z1hii(i32 {{.*}} 4, i32 {{.*}} %7)
+// CHECK-NEXT: br label %expand.end21
+// CHECK: expand.end21:
+// CHECK-NEXT: ret void
+
+
+// CHECK-LABEL: define {{.*}} i32 @_Z21break_continue_nestedv()
+// CHECK: entry:
+// CHECK-NEXT: %sum = alloca i32, align 4
+// CHECK-NEXT: %x = alloca i32, align 4
+// CHECK-NEXT: %y = alloca i32, align 4
+// CHECK-NEXT: %y1 = alloca i32, align 4
+// CHECK-NEXT: %x8 = alloca i32, align 4
+// CHECK-NEXT: %y9 = alloca i32, align 4
+// CHECK-NEXT: %y15 = alloca i32, align 4
+// CHECK-NEXT: %x23 = alloca i32, align 4
+// CHECK-NEXT: %y24 = alloca i32, align 4
+// CHECK-NEXT: %y30 = alloca i32, align 4
+// CHECK-NEXT: %x38 = alloca i32, align 4
+// CHECK-NEXT: %y39 = alloca i32, align 4
+// CHECK-NEXT: %y45 = alloca i32, align 4
+// CHECK-NEXT: store i32 0, ptr %sum, align 4
+// CHECK-NEXT: store i32 1, ptr %x, align 4
+// CHECK-NEXT: store i32 3, ptr %y, align 4
+// CHECK-NEXT: %0 = load i32, ptr %x, align 4
+// CHECK-NEXT: %cmp = icmp eq i32 %0, 2
+// CHECK-NEXT: br i1 %cmp, label %if.then, label %if.end
+// CHECK: if.then:
+// CHECK-NEXT: br label %expand.end
+// CHECK: if.end:
+// CHECK-NEXT: %1 = load i32, ptr %y, align 4
+// CHECK-NEXT: %2 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add = add nsw i32 %2, %1
+// CHECK-NEXT: store i32 %add, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT: store i32 4, ptr %y1, align 4
+// CHECK-NEXT: %3 = load i32, ptr %x, align 4
+// CHECK-NEXT: %cmp2 = icmp eq i32 %3, 2
+// CHECK-NEXT: br i1 %cmp2, label %if.then3, label %if.end4
+// CHECK: if.then3:
+// CHECK-NEXT: br label %expand.end
+// CHECK: if.end4:
+// CHECK-NEXT: %4 = load i32, ptr %y1, align 4
+// CHECK-NEXT: %5 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add5 = add nsw i32 %5, %4
+// CHECK-NEXT: store i32 %add5, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: %6 = load i32, ptr %x, align 4
+// CHECK-NEXT: %7 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add6 = add nsw i32 %7, %6
+// CHECK-NEXT: store i32 %add6, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next7
+// CHECK: expand.next7:
+// CHECK-NEXT: store i32 2, ptr %x8, align 4
+// CHECK-NEXT: store i32 3, ptr %y9, align 4
+// CHECK-NEXT: %8 = load i32, ptr %x8, align 4
+// CHECK-NEXT: %cmp10 = icmp eq i32 %8, 2
+// CHECK-NEXT: br i1 %cmp10, label %if.then11, label %if.end12
+// CHECK: if.then11:
+// CHECK-NEXT: br label %expand.end20
+// CHECK: if.end12:
+// CHECK-NEXT: %9 = load i32, ptr %y9, align 4
+// CHECK-NEXT: %10 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add13 = add nsw i32 %10, %9
+// CHECK-NEXT: store i32 %add13, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next14
+// CHECK: expand.next14:
+// CHECK-NEXT: store i32 4, ptr %y15, align 4
+// CHECK-NEXT: %11 = load i32, ptr %x8, align 4
+// CHECK-NEXT: %cmp16 = icmp eq i32 %11, 2
+// CHECK-NEXT: br i1 %cmp16, label %if.then17, label %if.end18
+// CHECK: if.then17:
+// CHECK-NEXT: br label %expand.end20
+// CHECK: if.end18:
+// CHECK-NEXT: %12 = load i32, ptr %y15, align 4
+// CHECK-NEXT: %13 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add19 = add nsw i32 %13, %12
+// CHECK-NEXT: store i32 %add19, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.end20
+// CHECK: expand.end20:
+// CHECK-NEXT: %14 = load i32, ptr %x8, align 4
+// CHECK-NEXT: %15 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add21 = add nsw i32 %15, %14
+// CHECK-NEXT: store i32 %add21, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.end22
+// CHECK: expand.end22:
+// CHECK-NEXT: store i32 5, ptr %x23, align 4
+// CHECK-NEXT: store i32 7, ptr %y24, align 4
+// CHECK-NEXT: %16 = load i32, ptr %x23, align 4
+// CHECK-NEXT: %cmp25 = icmp eq i32 %16, 6
+// CHECK-NEXT: br i1 %cmp25, label %if.then26, label %if.end27
+// CHECK: if.then26:
+// CHECK-NEXT: br label %expand.next29
+// CHECK: if.end27:
+// CHECK-NEXT: %17 = load i32, ptr %y24, align 4
+// CHECK-NEXT: %18 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add28 = add nsw i32 %18, %17
+// CHECK-NEXT: store i32 %add28, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next29
+// CHECK: expand.next29:
+// CHECK-NEXT: store i32 8, ptr %y30, align 4
+// CHECK-NEXT: %19 = load i32, ptr %x23, align 4
+// CHECK-NEXT: %cmp31 = icmp eq i32 %19, 6
+// CHECK-NEXT: br i1 %cmp31, label %if.then32, label %if.end33
+// CHECK: if.then32:
+// CHECK-NEXT: br label %expand.end35
+// CHECK: if.end33:
+// CHECK-NEXT: %20 = load i32, ptr %y30, align 4
+// CHECK-NEXT: %21 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add34 = add nsw i32 %21, %20
+// CHECK-NEXT: store i32 %add34, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.end35
+// CHECK: expand.end35:
+// CHECK-NEXT: %22 = load i32, ptr %x23, align 4
+// CHECK-NEXT: %23 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add36 = add nsw i32 %23, %22
+// CHECK-NEXT: store i32 %add36, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next37
+// CHECK: expand.next37:
+// CHECK-NEXT: store i32 6, ptr %x38, align 4
+// CHECK-NEXT: store i32 7, ptr %y39, align 4
+// CHECK-NEXT: %24 = load i32, ptr %x38, align 4
+// CHECK-NEXT: %cmp40 = icmp eq i32 %24, 6
+// CHECK-NEXT: br i1 %cmp40, label %if.then41, label %if.end42
+// CHECK: if.then41:
+// CHECK-NEXT: br label %expand.next44
+// CHECK: if.end42:
+// CHECK-NEXT: %25 = load i32, ptr %y39, align 4
+// CHECK-NEXT: %26 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add43 = add nsw i32 %26, %25
+// CHECK-NEXT: store i32 %add43, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next44
+// CHECK: expand.next44:
+// CHECK-NEXT: store i32 8, ptr %y45, align 4
+// CHECK-NEXT: %27 = load i32, ptr %x38, align 4
+// CHECK-NEXT: %cmp46 = icmp eq i32 %27, 6
+// CHECK-NEXT: br i1 %cmp46, label %if.then47, label %if.end48
+// CHECK: if.then47:
+// CHECK-NEXT: br label %expand.end50
+// CHECK: if.end48:
+// CHECK-NEXT: %28 = load i32, ptr %y45, align 4
+// CHECK-NEXT: %29 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add49 = add nsw i32 %29, %28
+// CHECK-NEXT: store i32 %add49, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.end50
+// CHECK: expand.end50:
+// CHECK-NEXT: %30 = load i32, ptr %x38, align 4
+// CHECK-NEXT: %31 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add51 = add nsw i32 %31, %30
+// CHECK-NEXT: store i32 %add51, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.end52
+// CHECK: expand.end52:
+// CHECK-NEXT: %32 = load i32, ptr %sum, align 4
+// CHECK-NEXT: ret i32 %32
+
+
+// CHECK-LABEL: define {{.*}} void @_Z5labelv()
+// CHECK: entry:
+// CHECK-NEXT: %x = alloca i32, align 4
+// CHECK-NEXT: %x1 = alloca i32, align 4
+// CHECK-NEXT: %x7 = alloca i32, align 4
+// CHECK-NEXT: store i32 1, ptr %x, align 4
+// CHECK-NEXT: %0 = load i32, ptr %x, align 4
+// CHECK-NEXT: %cmp = icmp eq i32 %0, 1
+// CHECK-NEXT: br i1 %cmp, label %if.then, label %if.end
+// CHECK: if.then:
+// CHECK-NEXT: br label %a
+// CHECK: if.end:
+// CHECK-NEXT: %1 = load i32, ptr %x, align 4
+// CHECK-NEXT: call void @_Z1hii(i32 {{.*}} 1, i32 {{.*}} %1)
+// CHECK-NEXT: br label %a
+// CHECK: a:
+// CHECK-NEXT: br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT: store i32 2, ptr %x1, align 4
+// CHECK-NEXT: %2 = load i32, ptr %x1, align 4
+// CHECK-NEXT: %cmp2 = icmp eq i32 %2, 1
+// CHECK-NEXT: br i1 %cmp2, label %if.then3, label %if.end4
+// CHECK: if.then3:
+// CHECK-NEXT: br label %a5
+// CHECK: if.end4:
+// CHECK-NEXT: %3 = load i32, ptr %x1, align 4
+// CHECK-NEXT: call void @_Z1hii(i32 {{.*}} 1, i32 {{.*}} %3)
+// CHECK-NEXT: br label %a5
+// CHECK: a5:
+// CHECK-NEXT: br label %expand.next6
+// CHECK: expand.next6:
+// CHECK-NEXT: store i32 3, ptr %x7, align 4
+// CHECK-NEXT: %4 = load i32, ptr %x7, align 4
+// CHECK-NEXT: %cmp8 = icmp eq i32 %4, 1
+// CHECK-NEXT: br i1 %cmp8, label %if.then9, label %if.end10
+// CHECK: if.then9:
+// CHECK-NEXT: br label %a11
+// CHECK: if.end10:
+// CHECK-NEXT: %5 = load i32, ptr %x7, align 4
+// CHECK-NEXT: call void @_Z1hii(i32 {{.*}} 1, i32 {{.*}} %5)
+// CHECK-NEXT: br label %a11
+// CHECK: a11:
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_Z12nested_labelv()
+// CHECK: entry:
+// CHECK-NEXT: %x = alloca i32, align 4
+// CHECK-NEXT: %y = alloca i32, align 4
+// CHECK-NEXT: %y4 = alloca i32, align 4
+// CHECK-NEXT: %x12 = alloca i32, align 4
+// CHECK-NEXT: %y13 = alloca i32, align 4
+// CHECK-NEXT: %y21 = alloca i32, align 4
+// CHECK-NEXT: store i32 1, ptr %x, align 4
+// CHECK-NEXT: store i32 3, ptr %y, align 4
+// CHECK-NEXT: %0 = load i32, ptr %y, align 4
+// CHECK-NEXT: %cmp = icmp eq i32 %0, 3
+// CHECK-NEXT: br i1 %cmp, label %if.then, label %if.end
+// CHECK: if.then:
+// CHECK-NEXT: br label %a
+// CHECK: if.end:
+// CHECK-NEXT: %1 = load i32, ptr %y, align 4
+// CHECK-NEXT: %cmp1 = icmp eq i32 %1, 4
+// CHECK-NEXT: br i1 %cmp1, label %if.then2, label %if.end3
+// CHECK: if.then2:
+// CHECK-NEXT: br label %end
+// CHECK: if.end3:
+// CHECK-NEXT: %2 = load i32, ptr %x, align 4
+// CHECK-NEXT: %3 = load i32, ptr %y, align 4
+// CHECK-NEXT: call void @_Z1hii(i32 {{.*}} %2, i32 {{.*}} %3)
+// CHECK-NEXT: br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT: store i32 4, ptr %y4, align 4
+// CHECK-NEXT: %4 = load i32, ptr %y4, align 4
+// CHECK-NEXT: %cmp5 = icmp eq i32 %4, 3
+// CHECK-NEXT: br i1 %cmp5, label %if.then6, label %if.end7
+// CHECK: if.then6:
+// CHECK-NEXT: br label %a
+// CHECK: if.end7:
+// CHECK-NEXT: %5 = load i32, ptr %y4, align 4
+// CHECK-NEXT: %cmp8 = icmp eq i32 %5, 4
+// CHECK-NEXT: br i1 %cmp8, label %if.then9, label %if.end10
+// CHECK: if.then9:
+// CHECK-NEXT: br label %end
+// CHECK: if.end10:
+// CHECK-NEXT: %6 = load i32, ptr %x, align 4
+// CHECK-NEXT: %7 = load i32, ptr %y4, align 4
+// CHECK-NEXT: call void @_Z1hii(i32 {{.*}} %6, i32 {{.*}} %7)
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: br label %a
+// CHECK: a:
+// CHECK-NEXT: br label %expand.next11
+// CHECK: expand.next11:
+// CHECK-NEXT: store i32 2, ptr %x12, align 4
+// CHECK-NEXT: store i32 3, ptr %y13, align 4
+// CHECK-NEXT: %8 = load i32, ptr %y13, align 4
+// CHECK-NEXT: %cmp14 = icmp eq i32 %8, 3
+// CHECK-NEXT: br i1 %cmp14, label %if.then15, label %if.end16
+// CHECK: if.then15:
+// CHECK-NEXT: br label %a29
+// CHECK: if.end16:
+// CHECK-NEXT: %9 = load i32, ptr %y13, align 4
+// CHECK-NEXT: %cmp17 = icmp eq i32 %9, 4
+// CHECK-NEXT: br i1 %cmp17, label %if.then18, label %if.end19
+// CHECK: if.then18:
+// CHECK-NEXT: br label %end
+// CHECK: if.end19:
+// CHECK-NEXT: %10 = load i32, ptr %x12, align 4
+// CHECK-NEXT: %11 = load i32, ptr %y13, align 4
+// CHECK-NEXT: call void @_Z1hii(i32 {{.*}} %10, i32 {{.*}} %11)
+// CHECK-NEXT: br label %expand.next20
+// CHECK: expand.next20:
+// CHECK-NEXT: store i32 4, ptr %y21, align 4
+// CHECK-NEXT: %12 = load i32, ptr %y21, align 4
+// CHECK-NEXT: %cmp22 = icmp eq i32 %12, 3
+// CHECK-NEXT: br i1 %cmp22, label %if.then23, label %if.end24
+// CHECK: if.then23:
+// CHECK-NEXT: br label %a29
+// CHECK: if.end24:
+// CHECK-NEXT: %13 = load i32, ptr %y21, align 4
+// CHECK-NEXT: %cmp25 = icmp eq i32 %13, 4
+// CHECK-NEXT: br i1 %cmp25, label %if.then26, label %if.end27
+// CHECK: if.then26:
+// CHECK-NEXT: br label %end
+// CHECK: if.end27:
+// CHECK-NEXT: %14 = load i32, ptr %x12, align 4
+// CHECK-NEXT: %15 = load i32, ptr %y21, align 4
+// CHECK-NEXT: call void @_Z1hii(i32 {{.*}} %14, i32 {{.*}} %15)
+// CHECK-NEXT: br label %expand.end28
+// CHECK: expand.end28:
+// CHECK-NEXT: br label %a29
+// CHECK: a29:
+// CHECK-NEXT: br label %expand.end30
+// CHECK: expand.end30:
+// CHECK-NEXT: br label %end
+// CHECK: end:
+// CHECK-NEXT: ret void
diff --git a/clang/test/CodeGenCXX/cxx2c-expansion-stmts-mangling.cpp b/clang/test/CodeGenCXX/cxx2c-expansion-stmts-mangling.cpp
new file mode 100644
index 0000000000000..5616bc65c7853
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx2c-expansion-stmts-mangling.cpp
@@ -0,0 +1,134 @@
+// RUN: %clang_cc1 -std=c++2c -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s
+
+// CHECK: @_ZZ2f1vE1y = internal global i32 1, align 4
+// CHECK: @_ZZ2f1vE1y_0 = internal global i32 2, align 4
+// CHECK: @_ZZ2f1vE1y_1 = internal global i32 3, align 4
+// CHECK: @_ZZ2f1vE1y_2 = internal global i32 4, align 4
+
+// CHECK-LABEL: define {{.*}} i32 @_Z2f1v()
+// CHECK: entry:
+// CHECK-NEXT: %sum = alloca i32, align 4
+// CHECK-NEXT: %x = alloca i32, align 4
+// CHECK-NEXT: %x1 = alloca i32, align 4
+// CHECK-NEXT: %x4 = alloca i32, align 4
+// CHECK-NEXT: %x7 = alloca i32, align 4
+// CHECK-NEXT: store i32 0, ptr %sum, align 4
+// CHECK-NEXT: store i32 1, ptr %x, align 4
+// CHECK-NEXT: %0 = load i32, ptr @_ZZ2f1vE1y, align 4
+// CHECK-NEXT: %1 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add = add nsw i32 %1, %0
+// CHECK-NEXT: store i32 %add, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT: store i32 2, ptr %x1, align 4
+// CHECK-NEXT: %2 = load i32, ptr @_ZZ2f1vE1y_0, align 4
+// CHECK-NEXT: %3 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add2 = add nsw i32 %3, %2
+// CHECK-NEXT: store i32 %add2, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next3
+// CHECK: expand.next3:
+// CHECK-NEXT: store i32 3, ptr %x4, align 4
+// CHECK-NEXT: %4 = load i32, ptr @_ZZ2f1vE1y_1, align 4
+// CHECK-NEXT: %5 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add5 = add nsw i32 %5, %4
+// CHECK-NEXT: store i32 %add5, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next6
+// CHECK: expand.next6:
+// CHECK-NEXT: store i32 4, ptr %x7, align 4
+// CHECK-NEXT: %6 = load i32, ptr @_ZZ2f1vE1y_2, align 4
+// CHECK-NEXT: %7 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add8 = add nsw i32 %7, %6
+// CHECK-NEXT: store i32 %add8, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: %8 = load i32, ptr %sum, align 4
+// CHECK-NEXT: ret i32 %8
+int f1() {
+ int sum = 0;
+ template for (constexpr auto x : {1, 2, 3, 4}) {
+ static int y = x;
+ sum += y;
+ }
+ return sum;
+}
+
+// CHECK-LABEL: define {{.*}} i32 @_Z2f2v()
+// CHECK: entry:
+// CHECK-NEXT: %sum = alloca i32, align 4
+// CHECK-NEXT: %x = alloca i32, align 4
+// CHECK-NEXT: %ref.tmp = alloca %class.anon, align 1
+// CHECK-NEXT: %x1 = alloca i32, align 4
+// CHECK-NEXT: %ref.tmp2 = alloca %class.anon.0, align 1
+// CHECK-NEXT: %x6 = alloca i32, align 4
+// CHECK-NEXT: %ref.tmp7 = alloca %class.anon.2, align 1
+// CHECK-NEXT: %x11 = alloca i32, align 4
+// CHECK-NEXT: %ref.tmp12 = alloca %class.anon.4, align 1
+// CHECK-NEXT: store i32 0, ptr %sum, align 4
+// CHECK-NEXT: store i32 1, ptr %x, align 4
+// CHECK-NEXT: %call = call {{.*}} i32 @_ZZ2f2vENKUlvE_clEv(ptr {{.*}} %ref.tmp)
+// CHECK-NEXT: %0 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add = add nsw i32 %0, %call
+// CHECK-NEXT: store i32 %add, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT: store i32 2, ptr %x1, align 4
+// CHECK-NEXT: %call3 = call {{.*}} i32 @_ZZ2f2vENKUlvE0_clEv(ptr {{.*}} %ref.tmp2)
+// CHECK-NEXT: %1 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add4 = add nsw i32 %1, %call3
+// CHECK-NEXT: store i32 %add4, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next5
+// CHECK: expand.next5:
+// CHECK-NEXT: store i32 3, ptr %x6, align 4
+// CHECK-NEXT: %call8 = call {{.*}} i32 @_ZZ2f2vENKUlvE1_clEv(ptr {{.*}} %ref.tmp7)
+// CHECK-NEXT: %2 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add9 = add nsw i32 %2, %call8
+// CHECK-NEXT: store i32 %add9, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next10
+// CHECK: expand.next10:
+// CHECK-NEXT: store i32 4, ptr %x11, align 4
+// CHECK-NEXT: %call13 = call {{.*}} i32 @_ZZ2f2vENKUlvE2_clEv(ptr {{.*}} %ref.tmp12)
+// CHECK-NEXT: %3 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add14 = add nsw i32 %3, %call13
+// CHECK-NEXT: store i32 %add14, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: %4 = load i32, ptr %sum, align 4
+// CHECK-NEXT: ret i32 %4
+int f2() {
+ int sum = 0;
+ template for (constexpr auto x : {1, 2, 3, 4}) {
+ sum += []{ return x; }();
+ }
+ return sum;
+}
+
+// CHECK-LABEL: define {{.*}} i32 @_ZZ2f2vENKUlvE_clEv(ptr {{.*}} %this)
+// CHECK: entry:
+// CHECK-NEXT: %this.addr = alloca ptr, align 8
+// CHECK-NEXT: store ptr %this, ptr %this.addr, align 8
+// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-NEXT: ret i32 1
+
+
+// CHECK-LABEL: define {{.*}} i32 @_ZZ2f2vENKUlvE0_clEv(ptr {{.*}} %this)
+// CHECK: entry:
+// CHECK-NEXT: %this.addr = alloca ptr, align 8
+// CHECK-NEXT: store ptr %this, ptr %this.addr, align 8
+// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-NEXT: ret i32 2
+
+
+// CHECK-LABEL: define {{.*}} i32 @_ZZ2f2vENKUlvE1_clEv(ptr {{.*}} %this)
+// CHECK: entry:
+// CHECK-NEXT: %this.addr = alloca ptr, align 8
+// CHECK-NEXT: store ptr %this, ptr %this.addr, align 8
+// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-NEXT: ret i32 3
+
+
+// CHECK-LABEL: define {{.*}} i32 @_ZZ2f2vENKUlvE2_clEv(ptr {{.*}} %this)
+// CHECK: entry:
+// CHECK-NEXT: %this.addr = alloca ptr, align 8
+// CHECK-NEXT: store ptr %this, ptr %this.addr, align 8
+// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-NEXT: ret i32 4
diff --git a/clang/test/CodeGenCXX/cxx2c-expansion-stmts-templates.cpp b/clang/test/CodeGenCXX/cxx2c-expansion-stmts-templates.cpp
new file mode 100644
index 0000000000000..e0de7ced5baee
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx2c-expansion-stmts-templates.cpp
@@ -0,0 +1,208 @@
+// RUN: %clang_cc1 -std=c++2c -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s
+
+struct E {
+ int x, y;
+ constexpr E(int x, int y) : x{x}, y{y} {}
+};
+
+template <typename ...Es>
+int unexpanded_pack_good(Es ...es) {
+ int sum = 0;
+ ([&] {
+ template for (auto x : es) sum += x;
+ template for (Es e : {{5, 6}, {7, 8}}) sum += e.x + e.y;
+ }(), ...);
+ return sum;
+}
+
+int unexpanded_pack() {
+ return unexpanded_pack_good(E{1, 2}, E{3, 4});
+}
+
+
+// CHECK: %struct.E = type { i32, i32 }
+// CHECK: %class.anon = type { ptr, ptr }
+// CHECK: %class.anon.0 = type { ptr, ptr }
+
+
+// CHECK-LABEL: define {{.*}} i32 @_Z15unexpanded_packv()
+// CHECK: entry:
+// CHECK-NEXT: %agg.tmp = alloca %struct.E, align 4
+// CHECK-NEXT: %agg.tmp1 = alloca %struct.E, align 4
+// CHECK-NEXT: call void @_ZN1EC1Eii(ptr {{.*}} %agg.tmp, i32 {{.*}} 1, i32 {{.*}} 2)
+// CHECK-NEXT: call void @_ZN1EC1Eii(ptr {{.*}} %agg.tmp1, i32 {{.*}} 3, i32 {{.*}} 4)
+// CHECK-NEXT: %0 = load i64, ptr %agg.tmp, align 4
+// CHECK-NEXT: %1 = load i64, ptr %agg.tmp1, align 4
+// CHECK-NEXT: %call = call {{.*}} i32 @_Z20unexpanded_pack_goodIJ1ES0_EEiDpT_(i64 %0, i64 %1)
+// CHECK-NEXT: ret i32 %call
+
+
+// CHECK-LABEL: define {{.*}} i32 @_Z20unexpanded_pack_goodIJ1ES0_EEiDpT_(i64 %es.coerce, i64 %es.coerce2)
+// CHECK: entry:
+// CHECK-NEXT: %es = alloca %struct.E, align 4
+// CHECK-NEXT: %es3 = alloca %struct.E, align 4
+// CHECK-NEXT: %sum = alloca i32, align 4
+// CHECK-NEXT: %ref.tmp = alloca %class.anon, align 8
+// CHECK-NEXT: %ref.tmp4 = alloca %class.anon.0, align 8
+// CHECK-NEXT: store i64 %es.coerce, ptr %es, align 4
+// CHECK-NEXT: store i64 %es.coerce2, ptr %es3, align 4
+// CHECK-NEXT: store i32 0, ptr %sum, align 4
+// CHECK-NEXT: %0 = getelementptr inbounds nuw %class.anon, ptr %ref.tmp, i32 0, i32 0
+// CHECK-NEXT: store ptr %es, ptr %0, align 8
+// CHECK-NEXT: %1 = getelementptr inbounds nuw %class.anon, ptr %ref.tmp, i32 0, i32 1
+// CHECK-NEXT: store ptr %sum, ptr %1, align 8
+// CHECK-NEXT: call void @_ZZ20unexpanded_pack_goodIJ1ES0_EEiDpT_ENKUlvE0_clEv(ptr {{.*}} %ref.tmp)
+// CHECK-NEXT: %2 = getelementptr inbounds nuw %class.anon.0, ptr %ref.tmp4, i32 0, i32 0
+// CHECK-NEXT: store ptr %es3, ptr %2, align 8
+// CHECK-NEXT: %3 = getelementptr inbounds nuw %class.anon.0, ptr %ref.tmp4, i32 0, i32 1
+// CHECK-NEXT: store ptr %sum, ptr %3, align 8
+// CHECK-NEXT: call void @_ZZ20unexpanded_pack_goodIJ1ES0_EEiDpT_ENKUlvE_clEv(ptr {{.*}} %ref.tmp4)
+// CHECK-NEXT: %4 = load i32, ptr %sum, align 4
+// CHECK-NEXT: ret i32 %4
+
+
+// CHECK-LABEL: define {{.*}} void @_ZN1EC1Eii(ptr {{.*}} %this, i32 {{.*}} %x, i32 {{.*}} %y) {{.*}}
+// CHECK: entry:
+// CHECK-NEXT: %this.addr = alloca ptr, align 8
+// CHECK-NEXT: %x.addr = alloca i32, align 4
+// CHECK-NEXT: %y.addr = alloca i32, align 4
+// CHECK-NEXT: store ptr %this, ptr %this.addr, align 8
+// CHECK-NEXT: store i32 %x, ptr %x.addr, align 4
+// CHECK-NEXT: store i32 %y, ptr %y.addr, align 4
+// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-NEXT: %0 = load i32, ptr %x.addr, align 4
+// CHECK-NEXT: %1 = load i32, ptr %y.addr, align 4
+// CHECK-NEXT: call void @_ZN1EC2Eii(ptr {{.*}} %this1, i32 {{.*}} %0, i32 {{.*}} %1)
+// CHECK-NEXT: ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_ZZ20unexpanded_pack_goodIJ1ES0_EEiDpT_ENKUlvE0_clEv(ptr {{.*}} %this)
+// CHECK: entry:
+// CHECK-NEXT: %this.addr = alloca ptr, align 8
+// CHECK-NEXT: %0 = alloca ptr, align 8
+// CHECK-NEXT: %x = alloca i32, align 4
+// CHECK-NEXT: %x3 = alloca i32, align 4
+// CHECK-NEXT: %e = alloca %struct.E, align 4
+// CHECK-NEXT: %e10 = alloca %struct.E, align 4
+// CHECK-NEXT: store ptr %this, ptr %this.addr, align 8
+// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-NEXT: %1 = getelementptr inbounds nuw %class.anon, ptr %this1, i32 0, i32 0
+// CHECK-NEXT: %2 = load ptr, ptr %1, align 8
+// CHECK-NEXT: store ptr %2, ptr %0, align 8
+// CHECK-NEXT: %3 = load ptr, ptr %0, align 8
+// CHECK-NEXT: %x2 = getelementptr inbounds nuw %struct.E, ptr %3, i32 0, i32 0
+// CHECK-NEXT: %4 = load i32, ptr %x2, align 4
+// CHECK-NEXT: store i32 %4, ptr %x, align 4
+// CHECK-NEXT: %5 = load i32, ptr %x, align 4
+// CHECK-NEXT: %6 = getelementptr inbounds nuw %class.anon, ptr %this1, i32 0, i32 1
+// CHECK-NEXT: %7 = load ptr, ptr %6, align 8
+// CHECK-NEXT: %8 = load i32, ptr %7, align 4
+// CHECK-NEXT: %add = add nsw i32 %8, %5
+// CHECK-NEXT: store i32 %add, ptr %7, align 4
+// CHECK-NEXT: br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT: %9 = load ptr, ptr %0, align 8
+// CHECK-NEXT: %y = getelementptr inbounds nuw %struct.E, ptr %9, i32 0, i32 1
+// CHECK-NEXT: %10 = load i32, ptr %y, align 4
+// CHECK-NEXT: store i32 %10, ptr %x3, align 4
+// CHECK-NEXT: %11 = load i32, ptr %x3, align 4
+// CHECK-NEXT: %12 = getelementptr inbounds nuw %class.anon, ptr %this1, i32 0, i32 1
+// CHECK-NEXT: %13 = load ptr, ptr %12, align 8
+// CHECK-NEXT: %14 = load i32, ptr %13, align 4
+// CHECK-NEXT: %add4 = add nsw i32 %14, %11
+// CHECK-NEXT: store i32 %add4, ptr %13, align 4
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: call void @_ZN1EC1Eii(ptr {{.*}} %e, i32 {{.*}} 5, i32 {{.*}} 6)
+// CHECK-NEXT: %x5 = getelementptr inbounds nuw %struct.E, ptr %e, i32 0, i32 0
+// CHECK-NEXT: %15 = load i32, ptr %x5, align 4
+// CHECK-NEXT: %y6 = getelementptr inbounds nuw %struct.E, ptr %e, i32 0, i32 1
+// CHECK-NEXT: %16 = load i32, ptr %y6, align 4
+// CHECK-NEXT: %add7 = add nsw i32 %15, %16
+// CHECK-NEXT: %17 = getelementptr inbounds nuw %class.anon, ptr %this1, i32 0, i32 1
+// CHECK-NEXT: %18 = load ptr, ptr %17, align 8
+// CHECK-NEXT: %19 = load i32, ptr %18, align 4
+// CHECK-NEXT: %add8 = add nsw i32 %19, %add7
+// CHECK-NEXT: store i32 %add8, ptr %18, align 4
+// CHECK-NEXT: br label %expand.next9
+// CHECK: expand.next9:
+// CHECK-NEXT: call void @_ZN1EC1Eii(ptr {{.*}} %e10, i32 {{.*}} 7, i32 {{.*}} 8)
+// CHECK-NEXT: %x11 = getelementptr inbounds nuw %struct.E, ptr %e10, i32 0, i32 0
+// CHECK-NEXT: %20 = load i32, ptr %x11, align 4
+// CHECK-NEXT: %y12 = getelementptr inbounds nuw %struct.E, ptr %e10, i32 0, i32 1
+// CHECK-NEXT: %21 = load i32, ptr %y12, align 4
+// CHECK-NEXT: %add13 = add nsw i32 %20, %21
+// CHECK-NEXT: %22 = getelementptr inbounds nuw %class.anon, ptr %this1, i32 0, i32 1
+// CHECK-NEXT: %23 = load ptr, ptr %22, align 8
+// CHECK-NEXT: %24 = load i32, ptr %23, align 4
+// CHECK-NEXT: %add14 = add nsw i32 %24, %add13
+// CHECK-NEXT: store i32 %add14, ptr %23, align 4
+// CHECK-NEXT: br label %expand.end15
+// CHECK: expand.end15:
+// CHECK-NEXT: ret void
+
+
+// CHECK-LABEL: define {{.*}} void @_ZZ20unexpanded_pack_goodIJ1ES0_EEiDpT_ENKUlvE_clEv(ptr {{.*}} %this)
+// CHECK: entry:
+// CHECK-NEXT: %this.addr = alloca ptr, align 8
+// CHECK-NEXT: %0 = alloca ptr, align 8
+// CHECK-NEXT: %x = alloca i32, align 4
+// CHECK-NEXT: %x3 = alloca i32, align 4
+// CHECK-NEXT: %e = alloca %struct.E, align 4
+// CHECK-NEXT: %e10 = alloca %struct.E, align 4
+// CHECK-NEXT: store ptr %this, ptr %this.addr, align 8
+// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 8
+// CHECK-NEXT: %1 = getelementptr inbounds nuw %class.anon.0, ptr %this1, i32 0, i32 0
+// CHECK-NEXT: %2 = load ptr, ptr %1, align 8
+// CHECK-NEXT: store ptr %2, ptr %0, align 8
+// CHECK-NEXT: %3 = load ptr, ptr %0, align 8
+// CHECK-NEXT: %x2 = getelementptr inbounds nuw %struct.E, ptr %3, i32 0, i32 0
+// CHECK-NEXT: %4 = load i32, ptr %x2, align 4
+// CHECK-NEXT: store i32 %4, ptr %x, align 4
+// CHECK-NEXT: %5 = load i32, ptr %x, align 4
+// CHECK-NEXT: %6 = getelementptr inbounds nuw %class.anon.0, ptr %this1, i32 0, i32 1
+// CHECK-NEXT: %7 = load ptr, ptr %6, align 8
+// CHECK-NEXT: %8 = load i32, ptr %7, align 4
+// CHECK-NEXT: %add = add nsw i32 %8, %5
+// CHECK-NEXT: store i32 %add, ptr %7, align 4
+// CHECK-NEXT: br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT: %9 = load ptr, ptr %0, align 8
+// CHECK-NEXT: %y = getelementptr inbounds nuw %struct.E, ptr %9, i32 0, i32 1
+// CHECK-NEXT: %10 = load i32, ptr %y, align 4
+// CHECK-NEXT: store i32 %10, ptr %x3, align 4
+// CHECK-NEXT: %11 = load i32, ptr %x3, align 4
+// CHECK-NEXT: %12 = getelementptr inbounds nuw %class.anon.0, ptr %this1, i32 0, i32 1
+// CHECK-NEXT: %13 = load ptr, ptr %12, align 8
+// CHECK-NEXT: %14 = load i32, ptr %13, align 4
+// CHECK-NEXT: %add4 = add nsw i32 %14, %11
+// CHECK-NEXT: store i32 %add4, ptr %13, align 4
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: call void @_ZN1EC1Eii(ptr {{.*}} %e, i32 {{.*}} 5, i32 {{.*}} 6)
+// CHECK-NEXT: %x5 = getelementptr inbounds nuw %struct.E, ptr %e, i32 0, i32 0
+// CHECK-NEXT: %15 = load i32, ptr %x5, align 4
+// CHECK-NEXT: %y6 = getelementptr inbounds nuw %struct.E, ptr %e, i32 0, i32 1
+// CHECK-NEXT: %16 = load i32, ptr %y6, align 4
+// CHECK-NEXT: %add7 = add nsw i32 %15, %16
+// CHECK-NEXT: %17 = getelementptr inbounds nuw %class.anon.0, ptr %this1, i32 0, i32 1
+// CHECK-NEXT: %18 = load ptr, ptr %17, align 8
+// CHECK-NEXT: %19 = load i32, ptr %18, align 4
+// CHECK-NEXT: %add8 = add nsw i32 %19, %add7
+// CHECK-NEXT: store i32 %add8, ptr %18, align 4
+// CHECK-NEXT: br label %expand.next9
+// CHECK: expand.next9:
+// CHECK-NEXT: call void @_ZN1EC1Eii(ptr {{.*}} %e10, i32 {{.*}} 7, i32 {{.*}} 8)
+// CHECK-NEXT: %x11 = getelementptr inbounds nuw %struct.E, ptr %e10, i32 0, i32 0
+// CHECK-NEXT: %20 = load i32, ptr %x11, align 4
+// CHECK-NEXT: %y12 = getelementptr inbounds nuw %struct.E, ptr %e10, i32 0, i32 1
+// CHECK-NEXT: %21 = load i32, ptr %y12, align 4
+// CHECK-NEXT: %add13 = add nsw i32 %20, %21
+// CHECK-NEXT: %22 = getelementptr inbounds nuw %class.anon.0, ptr %this1, i32 0, i32 1
+// CHECK-NEXT: %23 = load ptr, ptr %22, align 8
+// CHECK-NEXT: %24 = load i32, ptr %23, align 4
+// CHECK-NEXT: %add14 = add nsw i32 %24, %add13
+// CHECK-NEXT: store i32 %add14, ptr %23, align 4
+// CHECK-NEXT: br label %expand.end15
+// CHECK: expand.end15:
+// CHECK-NEXT: ret void
diff --git a/clang/test/CodeGenCXX/cxx2c-iterating-expansion-stmt.cpp b/clang/test/CodeGenCXX/cxx2c-iterating-expansion-stmt.cpp
new file mode 100644
index 0000000000000..12c8328f41be0
--- /dev/null
+++ b/clang/test/CodeGenCXX/cxx2c-iterating-expansion-stmt.cpp
@@ -0,0 +1,551 @@
+// RUN: %clang_cc1 -std=c++2c -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s
+
+// Iterating expansion statements are currently not supported.
+// XFAIL: *
+
+template <typename T, __SIZE_TYPE__ size>
+struct Array {
+ T data[size]{};
+ constexpr const T* begin() const { return data; }
+ constexpr const T* end() const { return data + size; }
+};
+
+int f1() {
+ static constexpr Array<int, 3> integers{1, 2, 3};
+ int sum = 0;
+ template for (auto x : integers) sum += x;
+ return sum;
+}
+
+int f2() {
+ static constexpr Array<int, 3> integers{1, 2, 3};
+ int sum = 0;
+ template for (constexpr auto x : integers) sum += x;
+ return sum;
+}
+
+int f3() {
+ static constexpr Array<int, 0> integers{};
+ int sum = 0;
+ template for (constexpr auto x : integers) {
+ static_assert(false, "not expanded");
+ sum += x;
+ }
+ return sum;
+}
+
+int f4() {
+ static constexpr Array<int, 2> a{1, 2};
+ static constexpr Array<int, 2> b{3, 4};
+ int sum = 0;
+
+ template for (auto x : a)
+ template for (auto y : b)
+ sum += x + y;
+
+ template for (constexpr auto x : a)
+ template for (constexpr auto y : b)
+ sum += x + y;
+
+ return sum;
+}
+
+struct Private {
+ static constexpr Array<int, 3> integers{1, 2, 3};
+ friend constexpr int friend_func();
+
+private:
+ constexpr const int* begin() const { return integers.begin(); }
+ constexpr const int* end() const { return integers.end(); }
+
+public:
+ static int member_func();
+};
+
+int Private::member_func() {
+ int sum = 0;
+ static constexpr Private p1;
+ template for (auto x : p1) sum += x;
+ return sum;
+}
+
+struct CustomIterator {
+ struct iterator {
+ int n;
+
+ constexpr iterator operator+(int m) const {
+ return {n + m};
+ }
+
+ constexpr void operator++() { ++n; }
+
+ constexpr int operator*() const {
+ return n;
+ }
+
+ friend constexpr bool operator!=(iterator a, iterator b) {
+ return a.n != b.n;
+ }
+
+ friend constexpr int operator-(iterator a, iterator b) {
+ return a.n - b.n;
+ }
+ };
+
+ constexpr iterator begin() const { return iterator(1); }
+ constexpr iterator end() const { return iterator(5); }
+};
+
+int custom_iterator() {
+ static constexpr CustomIterator c;
+ int sum = 0;
+ template for (auto x : c) sum += x;
+ template for (constexpr auto x : c) sum += x;
+ return sum;
+}
+
+// CHECK: @_ZZ2f1vE8integers = internal constant %struct.Array { [3 x i32] [i32 1, i32 2, i32 3] }, align 4
+// CHECK: @_ZZ2f2vE8integers = internal constant %struct.Array { [3 x i32] [i32 1, i32 2, i32 3] }, align 4
+// CHECK: @_ZZ2f3vE8integers = internal constant %struct.Array.0 zeroinitializer, align 4
+// CHECK: @_ZZ2f4vE1a = internal constant %struct.Array.1 { [2 x i32] [i32 1, i32 2] }, align 4
+// CHECK: @_ZZ2f4vE1b = internal constant %struct.Array.1 { [2 x i32] [i32 3, i32 4] }, align 4
+// CHECK: @_ZZN7Private11member_funcEvE2p1 = internal constant %struct.Private zeroinitializer, align 1
+// CHECK: @_ZZ15custom_iteratorvE1c = internal constant %struct.CustomIterator zeroinitializer, align 1
+// CHECK: @__const._Z15custom_iteratorv.__begin1 = private {{.*}} constant %"struct.CustomIterator::iterator" { i32 1 }, align 4
+// CHECK: @__const._Z15custom_iteratorv.__begin1.1 = private {{.*}} constant %"struct.CustomIterator::iterator" { i32 1 }, align 4
+// CHECK: @__const._Z15custom_iteratorv.__iter1 = private {{.*}} constant %"struct.CustomIterator::iterator" { i32 1 }, align 4
+// CHECK: @__const._Z15custom_iteratorv.__iter1.2 = private {{.*}} constant %"struct.CustomIterator::iterator" { i32 2 }, align 4
+// CHECK: @__const._Z15custom_iteratorv.__iter1.3 = private {{.*}} constant %"struct.CustomIterator::iterator" { i32 3 }, align 4
+// CHECK: @__const._Z15custom_iteratorv.__iter1.4 = private {{.*}} constant %"struct.CustomIterator::iterator" { i32 4 }, align 4
+// CHECK: @_ZN7Private8integersE = {{.*}} constant %struct.Array { [3 x i32] [i32 1, i32 2, i32 3] }, comdat, align 4
+
+// CHECK-LABEL: define {{.*}} i32 @_Z2f1v()
+// CHECK: entry:
+// CHECK-NEXT: %sum = alloca i32, align 4
+// CHECK-NEXT: %__range1 = alloca ptr, align 8
+// CHECK-NEXT: %__begin1 = alloca ptr, align 8
+// CHECK-NEXT: %__iter1 = alloca ptr, align 8
+// CHECK-NEXT: %x = alloca i32, align 4
+// CHECK-NEXT: %__iter11 = alloca ptr, align 8
+// CHECK-NEXT: %x3 = alloca i32, align 4
+// CHECK-NEXT: %__iter16 = alloca ptr, align 8
+// CHECK-NEXT: %x8 = alloca i32, align 4
+// CHECK-NEXT: store i32 0, ptr %sum, align 4
+// CHECK-NEXT: store ptr @_ZZ2f1vE8integers, ptr %__range1, align 8
+// CHECK-NEXT: %call = call {{.*}} ptr @_ZNK5ArrayIiLm3EE5beginEv(ptr {{.*}} @_ZZ2f1vE8integers)
+// CHECK-NEXT: store ptr %call, ptr %__begin1, align 8
+// CHECK-NEXT: %0 = load ptr, ptr %__begin1, align 8
+// CHECK-NEXT: %add.ptr = getelementptr inbounds i32, ptr %0, i64 0
+// CHECK-NEXT: store ptr %add.ptr, ptr %__iter1, align 8
+// CHECK-NEXT: %1 = load ptr, ptr %__iter1, align 8
+// CHECK-NEXT: %2 = load i32, ptr %1, align 4
+// CHECK-NEXT: store i32 %2, ptr %x, align 4
+// CHECK-NEXT: %3 = load i32, ptr %x, align 4
+// CHECK-NEXT: %4 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add = add nsw i32 %4, %3
+// CHECK-NEXT: store i32 %add, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT: %5 = load ptr, ptr %__begin1, align 8
+// CHECK-NEXT: %add.ptr2 = getelementptr inbounds i32, ptr %5, i64 1
+// CHECK-NEXT: store ptr %add.ptr2, ptr %__iter11, align 8
+// CHECK-NEXT: %6 = load ptr, ptr %__iter11, align 8
+// CHECK-NEXT: %7 = load i32, ptr %6, align 4
+// CHECK-NEXT: store i32 %7, ptr %x3, align 4
+// CHECK-NEXT: %8 = load i32, ptr %x3, align 4
+// CHECK-NEXT: %9 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add4 = add nsw i32 %9, %8
+// CHECK-NEXT: store i32 %add4, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next5
+// CHECK: expand.next5:
+// CHECK-NEXT: %10 = load ptr, ptr %__begin1, align 8
+// CHECK-NEXT: %add.ptr7 = getelementptr inbounds i32, ptr %10, i64 2
+// CHECK-NEXT: store ptr %add.ptr7, ptr %__iter16, align 8
+// CHECK-NEXT: %11 = load ptr, ptr %__iter16, align 8
+// CHECK-NEXT: %12 = load i32, ptr %11, align 4
+// CHECK-NEXT: store i32 %12, ptr %x8, align 4
+// CHECK-NEXT: %13 = load i32, ptr %x8, align 4
+// CHECK-NEXT: %14 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add9 = add nsw i32 %14, %13
+// CHECK-NEXT: store i32 %add9, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: %15 = load i32, ptr %sum, align 4
+// CHECK-NEXT: ret i32 %15
+
+
+// CHECK-LABEL: define {{.*}} i32 @_Z2f2v()
+// CHECK: entry:
+// CHECK-NEXT: %sum = alloca i32, align 4
+// CHECK-NEXT: %__range1 = alloca ptr, align 8
+// CHECK-NEXT: %__begin1 = alloca ptr, align 8
+// CHECK-NEXT: %__iter1 = alloca ptr, align 8
+// CHECK-NEXT: %x = alloca i32, align 4
+// CHECK-NEXT: %__iter11 = alloca ptr, align 8
+// CHECK-NEXT: %x2 = alloca i32, align 4
+// CHECK-NEXT: %__iter15 = alloca ptr, align 8
+// CHECK-NEXT: %x6 = alloca i32, align 4
+// CHECK-NEXT: store i32 0, ptr %sum, align 4
+// CHECK-NEXT: store ptr @_ZZ2f2vE8integers, ptr %__range1, align 8
+// CHECK-NEXT: store ptr @_ZZ2f2vE8integers, ptr %__begin1, align 8
+// CHECK-NEXT: store ptr @_ZZ2f2vE8integers, ptr %__iter1, align 8
+// CHECK-NEXT: store i32 1, ptr %x, align 4
+// CHECK-NEXT: %0 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add = add nsw i32 %0, 1
+// CHECK-NEXT: store i32 %add, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT: store ptr getelementptr (i8, ptr @_ZZ2f2vE8integers, i64 4), ptr %__iter11, align 8
+// CHECK-NEXT: store i32 2, ptr %x2, align 4
+// CHECK-NEXT: %1 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add3 = add nsw i32 %1, 2
+// CHECK-NEXT: store i32 %add3, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next4
+// CHECK: expand.next4:
+// CHECK-NEXT: store ptr getelementptr (i8, ptr @_ZZ2f2vE8integers, i64 8), ptr %__iter15, align 8
+// CHECK-NEXT: store i32 3, ptr %x6, align 4
+// CHECK-NEXT: %2 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add7 = add nsw i32 %2, 3
+// CHECK-NEXT: store i32 %add7, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: %3 = load i32, ptr %sum, align 4
+// CHECK-NEXT: ret i32 %3
+
+
+// CHECK-LABEL: define {{.*}} i32 @_Z2f3v()
+// CHECK: entry:
+// CHECK-NEXT: %sum = alloca i32, align 4
+// CHECK-NEXT: %__range1 = alloca ptr, align 8
+// CHECK-NEXT: %__begin1 = alloca ptr, align 8
+// CHECK-NEXT: store i32 0, ptr %sum, align 4
+// CHECK-NEXT: store ptr @_ZZ2f3vE8integers, ptr %__range1, align 8
+// CHECK-NEXT: store ptr @_ZZ2f3vE8integers, ptr %__begin1, align 8
+// CHECK-NEXT: %0 = load i32, ptr %sum, align 4
+// CHECK-NEXT: ret i32 %0
+
+
+// CHECK-LABEL: define {{.*}} i32 @_Z2f4v()
+// CHECK: entry:
+// CHECK-NEXT: %sum = alloca i32, align 4
+// CHECK-NEXT: %__range1 = alloca ptr, align 8
+// CHECK-NEXT: %__begin1 = alloca ptr, align 8
+// CHECK-NEXT: %__iter1 = alloca ptr, align 8
+// CHECK-NEXT: %x = alloca i32, align 4
+// CHECK-NEXT: %__range2 = alloca ptr, align 8
+// CHECK-NEXT: %__begin2 = alloca ptr, align 8
+// CHECK-NEXT: %__iter2 = alloca ptr, align 8
+// CHECK-NEXT: %y = alloca i32, align 4
+// CHECK-NEXT: %__iter24 = alloca ptr, align 8
+// CHECK-NEXT: %y6 = alloca i32, align 4
+// CHECK-NEXT: %__iter110 = alloca ptr, align 8
+// CHECK-NEXT: %x12 = alloca i32, align 4
+// CHECK-NEXT: %__range213 = alloca ptr, align 8
+// CHECK-NEXT: %__begin214 = alloca ptr, align 8
+// CHECK-NEXT: %__iter216 = alloca ptr, align 8
+// CHECK-NEXT: %y18 = alloca i32, align 4
+// CHECK-NEXT: %__iter222 = alloca ptr, align 8
+// CHECK-NEXT: %y24 = alloca i32, align 4
+// CHECK-NEXT: %__range129 = alloca ptr, align 8
+// CHECK-NEXT: %__begin130 = alloca ptr, align 8
+// CHECK-NEXT: %__iter131 = alloca ptr, align 8
+// CHECK-NEXT: %x32 = alloca i32, align 4
+// CHECK-NEXT: %__range233 = alloca ptr, align 8
+// CHECK-NEXT: %__begin234 = alloca ptr, align 8
+// CHECK-NEXT: %__iter235 = alloca ptr, align 8
+// CHECK-NEXT: %y36 = alloca i32, align 4
+// CHECK-NEXT: %__iter239 = alloca ptr, align 8
+// CHECK-NEXT: %y40 = alloca i32, align 4
+// CHECK-NEXT: %__iter144 = alloca ptr, align 8
+// CHECK-NEXT: %x45 = alloca i32, align 4
+// CHECK-NEXT: %__range246 = alloca ptr, align 8
+// CHECK-NEXT: %__begin247 = alloca ptr, align 8
+// CHECK-NEXT: %__iter248 = alloca ptr, align 8
+// CHECK-NEXT: %y49 = alloca i32, align 4
+// CHECK-NEXT: %__iter252 = alloca ptr, align 8
+// CHECK-NEXT: %y53 = alloca i32, align 4
+// CHECK-NEXT: store i32 0, ptr %sum, align 4
+// CHECK-NEXT: store ptr @_ZZ2f4vE1a, ptr %__range1, align 8
+// CHECK-NEXT: %call = call {{.*}} ptr @_ZNK5ArrayIiLm2EE5beginEv(ptr {{.*}} @_ZZ2f4vE1a)
+// CHECK-NEXT: store ptr %call, ptr %__begin1, align 8
+// CHECK-NEXT: %0 = load ptr, ptr %__begin1, align 8
+// CHECK-NEXT: %add.ptr = getelementptr inbounds i32, ptr %0, i64 0
+// CHECK-NEXT: store ptr %add.ptr, ptr %__iter1, align 8
+// CHECK-NEXT: %1 = load ptr, ptr %__iter1, align 8
+// CHECK-NEXT: %2 = load i32, ptr %1, align 4
+// CHECK-NEXT: store i32 %2, ptr %x, align 4
+// CHECK-NEXT: store ptr @_ZZ2f4vE1b, ptr %__range2, align 8
+// CHECK-NEXT: %call1 = call {{.*}} ptr @_ZNK5ArrayIiLm2EE5beginEv(ptr {{.*}} @_ZZ2f4vE1b)
+// CHECK-NEXT: store ptr %call1, ptr %__begin2, align 8
+// CHECK-NEXT: %3 = load ptr, ptr %__begin2, align 8
+// CHECK-NEXT: %add.ptr2 = getelementptr inbounds i32, ptr %3, i64 0
+// CHECK-NEXT: store ptr %add.ptr2, ptr %__iter2, align 8
+// CHECK-NEXT: %4 = load ptr, ptr %__iter2, align 8
+// CHECK-NEXT: %5 = load i32, ptr %4, align 4
+// CHECK-NEXT: store i32 %5, ptr %y, align 4
+// CHECK-NEXT: %6 = load i32, ptr %x, align 4
+// CHECK-NEXT: %7 = load i32, ptr %y, align 4
+// CHECK-NEXT: %add = add nsw i32 %6, %7
+// CHECK-NEXT: %8 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add3 = add nsw i32 %8, %add
+// CHECK-NEXT: store i32 %add3, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT: %9 = load ptr, ptr %__begin2, align 8
+// CHECK-NEXT: %add.ptr5 = getelementptr inbounds i32, ptr %9, i64 1
+// CHECK-NEXT: store ptr %add.ptr5, ptr %__iter24, align 8
+// CHECK-NEXT: %10 = load ptr, ptr %__iter24, align 8
+// CHECK-NEXT: %11 = load i32, ptr %10, align 4
+// CHECK-NEXT: store i32 %11, ptr %y6, align 4
+// CHECK-NEXT: %12 = load i32, ptr %x, align 4
+// CHECK-NEXT: %13 = load i32, ptr %y6, align 4
+// CHECK-NEXT: %add7 = add nsw i32 %12, %13
+// CHECK-NEXT: %14 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add8 = add nsw i32 %14, %add7
+// CHECK-NEXT: store i32 %add8, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: br label %expand.next9
+// CHECK: expand.next9:
+// CHECK-NEXT: %15 = load ptr, ptr %__begin1, align 8
+// CHECK-NEXT: %add.ptr11 = getelementptr inbounds i32, ptr %15, i64 1
+// CHECK-NEXT: store ptr %add.ptr11, ptr %__iter110, align 8
+// CHECK-NEXT: %16 = load ptr, ptr %__iter110, align 8
+// CHECK-NEXT: %17 = load i32, ptr %16, align 4
+// CHECK-NEXT: store i32 %17, ptr %x12, align 4
+// CHECK-NEXT: store ptr @_ZZ2f4vE1b, ptr %__range213, align 8
+// CHECK-NEXT: %call15 = call {{.*}} ptr @_ZNK5ArrayIiLm2EE5beginEv(ptr {{.*}} @_ZZ2f4vE1b)
+// CHECK-NEXT: store ptr %call15, ptr %__begin214, align 8
+// CHECK-NEXT: %18 = load ptr, ptr %__begin214, align 8
+// CHECK-NEXT: %add.ptr17 = getelementptr inbounds i32, ptr %18, i64 0
+// CHECK-NEXT: store ptr %add.ptr17, ptr %__iter216, align 8
+// CHECK-NEXT: %19 = load ptr, ptr %__iter216, align 8
+// CHECK-NEXT: %20 = load i32, ptr %19, align 4
+// CHECK-NEXT: store i32 %20, ptr %y18, align 4
+// CHECK-NEXT: %21 = load i32, ptr %x12, align 4
+// CHECK-NEXT: %22 = load i32, ptr %y18, align 4
+// CHECK-NEXT: %add19 = add nsw i32 %21, %22
+// CHECK-NEXT: %23 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add20 = add nsw i32 %23, %add19
+// CHECK-NEXT: store i32 %add20, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next21
+// CHECK: expand.next21:
+// CHECK-NEXT: %24 = load ptr, ptr %__begin214, align 8
+// CHECK-NEXT: %add.ptr23 = getelementptr inbounds i32, ptr %24, i64 1
+// CHECK-NEXT: store ptr %add.ptr23, ptr %__iter222, align 8
+// CHECK-NEXT: %25 = load ptr, ptr %__iter222, align 8
+// CHECK-NEXT: %26 = load i32, ptr %25, align 4
+// CHECK-NEXT: store i32 %26, ptr %y24, align 4
+// CHECK-NEXT: %27 = load i32, ptr %x12, align 4
+// CHECK-NEXT: %28 = load i32, ptr %y24, align 4
+// CHECK-NEXT: %add25 = add nsw i32 %27, %28
+// CHECK-NEXT: %29 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add26 = add nsw i32 %29, %add25
+// CHECK-NEXT: store i32 %add26, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.end27
+// CHECK: expand.end27:
+// CHECK-NEXT: br label %expand.end28
+// CHECK: expand.end28:
+// CHECK-NEXT: store ptr @_ZZ2f4vE1a, ptr %__range129, align 8
+// CHECK-NEXT: store ptr @_ZZ2f4vE1a, ptr %__begin130, align 8
+// CHECK-NEXT: store ptr @_ZZ2f4vE1a, ptr %__iter131, align 8
+// CHECK-NEXT: store i32 1, ptr %x32, align 4
+// CHECK-NEXT: store ptr @_ZZ2f4vE1b, ptr %__range233, align 8
+// CHECK-NEXT: store ptr @_ZZ2f4vE1b, ptr %__begin234, align 8
+// CHECK-NEXT: store ptr @_ZZ2f4vE1b, ptr %__iter235, align 8
+// CHECK-NEXT: store i32 3, ptr %y36, align 4
+// CHECK-NEXT: %30 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add37 = add nsw i32 %30, 4
+// CHECK-NEXT: store i32 %add37, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next38
+// CHECK: expand.next38:
+// CHECK-NEXT: store ptr getelementptr (i8, ptr @_ZZ2f4vE1b, i64 4), ptr %__iter239, align 8
+// CHECK-NEXT: store i32 4, ptr %y40, align 4
+// CHECK-NEXT: %31 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add41 = add nsw i32 %31, 5
+// CHECK-NEXT: store i32 %add41, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.end42
+// CHECK: expand.end42:
+// CHECK-NEXT: br label %expand.next43
+// CHECK: expand.next43:
+// CHECK-NEXT: store ptr getelementptr (i8, ptr @_ZZ2f4vE1a, i64 4), ptr %__iter144, align 8
+// CHECK-NEXT: store i32 2, ptr %x45, align 4
+// CHECK-NEXT: store ptr @_ZZ2f4vE1b, ptr %__range246, align 8
+// CHECK-NEXT: store ptr @_ZZ2f4vE1b, ptr %__begin247, align 8
+// CHECK-NEXT: store ptr @_ZZ2f4vE1b, ptr %__iter248, align 8
+// CHECK-NEXT: store i32 3, ptr %y49, align 4
+// CHECK-NEXT: %32 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add50 = add nsw i32 %32, 5
+// CHECK-NEXT: store i32 %add50, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next51
+// CHECK: expand.next51:
+// CHECK-NEXT: store ptr getelementptr (i8, ptr @_ZZ2f4vE1b, i64 4), ptr %__iter252, align 8
+// CHECK-NEXT: store i32 4, ptr %y53, align 4
+// CHECK-NEXT: %33 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add54 = add nsw i32 %33, 6
+// CHECK-NEXT: store i32 %add54, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.end55
+// CHECK: expand.end55:
+// CHECK-NEXT: br label %expand.end56
+// CHECK: expand.end56:
+// CHECK-NEXT: %34 = load i32, ptr %sum, align 4
+// CHECK-NEXT: ret i32 %34
+
+
+// CHECK-LABEL: define {{.*}} i32 @_ZN7Private11member_funcEv()
+// CHECK: entry:
+// CHECK-NEXT: %sum = alloca i32, align 4
+// CHECK-NEXT: %__range1 = alloca ptr, align 8
+// CHECK-NEXT: %__begin1 = alloca ptr, align 8
+// CHECK-NEXT: %__iter1 = alloca ptr, align 8
+// CHECK-NEXT: %x = alloca i32, align 4
+// CHECK-NEXT: %__iter11 = alloca ptr, align 8
+// CHECK-NEXT: %x3 = alloca i32, align 4
+// CHECK-NEXT: %__iter16 = alloca ptr, align 8
+// CHECK-NEXT: %x8 = alloca i32, align 4
+// CHECK-NEXT: store i32 0, ptr %sum, align 4
+// CHECK-NEXT: store ptr @_ZZN7Private11member_funcEvE2p1, ptr %__range1, align 8
+// CHECK-NEXT: %call = call {{.*}} ptr @_ZNK7Private5beginEv(ptr {{.*}} @_ZZN7Private11member_funcEvE2p1)
+// CHECK-NEXT: store ptr %call, ptr %__begin1, align 8
+// CHECK-NEXT: %0 = load ptr, ptr %__begin1, align 8
+// CHECK-NEXT: %add.ptr = getelementptr inbounds i32, ptr %0, i64 0
+// CHECK-NEXT: store ptr %add.ptr, ptr %__iter1, align 8
+// CHECK-NEXT: %1 = load ptr, ptr %__iter1, align 8
+// CHECK-NEXT: %2 = load i32, ptr %1, align 4
+// CHECK-NEXT: store i32 %2, ptr %x, align 4
+// CHECK-NEXT: %3 = load i32, ptr %x, align 4
+// CHECK-NEXT: %4 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add = add nsw i32 %4, %3
+// CHECK-NEXT: store i32 %add, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT: %5 = load ptr, ptr %__begin1, align 8
+// CHECK-NEXT: %add.ptr2 = getelementptr inbounds i32, ptr %5, i64 1
+// CHECK-NEXT: store ptr %add.ptr2, ptr %__iter11, align 8
+// CHECK-NEXT: %6 = load ptr, ptr %__iter11, align 8
+// CHECK-NEXT: %7 = load i32, ptr %6, align 4
+// CHECK-NEXT: store i32 %7, ptr %x3, align 4
+// CHECK-NEXT: %8 = load i32, ptr %x3, align 4
+// CHECK-NEXT: %9 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add4 = add nsw i32 %9, %8
+// CHECK-NEXT: store i32 %add4, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next5
+// CHECK: expand.next5:
+// CHECK-NEXT: %10 = load ptr, ptr %__begin1, align 8
+// CHECK-NEXT: %add.ptr7 = getelementptr inbounds i32, ptr %10, i64 2
+// CHECK-NEXT: store ptr %add.ptr7, ptr %__iter16, align 8
+// CHECK-NEXT: %11 = load ptr, ptr %__iter16, align 8
+// CHECK-NEXT: %12 = load i32, ptr %11, align 4
+// CHECK-NEXT: store i32 %12, ptr %x8, align 4
+// CHECK-NEXT: %13 = load i32, ptr %x8, align 4
+// CHECK-NEXT: %14 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add9 = add nsw i32 %14, %13
+// CHECK-NEXT: store i32 %add9, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: %15 = load i32, ptr %sum, align 4
+// CHECK-NEXT: ret i32 %15
+
+
+// CHECK-LABEL: define {{.*}} i32 @_Z15custom_iteratorv()
+// CHECK: entry:
+// CHECK-NEXT: %sum = alloca i32, align 4
+// CHECK-NEXT: %__range1 = alloca ptr, align 8
+// CHECK: %__begin1 = alloca %"struct.CustomIterator::iterator", align 4
+// CHECK: %__iter1 = alloca %"struct.CustomIterator::iterator", align 4
+// CHECK-NEXT: %x = alloca i32, align 4
+// CHECK: %__iter12 = alloca %"struct.CustomIterator::iterator", align 4
+// CHECK-NEXT: %x5 = alloca i32, align 4
+// CHECK: %__iter19 = alloca %"struct.CustomIterator::iterator", align 4
+// CHECK-NEXT: %x12 = alloca i32, align 4
+// CHECK: %__iter116 = alloca %"struct.CustomIterator::iterator", align 4
+// CHECK-NEXT: %x19 = alloca i32, align 4
+// CHECK-NEXT: %__range122 = alloca ptr, align 8
+// CHECK: %__begin123 = alloca %"struct.CustomIterator::iterator", align 4
+// CHECK: %__iter124 = alloca %"struct.CustomIterator::iterator", align 4
+// CHECK-NEXT: %x25 = alloca i32, align 4
+// CHECK: %__iter128 = alloca %"struct.CustomIterator::iterator", align 4
+// CHECK-NEXT: %x29 = alloca i32, align 4
+// CHECK: %__iter132 = alloca %"struct.CustomIterator::iterator", align 4
+// CHECK-NEXT: %x33 = alloca i32, align 4
+// CHECK: %__iter136 = alloca %"struct.CustomIterator::iterator", align 4
+// CHECK-NEXT: %x37 = alloca i32, align 4
+// CHECK-NEXT: store i32 0, ptr %sum, align 4
+// CHECK-NEXT: store ptr @_ZZ15custom_iteratorvE1c, ptr %__range1, align 8
+// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %__begin1, ptr align 4 @__const._Z15custom_iteratorv.__begin1, i64 4, i1 false)
+// CHECK-NEXT: %call = call i32 @_ZNK14CustomIterator8iteratorplEi(ptr {{.*}} %__begin1, i32 {{.*}} 0)
+// CHECK: %coerce.dive = getelementptr inbounds nuw %"struct.CustomIterator::iterator", ptr %__iter1, i32 0, i32 0
+// CHECK-NEXT: store i32 %call, ptr %coerce.dive, align 4
+// CHECK-NEXT: %call1 = call {{.*}} i32 @_ZNK14CustomIterator8iteratordeEv(ptr {{.*}} %__iter1)
+// CHECK-NEXT: store i32 %call1, ptr %x, align 4
+// CHECK-NEXT: %0 = load i32, ptr %x, align 4
+// CHECK-NEXT: %1 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add = add nsw i32 %1, %0
+// CHECK-NEXT: store i32 %add, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next
+// CHECK: expand.next:
+// CHECK-NEXT: %call3 = call i32 @_ZNK14CustomIterator8iteratorplEi(ptr {{.*}} %__begin1, i32 {{.*}} 1)
+// CHECK: %coerce.dive4 = getelementptr inbounds nuw %"struct.CustomIterator::iterator", ptr %__iter12, i32 0, i32 0
+// CHECK-NEXT: store i32 %call3, ptr %coerce.dive4, align 4
+// CHECK-NEXT: %call6 = call {{.*}} i32 @_ZNK14CustomIterator8iteratordeEv(ptr {{.*}} %__iter12)
+// CHECK-NEXT: store i32 %call6, ptr %x5, align 4
+// CHECK-NEXT: %2 = load i32, ptr %x5, align 4
+// CHECK-NEXT: %3 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add7 = add nsw i32 %3, %2
+// CHECK-NEXT: store i32 %add7, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next8
+// CHECK: expand.next8:
+// CHECK-NEXT: %call10 = call i32 @_ZNK14CustomIterator8iteratorplEi(ptr {{.*}} %__begin1, i32 {{.*}} 2)
+// CHECK: %coerce.dive11 = getelementptr inbounds nuw %"struct.CustomIterator::iterator", ptr %__iter19, i32 0, i32 0
+// CHECK-NEXT: store i32 %call10, ptr %coerce.dive11, align 4
+// CHECK-NEXT: %call13 = call {{.*}} i32 @_ZNK14CustomIterator8iteratordeEv(ptr {{.*}} %__iter19)
+// CHECK-NEXT: store i32 %call13, ptr %x12, align 4
+// CHECK-NEXT: %4 = load i32, ptr %x12, align 4
+// CHECK-NEXT: %5 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add14 = add nsw i32 %5, %4
+// CHECK-NEXT: store i32 %add14, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next15
+// CHECK: expand.next15:
+// CHECK-NEXT: %call17 = call i32 @_ZNK14CustomIterator8iteratorplEi(ptr {{.*}} %__begin1, i32 {{.*}} 3)
+// CHECK: %coerce.dive18 = getelementptr inbounds nuw %"struct.CustomIterator::iterator", ptr %__iter116, i32 0, i32 0
+// CHECK-NEXT: store i32 %call17, ptr %coerce.dive18, align 4
+// CHECK-NEXT: %call20 = call {{.*}} i32 @_ZNK14CustomIterator8iteratordeEv(ptr {{.*}} %__iter116)
+// CHECK-NEXT: store i32 %call20, ptr %x19, align 4
+// CHECK-NEXT: %6 = load i32, ptr %x19, align 4
+// CHECK-NEXT: %7 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add21 = add nsw i32 %7, %6
+// CHECK-NEXT: store i32 %add21, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.end
+// CHECK: expand.end:
+// CHECK-NEXT: store ptr @_ZZ15custom_iteratorvE1c, ptr %__range122, align 8
+// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %__begin123, ptr align 4 @__const._Z15custom_iteratorv.__begin1.1, i64 4, i1 false)
+// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %__iter124, ptr align 4 @__const._Z15custom_iteratorv.__iter1, i64 4, i1 false)
+// CHECK-NEXT: store i32 1, ptr %x25, align 4
+// CHECK-NEXT: %8 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add26 = add nsw i32 %8, 1
+// CHECK-NEXT: store i32 %add26, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next27
+// CHECK: expand.next27:
+// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %__iter128, ptr align 4 @__const._Z15custom_iteratorv.__iter1.2, i64 4, i1 false)
+// CHECK-NEXT: store i32 2, ptr %x29, align 4
+// CHECK-NEXT: %9 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add30 = add nsw i32 %9, 2
+// CHECK-NEXT: store i32 %add30, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next31
+// CHECK: expand.next31:
+// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %__iter132, ptr align 4 @__const._Z15custom_iteratorv.__iter1.3, i64 4, i1 false)
+// CHECK-NEXT: store i32 3, ptr %x33, align 4
+// CHECK-NEXT: %10 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add34 = add nsw i32 %10, 3
+// CHECK-NEXT: store i32 %add34, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.next35
+// CHECK: expand.next35:
+// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %__iter136, ptr align 4 @__const._Z15custom_iteratorv.__iter1.4, i64 4, i1 false)
+// CHECK-NEXT: store i32 4, ptr %x37, align 4
+// CHECK-NEXT: %11 = load i32, ptr %sum, align 4
+// CHECK-NEXT: %add38 = add nsw i32 %11, 4
+// CHECK-NEXT: store i32 %add38, ptr %sum, align 4
+// CHECK-NEXT: br label %expand.end39
+// CHECK: expand.end39:
+// CHECK-NEXT: %12 = load i32, ptr %sum, align 4
+// CHECK-NEXT: ret i32 %12
diff --git a/clang/test/Parser/cxx2c-expansion-statements-not-backported.cpp b/clang/test/Parser/cxx2c-expansion-statements-not-backported.cpp
new file mode 100644
index 0000000000000..0c0a229abeedc
--- /dev/null
+++ b/clang/test/Parser/cxx2c-expansion-statements-not-backported.cpp
@@ -0,0 +1,5 @@
+// RUN: %clang_cc1 %s -std=c++23 -fsyntax-only -verify
+
+void f() {
+ template for (char x : "123") {} // expected-error {{expansion statements are only supported in C++2c}}
+}
diff --git a/clang/test/Parser/cxx2c-expansion-statements.cpp b/clang/test/Parser/cxx2c-expansion-statements.cpp
new file mode 100644
index 0000000000000..736f9fead383c
--- /dev/null
+++ b/clang/test/Parser/cxx2c-expansion-statements.cpp
@@ -0,0 +1,68 @@
+// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -verify
+namespace std {
+template <typename T>
+struct initializer_list {
+ const T* a;
+ const T* b;
+ initializer_list(T*, T*) {}
+};
+}
+
+void bad() {
+ template for; // expected-error {{expected '(' after 'for'}}
+ template for (); // expected-error {{expected expression}} expected-error {{expected ';' in 'for' statement specifier}} expected-error {{expansion statement must use the syntax of a range-based for loop}}
+ template for (;); // expected-error {{expected ';' in 'for' statement specifier}} expected-error {{expansion statement must use the syntax of a range-based for loop}}
+ template for (;;); // expected-error {{expansion statement must use the syntax of a range-based for loop}}
+ template for (int x;;); // expected-error {{expansion statement must use the syntax of a range-based for loop}}
+ template for (x : {1}); // expected-error {{expansion statement requires type for expansion variable}}
+ template for (: {1}); // expected-error {{expected expression}} expected-error {{expected ';' in 'for' statement specifier}} expected-error {{expansion statement must use the syntax of a range-based for loop}}
+ template for (auto y : {1})]; // expected-error {{expected expression}}
+ template for (auto y : {1}; // expected-error {{expected ')'}} expected-note {{to match this '('}}
+ template for (extern auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'extern'}}
+ template for (register auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'register'}} expected-error {{ISO C++17 does not allow 'register' storage class specifier}}
+ template for (__private_extern__ auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'extern'}}
+ template for (extern static auto y : {1, 2}); // expected-error {{cannot combine with previous 'extern' declaration specifier}} expected-error {{expansion variable 'y' may not be declared 'extern'}}
+ template for (static auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}}
+ template for (thread_local auto y : {1, 2}); // expected-error {{'thread_local' variables must have global storage}}
+ template for (static thread_local auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'thread_local'}}
+ template for (__thread auto y : {1, 2}); // expected-error {{'__thread' variables must have global storage}}
+ template for (static __thread auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}}
+ template for (constinit auto y : {1, 2}); // expected-error {{local variable cannot be declared 'constinit'}}
+ template for (consteval auto y : {1, 2}); // expected-error {{consteval can only be used in function declarations}}
+ template for (int x; extern auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'extern'}}
+ template for (int x; extern static auto y : {1, 2}); // expected-error {{cannot combine with previous 'extern' declaration specifier}} expected-error {{expansion variable 'y' may not be declared 'extern'}}
+ template for (int x; static auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}}
+ template for (int x; thread_local auto y : {1, 2}); // expected-error {{'thread_local' variables must have global storage}}
+ template for (int x; static thread_local auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'thread_local'}}
+ template for (int x; __thread auto y : {1, 2}); // expected-error {{'__thread' variables must have global storage}}
+ template for (int x; static __thread auto y : {1, 2}); // expected-error {{expansion variable 'y' may not be declared 'static'}}
+ template for (int x; constinit auto y : {1, 2}); // expected-error {{local variable cannot be declared 'constinit'}}
+ template for (int x; consteval auto y : {1, 2}); // expected-error {{consteval can only be used in function declarations}}
+ template for (auto y : {abc, -+, }); // expected-error {{use of undeclared identifier 'abc'}} expected-error {{expected expression}}
+ template for (3 : "error") // expected-error {{expansion statement declaration must declare a variable}} \
+ expected-error {{expansion statement must use the syntax of a range-based for loop}}
+ ;
+ template while (true) {} // expected-error {{expected '<' after 'template'}}
+ ; // Semicolon for synchronisation; otherwise, the parser skips over next statement...
+ template do {} while (true); // expected-error {{expected '<' after 'template'}}
+ for template (int x : {}) {} // expected-error {{'for template' is invalid; use 'template for' instead}}
+}
+
+void good() {
+ template for (auto y : {});
+ template for (auto y : {1, 2});
+ template for (int x; auto y : {1, 2});
+ template for (int x; int y : {1, 2});
+ template for (int x; constexpr auto y : {1, 2});
+ template for (int x; constexpr int y : {1, 2});
+ template for (constexpr int a : {1, 2}) {
+ template for (constexpr int b : {1, 2}) {
+ template for (constexpr int c : {1, 2});
+ }
+ }
+}
+
+void trailing_comma() {
+ template for (int x : {1, 2,}) {}
+ template for (int x : {,}) {} // expected-error {{expected expression}}
+}
diff --git a/clang/test/SemaCXX/cxx2c-expansion-statements-shadow.cpp b/clang/test/SemaCXX/cxx2c-expansion-statements-shadow.cpp
new file mode 100644
index 0000000000000..c82fd0b5e39be
--- /dev/null
+++ b/clang/test/SemaCXX/cxx2c-expansion-statements-shadow.cpp
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -Wshadow -verify
+// expected-no-diagnostics
+
+// Test that -Wshadow doesn't fire on implicit variable declarations
+// introduced in the expansion of an expansion statement.
+
+void f() {
+ int a[4];
+ template for (int __u0; auto x : a) {}
+ template for (auto x : a) { int __u0; }
+}
diff --git a/clang/test/SemaCXX/cxx2c-expansion-stmts-control-flow.cpp b/clang/test/SemaCXX/cxx2c-expansion-stmts-control-flow.cpp
new file mode 100644
index 0000000000000..51b383d63cdcb
--- /dev/null
+++ b/clang/test/SemaCXX/cxx2c-expansion-stmts-control-flow.cpp
@@ -0,0 +1,135 @@
+// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -fblocks -verify
+
+void g(int);
+
+void label() {
+ template for (auto x : {1, 2}) {
+ invalid1:; // expected-error {{labels are not allowed in expansion statements}}
+ invalid2:; // expected-error {{labels are not allowed in expansion statements}}
+ goto invalid1; // expected-error {{use of undeclared label 'invalid1'}}
+ }
+
+ template for (auto x : {1, 2}) {
+ (void) [] {
+ template for (auto x : {1, 2}) {
+ invalid3:; // expected-error {{labels are not allowed in expansion statements}}
+ }
+ ok:;
+ };
+
+ (void) ^{
+ template for (auto x : {1, 2}) {
+ invalid4:; // expected-error {{labels are not allowed in expansion statements}}
+ }
+ ok:;
+ };
+
+ struct X {
+ void f() {
+ ok:;
+ }
+ };
+ }
+
+ // GNU local labels are allowed.
+ template for (auto x : {1, 2}) {
+ __label__ a;
+ if (x == 1) goto a;
+ a:;
+ if (x == 1) goto a;
+ }
+
+ // Likewise, jumping *out* of an expansion statement is fine.
+ template for (auto x : {1, 2}) {
+ if (x == 1) goto lbl;
+ g(x);
+ }
+ lbl:;
+ template for (auto x : {1, 2}) {
+ if (x == 1) goto lbl;
+ g(x);
+ }
+
+ // Jumping into one is not possible, as local labels aren't visible
+ // outside the block that declares them, and non-local labels are invalid.
+ goto exp1; // expected-error {{use of undeclared label 'exp1'}}
+ goto exp3; // expected-error {{use of undeclared label 'exp3'}}
+ template for (auto x : {1, 2}) {
+ __label__ exp1, exp2;
+ exp1:;
+ exp2:;
+ exp3:; // expected-error {{labels are not allowed in expansion statements}}
+ }
+ goto exp2; // expected-error {{use of undeclared label 'exp2'}}
+
+ // Allow jumping from inside an expansion statement to a local label in
+ // one of its parents.
+ out1:;
+ template for (auto x : {1, 2}) {
+ __label__ x, y;
+ x:
+ goto out1;
+ goto out2;
+ template for (auto x : {3, 4}) {
+ goto x;
+ goto y;
+ goto out1;
+ goto out2;
+ }
+ y:
+ }
+ out2:;
+}
+
+
+void case_default(int i) {
+ switch (i) { // expected-note 3 {{switch statement is here}}
+ template for (auto x : {1, 2}) {
+ case 1:; // expected-error {{'case' belongs to 'switch' outside enclosing expansion statement}}
+ template for (auto x : {1, 2}) {
+ case 2:; // expected-error {{'case' belongs to 'switch' outside enclosing expansion statement}}
+ }
+ default: // expected-error {{'default' belongs to 'switch' outside enclosing expansion statement}}
+ switch (i) { // expected-note {{switch statement is here}}
+ case 3:;
+ default:
+ template for (auto x : {1, 2}) {
+ case 4:; // expected-error {{'case' belongs to 'switch' outside enclosing expansion statement}}
+ }
+ }
+ }
+ }
+
+ template for (auto x : {1, 2}) {
+ switch (i) {
+ case 1:;
+ default:
+ }
+ }
+
+ // Ensure that we diagnose this even if the statements would be discarded.
+ switch (i) { // expected-note 2 {{switch statement is here}}
+ template for (auto x : {}) {
+ case 1:; // expected-error {{'case' belongs to 'switch' outside enclosing expansion statement}}
+ default:; // expected-error {{'default' belongs to 'switch' outside enclosing expansion statement}}
+ }
+ }
+}
+
+void case_constexpr(int i) {
+ template for (constexpr auto x : {1, 2, 3}) { // expected-note {{in instantiation of expansion statement requested here}}
+ switch (i) {
+ case x:; // expected-note {{previous case defined here}}
+ case 2:; // expected-error {{duplicate case value: 'x' and '2' both equal '2'}}
+ default:;
+ }
+ }
+
+ template for (constexpr auto x : {1, 2, 3}) {
+ switch (i) {
+ case x:;
+ case 4:;
+ default:;
+ }
+ }
+}
diff --git a/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp b/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp
new file mode 100644
index 0000000000000..f98ddd0fa949d
--- /dev/null
+++ b/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp
@@ -0,0 +1,1590 @@
+// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -fdeclspec -fblocks -Wno-vla-cxx-extension -fconstexpr-steps=10000 -verify=expected,old-interp
+// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -fdeclspec -fblocks -Wno-vla-cxx-extension -fconstexpr-steps=10000 -verify=expected,new-interp -fexperimental-new-constant-interpreter
+namespace std {
+template <typename T>
+struct initializer_list {
+ const T* a;
+ const T* b;
+ initializer_list(T* a, T* b): a{a}, b{b} {}
+};
+}
+
+struct S {
+ int x;
+ constexpr S(int x) : x{x} {}
+};
+
+void g(int); // #g
+template <int n> constexpr int tg() { return n; }
+
+void f1() {
+ template for (auto x : {}) static_assert(false, "discarded");
+ template for (constexpr auto x : {}) static_assert(false, "discarded");
+ template for (auto x : {1}) g(x);
+ template for (auto x : {1, 2, 3}) g(x);
+ template for (constexpr auto x : {1}) g(x);
+ template for (constexpr auto x : {1, 2, 3}) g(x);
+ template for (constexpr auto x : {1}) tg<x>();
+ template for (constexpr auto x : {1, 2, 3})
+ static_assert(tg<x>());
+
+ template for (int x : {1, 2, 3}) g(x);
+ template for (S x : {1, 2, 3}) g(x.x);
+ template for (constexpr S x : {1, 2, 3}) tg<x.x>();
+
+ template for (int x : {"1", S(1), {1, 2}}) { // expected-error {{cannot initialize a variable of type 'int' with an lvalue of type 'const char[2]'}} \
+ expected-error {{no viable conversion from 'S' to 'int'}} \
+ expected-error {{excess elements in scalar initializer}} \
+ expected-note 3 {{in instantiation of expansion statement requested here}}
+ g(x);
+ }
+
+ template for (constexpr auto x : {1, 2, 3, 4}) { // expected-note 3 {{in instantiation of expansion statement requested here}}
+ static_assert(tg<x>() == 4); // expected-error 3 {{static assertion failed due to requirement 'tg<x>() == 4'}} \
+ expected-note {{expression evaluates to '1 == 4'}} \
+ expected-note {{expression evaluates to '2 == 4'}} \
+ expected-note {{expression evaluates to '3 == 4'}}
+ }
+
+
+ template for (constexpr auto x : {1, 2}) { // expected-note 2 {{in instantiation of expansion statement requested here}}
+ static_assert(false, "not discarded"); // expected-error 2 {{static assertion failed: not discarded}}
+ }
+}
+
+template <typename T>
+void t1() {
+ template for (T x : {}) g(x);
+ template for (constexpr T x : {}) g(x);
+ template for (auto x : {}) g(x);
+ template for (constexpr auto x : {}) g(x);
+ template for (T x : {1, 2}) g(x);
+ template for (T x : {T(1), T(2)}) g(x);
+ template for (auto x : {T(1), T(2)}) g(x);
+ template for (constexpr T x : {T(1), T(2)}) static_assert(tg<x>());
+ template for (constexpr auto x : {T(1), T(2)}) static_assert(tg<x>());
+}
+
+template <typename U>
+struct s1 {
+ template <typename T>
+ void tf() {
+ template for (T x : {}) g(x);
+ template for (constexpr T x : {}) g(x);
+ template for (U x : {}) g(x);
+ template for (constexpr U x : {}) g(x);
+ template for (auto x : {}) g(x);
+ template for (constexpr auto x : {}) g(x);
+ template for (T x : {1, 2}) g(x);
+ template for (U x : {1, 2}) g(x);
+ template for (U x : {T(1), T(2)}) g(x);
+ template for (T x : {U(1), U(2)}) g(x);
+ template for (auto x : {T(1), T(2)}) g(x);
+ template for (auto x : {U(1), T(2)}) g(x);
+ template for (constexpr U x : {T(1), T(2)}) static_assert(tg<x>());
+ template for (constexpr T x : {U(1), U(2)}) static_assert(tg<x>());
+ template for (constexpr auto x : {T(1), U(2)}) static_assert(tg<x>());
+ }
+};
+
+template <typename T>
+void t2() {
+ template for (T x : {}) g(x);
+}
+
+void f2() {
+ t1<int>();
+ t1<long>();
+ s1<long>().tf<long>();
+ s1<int>().tf<int>();
+ s1<int>().tf<long>();
+ s1<long>().tf<int>();
+ t2<S>();
+ t2<S[1231]>();
+ t2<S***>();
+}
+
+template <__SIZE_TYPE__ size>
+struct String {
+ char data[size];
+
+ template <__SIZE_TYPE__ n>
+ constexpr String(const char (&str)[n]) { __builtin_memcpy(data, str, n); }
+
+ constexpr const char* begin() const { return data; }
+ constexpr const char* end() const { return data + size - 1; }
+};
+
+template <__SIZE_TYPE__ n>
+String(const char (&str)[n]) -> String<n>;
+
+// Note: Remove this test once we do support them.
+int iterating_expansion_stmts_unsupported() {
+ static constexpr String s{"abcd"};
+ int count = 0;
+ template for (constexpr auto x : s) count++; // expected-error {{iterating expansion statements are not yet supported}}
+ return count;
+}
+
+template <typename T>
+int iterating_expansion_stmts_unsupported_dependent() {
+ static constexpr String s{"abcd"};
+ int count = 0;
+ template for (auto x : T(s)) count++; // expected-error {{iterating expansion statements are not yet supported}}
+ return count;
+}
+
+void iterating_expansion_stmts_unsupported_dependent_instantiate() {
+ iterating_expansion_stmts_unsupported_dependent<String<5>>(); // expected-note {{in instantiation of}}
+}
+
+#if 0 // Disabled until we support iterating expansion statements.
+constexpr int f3() {
+ static constexpr String s{"abcd"};
+ int count = 0;
+ template for (constexpr auto x : s) count++;
+ return count;
+}
+
+template <String s>
+constexpr int tf3() {
+ int count = 0;
+ template for (constexpr auto x : s) count++;
+ return count;
+}
+
+static_assert(f3() == 4);
+static_assert(tf3<"1">() == 1);
+static_assert(tf3<"12">() == 2);
+static_assert(tf3<"123">() == 3);
+static_assert(tf3<"1234">() == 4);
+
+void f4() {
+ static constexpr String empty{""};
+ static constexpr String s{"abcd"};
+ template for (auto x : empty) static_assert(false, "not expanded");
+ template for (constexpr auto x : s) g(x);
+ template for (auto x : s) g(x);
+}
+
+struct NegativeSize {
+ static constexpr const char* str = "123";
+ constexpr const char* begin() const { return str + 3; }
+ constexpr const char* end() const { return str; }
+};
+
+void negative_size() {
+ static constexpr NegativeSize n;
+ template for (auto x : n) g(x); // expected-error {{expansion statement size is not a constant expression}} \
+ old-interp-note {{constexpr evaluation hit maximum step limit}} \
+ new-interp-note {{cannot refer to element 5 of array of 4 elements in a constant expression}} \
+ expected-note {{in call to}}
+ template for (constexpr auto x : n) g(x); // expected-error {{expansion statement size is not a constant expression}} \
+ old-interp-note {{constexpr evaluation hit maximum step limit}} \
+ new-interp-note {{cannot refer to element 5 of array of 4 elements in a constant expression}} \
+ expected-note {{in call to}}
+}
+
+template <typename T, __SIZE_TYPE__ size>
+struct Array {
+ T data[size]{};
+ constexpr const T* begin() const { return data; }
+ constexpr const T* end() const { return data + size; }
+};
+
+struct NotInt {
+ struct iterator {};
+ constexpr iterator begin() const { return {}; }
+ constexpr iterator end() const { return {}; }
+};
+
+void not_int() {
+ static constexpr NotInt ni;
+ template for (auto x : ni) g(x); // expected-error {{invalid operands to binary expression}} \
+ expected-note {{while attempting to construct 'begin - begin' with iterator type 'iterator'}}
+}
+
+static constexpr Array<int, 3> integers{1, 2, 3};
+
+constexpr int friend_func();
+
+struct Private {
+ friend constexpr int friend_func();
+
+private:
+ constexpr const int* begin() const { return integers.begin(); } // expected-note 3 {{declared private here}}
+ constexpr const int* end() const { return integers.end(); } // expected-note 3 {{declared private here}}
+
+public:
+ static constexpr int member_func() {
+ int sum = 0;
+ static constexpr Private p1;
+ template for (auto x : p1) sum += x;
+ return sum;
+ }
+};
+
+struct Protected {
+ friend constexpr int friend_func();
+
+protected:
+ constexpr const int* begin() const { return integers.begin(); } // expected-note 3 {{declared protected here}}
+ constexpr const int* end() const { return integers.end(); } // expected-note 3 {{declared protected here}}
+
+public:
+ static constexpr int member_func() {
+ int sum = 0;
+ static constexpr Protected p1;
+ template for (auto x : p1) sum += x;
+ return sum;
+ }
+};
+
+void access_control() {
+ static constexpr Private p1;
+ template for (auto x : p1) g(x); // expected-error 3 {{'begin' is a private member of 'Private'}} expected-error 3 {{'end' is a private member of 'Private'}}
+
+ static constexpr Protected p2;
+ template for (auto x : p2) g(x); // expected-error 3 {{'begin' is a protected member of 'Protected'}} expected-error 3 {{'end' is a protected member of 'Protected'}}
+}
+
+constexpr int friend_func() {
+ int sum = 0;
+ static constexpr Private p1;
+ template for (auto x : p1) sum += x;
+
+ static constexpr Protected p2;
+ template for (auto x : p2) sum += x;
+ return sum;
+}
+
+static_assert(friend_func() == 12);
+static_assert(Private::member_func() == 6);
+static_assert(Protected::member_func() == 6);
+
+struct SizeNotICE {
+ struct iterator {
+ friend constexpr iterator operator+(iterator a, __PTRDIFF_TYPE__) { return a; }
+ friend constexpr __PTRDIFF_TYPE__ operator-(iterator, iterator) { return 7; }
+ constexpr iterator operator++() { return *this; }
+ int constexpr operator*() const { return 7; }
+
+ // NOT constexpr!
+ friend int operator!=(iterator, iterator) { return 7; } // expected-note {{declared here}}
+ };
+ constexpr iterator begin() const { return {}; }
+ constexpr iterator end() const { return {}; }
+};
+
+struct PlusMissing {
+ struct iterator {
+ friend constexpr __PTRDIFF_TYPE__ operator-(iterator, iterator) { return 7; }
+ constexpr iterator operator++() { return *this; }
+ int constexpr operator*() const { return 7; }
+ };
+ constexpr iterator begin() const { return {}; }
+ constexpr iterator end() const { return {}; }
+};
+
+struct DerefMissing {
+ struct iterator {
+ friend constexpr __PTRDIFF_TYPE__ operator-(iterator, iterator) { return 7; }
+ friend constexpr iterator operator+(iterator a, __PTRDIFF_TYPE__) { return a; }
+ };
+ constexpr iterator begin() const { return {}; }
+ constexpr iterator end() const { return {}; }
+};
+
+struct MinusMissing {
+ struct iterator {};
+ constexpr iterator begin() const { return {}; }
+ constexpr iterator end() const { return {}; }
+};
+
+void missing_funcs() {
+ static constexpr SizeNotICE s1;
+ static constexpr PlusMissing s2;
+ static constexpr DerefMissing s3;
+ static constexpr MinusMissing s4;
+
+ template for (auto x : s1) g(x); // expected-error {{expansion statement size is not a constant expression}} \
+ expected-note {{non-constexpr function 'operator!=' cannot be used in a constant expression}} \
+ expected-note {{in call to}}
+
+ template for (auto x : s2) g(x); // expected-error {{invalid operands to binary expression}}
+ template for (auto x : s3) g(x); // expected-error {{indirection requires pointer operand ('iterator' invalid)}}
+ template for (auto x : s4) g(x); // expected-error {{invalid operands to binary expression ('iterator' and 'iterator')}} \
+ expected-note {{while attempting to construct 'begin - begin' with iterator type 'iterator'}}
+}
+
+namespace adl {
+struct ADL {
+
+};
+
+constexpr const int* begin(const ADL&) { return integers.begin(); }
+constexpr const int* end(const ADL&) { return integers.end(); }
+}
+
+namespace adl_error {
+struct ADLError1 {
+ constexpr const int* begin() const { return integers.begin(); }
+};
+
+struct ADLError2 {
+ constexpr const int* end() const { return integers.end(); }
+};
+
+constexpr const int* begin(const ADLError2&) { return integers.begin(); }
+constexpr const int* end(const ADLError1&) { return integers.end(); }
+}
+
+namespace adl_both {
+static constexpr Array<int, 5> integers2{1, 2, 3, 4, 5};
+struct ADLBoth {
+ // Test that member begin/end are preferred over ADl begin/end. These return
+ // pointers to a different array.
+ constexpr const int* begin() const { return integers2.begin(); }
+ constexpr const int* end() const { return integers2.end(); }
+};
+
+constexpr const int* begin(const ADLBoth&) { return integers.begin(); }
+constexpr const int* end(const ADLBoth&) { return integers.end(); }
+}
+
+constexpr int adl_begin_end() {
+ static constexpr adl::ADL a;
+ int sum = 0;
+ template for (auto x : a) sum += x;
+ template for (constexpr auto x : a) sum += x;
+ return sum;
+}
+
+static_assert(adl_begin_end() == 12);
+
+void adl_mixed() {
+ static constexpr adl_error::ADLError1 a1;
+ static constexpr adl_error::ADLError2 a2;
+
+ // These are actually destructuring because there is no
+ // valid begin/end pair.
+ template for (auto x : a1) g(x);
+ template for (auto x : a2) g(x);
+}
+
+constexpr int adl_both_test() {
+ static constexpr adl_both::ADLBoth a;
+ int sum = 0;
+ template for (auto x : a) sum += x;
+ return sum;
+}
+
+static_assert(adl_both_test() == 15);
+#endif // 0
+
+struct A {};
+struct B { int x = 1; };
+struct C { int a = 1, b = 2, c = 3; };
+struct D {
+ int a = 1;
+ int* b = nullptr;
+ const char* c = "3";
+};
+
+struct Nested {
+ A a;
+ B b;
+ C c;
+};
+
+struct PrivateDestructurable {
+ friend void destructurable_friend();
+private:
+ int a, b; // expected-note 4 {{declared private here}}
+};
+
+struct ProtectedDestructurable {
+ friend void destructurable_friend();
+protected:
+ int a, b; // expected-note 4 {{declared protected here}}
+};
+
+void destructuring() {
+ static constexpr A a;
+ static constexpr B b;
+ static constexpr C c;
+ static constexpr D d;
+
+ template for (auto x : a) static_assert(false, "not expanded");
+ template for (constexpr auto x : a) static_assert(false, "not expanded");
+
+ template for (auto x : b) g(x);
+ template for (constexpr auto x : b) g(x);
+
+ template for (auto x : c) g(x);
+ template for (constexpr auto x : c) g(x);
+
+ template for (auto x : d) { // expected-note 2 {{in instantiation of expansion statement requested here}}
+ // expected-note@#g {{candidate function not viable: no known conversion from 'int *' to 'int' for 1st argument}}
+ // expected-note@#g {{candidate function not viable: no known conversion from 'const char *' to 'int' for 1st argument}}
+ g(x); // expected-error 2 {{no matching function for call to 'g'}}
+
+ }
+
+ template for (constexpr auto x : d) { // expected-note 2 {{in instantiation of expansion statement requested here}}
+ // expected-note@#g {{candidate function not viable: no known conversion from 'int *const' to 'int' for 1st argument}}
+ // expected-note@#g {{candidate function not viable: no known conversion from 'const char *const' to 'int' for 1st argument}}
+ g(x); // expected-error 2 {{no matching function for call to 'g'}}
+ }
+}
+
+constexpr int array() {
+ static constexpr int x[4]{1, 2, 3, 4};
+ int sum = 0;
+ template for (auto y : x) sum += y;
+ template for (constexpr auto y : x) sum += y;
+ return sum;
+}
+
+static_assert(array() == 20);
+
+template <auto v>
+constexpr int destructure() {
+ int sum = 0;
+ template for (auto x : v) sum += x;
+ template for (constexpr auto x : v) sum += x;
+ return sum;
+}
+
+static_assert(destructure<B{10}>() == 20);
+static_assert(destructure<C{}>() == 12);
+static_assert(destructure<C{3, 4, 5}>() == 24);
+
+constexpr int nested() {
+ static constexpr Nested n;
+ int sum = 0;
+ template for (constexpr auto x : n) {
+ static constexpr auto val = x;
+ template for (auto y : val) {
+ sum += y;
+ }
+ }
+ template for (constexpr auto x : n) {
+ static constexpr auto val = x;
+ template for (constexpr auto y : val) {
+ sum += y;
+ }
+ }
+ return sum;
+}
+
+static_assert(nested() == 14);
+
+void access_control_destructurable() {
+ template for (auto x : PrivateDestructurable()) {} // expected-error 2 {{cannot bind private member 'a' of 'PrivateDestructurable'}} \
+ expected-error 2 {{cannot bind private member 'b' of 'PrivateDestructurable'}}
+
+ template for (auto x : ProtectedDestructurable()) {} // expected-error 2 {{cannot bind protected member 'a' of 'ProtectedDestructurable'}} \
+ expected-error 2 {{cannot bind protected member 'b' of 'ProtectedDestructurable'}}
+
+ struct Derived : ProtectedDestructurable {
+ void f() {
+ template for (auto x : *this) {}
+ }
+ };
+}
+
+void destructurable_friend() {
+ template for (auto x : PrivateDestructurable()) {}
+ template for (auto x : ProtectedDestructurable()) {}
+}
+
+struct Placeholder {
+ A get_value() const { return {}; }
+ __declspec(property(get = get_value)) A a;
+};
+
+void placeholder() {
+ template for (auto x: Placeholder().a) {}
+}
+
+union Union { int a; long b;};
+
+struct MemberPtr {
+ void f() {}
+};
+
+void overload_set(int); // expected-note 2 {{possible target for call}}
+void overload_set(long); // expected-note 2 {{possible target for call}}
+
+void invalid_types() {
+ template for (auto x : void()) {} // expected-error {{cannot expand expression of incomplete type 'void'}}
+ template for (auto x : 1) {} // expected-error {{cannot expand expression of type 'int'}}
+ template for (auto x : 1.f) {} // expected-error {{cannot expand expression of type 'float'}}
+ template for (auto x : 'c') {} // expected-error {{cannot expand expression of type 'char'}}
+ template for (auto x : invalid_types) {} // expected-error {{cannot expand expression of type 'void ()'}}
+ template for (auto x : &invalid_types) {} // expected-error {{cannot expand expression of type 'void (*)()'}}
+ template for (auto x : &MemberPtr::f) {} // expected-error {{cannot expand expression of type 'void (MemberPtr::*)()'}}
+ template for (auto x : overload_set) {} // expected-error{{reference to overloaded function could not be resolved; did you mean to call it?}}
+ template for (auto x : &overload_set) {} // expected-error{{reference to overloaded function could not be resolved; did you mean to call it?}}
+ template for (auto x : nullptr) {} // expected-error {{cannot expand expression of type 'std::nullptr_t'}}
+ template for (auto x : __builtin_strlen) {} // expected-error {{builtin functions must be directly called}}
+ template for (auto x : Union()) {} // expected-error {{cannot expand expression of type 'Union'}}
+ template for (auto x : (char*)nullptr) {} // expected-error {{cannot expand expression of type 'char *'}}
+ template for (auto x : []{}) {} // expected-error {{cannot expand lambda closure type}}
+ template for (auto x : [x=3]{}) {} // expected-error {{cannot expand lambda closure type}}
+ template for (auto x : [](auto){}) {} // expected-error {{cannot expand lambda closure type}}
+ template for (auto x : []<typename>(){}) {} // expected-error {{cannot expand lambda closure type}}
+}
+
+constexpr int string_literals() {
+ int i = 0;
+ template for (auto x : "1234") i += int(x);
+ template for (auto x : L"1234") i += int(x);
+ template for (auto x : u"1234") i += int(x);
+ template for (auto x : U"1234") i += int(x);
+ template for (auto x : R"(1234)") i += int(x);
+ return i;
+}
+
+static_assert(string_literals() == 5 * (int('1') + int('2') + int('3') + int('4')));
+
+struct Bitfields {
+ int x : 5 = 1;
+ int y : 5 = 2;
+ int _ : 5 = 3;
+};
+
+constexpr int bitfields() {
+ int i = 0;
+ template for (auto x : Bitfields()) i += x;
+ return i;
+}
+
+static_assert(bitfields() == 6);
+
+struct EnumMember {
+ enum {
+ x, y, z
+ };
+
+ enum class Foo {
+ a, b, c
+ };
+
+ using enum Foo;
+};
+
+constexpr int enum_member() {
+ int i = 0;
+ template for (auto x : EnumMember()) i += x;
+ return i;
+}
+
+static_assert(enum_member() == 0);
+
+struct Mutable {
+ mutable int x{};
+};
+
+constexpr int mutable_member() {
+ const Mutable m;
+ template for (auto& i : m) i = 42;
+ return m.x;
+}
+
+static_assert(mutable_member() == 42);
+
+constexpr int vector_type() {
+ __attribute__((vector_size(sizeof(int) * 4))) int vec{1, 2, 3, 4};
+ int i = 0;
+ template for (auto x : vec) i += x;
+ return i;
+}
+
+static_assert(vector_type() == 10);
+
+constexpr int complex() {
+ _Complex int c{1, 2};
+ int i = 0;
+ template for (auto x : c) i += x;
+ return i;
+}
+
+static_assert(complex() == 3);
+
+#if 0 // Disabled until we support iterating expansion statements.
+struct BeginOnly {
+ int x{1};
+ constexpr const int* begin() const { return nullptr; }
+};
+
+struct EndOnly {
+ int x{2};
+ constexpr const int* end() const { return nullptr; }
+};
+
+namespace adl1 {
+struct BeginOnly {
+ int x{3};
+};
+constexpr const int* begin(const BeginOnly&) { return nullptr; }
+}
+
+namespace adl2 {
+struct EndOnly {
+ int x{4};
+};
+constexpr const int* end(const EndOnly&) { return nullptr; }
+}
+
+namespace adl3 {
+struct BeginOnlyDeleted {
+ int x{4};
+};
+constexpr const int* begin(const BeginOnlyDeleted&) = delete;
+}
+
+namespace adl4 {
+struct EndOnlyDeleted {
+ int x{4};
+};
+constexpr const int* end(const EndOnlyDeleted&) = delete;
+}
+
+namespace adl5 {
+struct BothDeleted {
+ int x{4};
+};
+constexpr const int* begin(const BothDeleted&) = delete; // expected-note {{candidate function has been explicitly deleted}}
+constexpr const int* end(const BothDeleted&) = delete;
+}
+
+namespace adl6 {
+struct BeginNotViable {
+ int x{4};
+};
+constexpr const int* begin(int) { return nullptr; }
+}
+
+namespace adl7 {
+struct EndNotViable {
+ int x{4};
+};
+constexpr const int* end(int) { return nullptr; }
+}
+
+namespace adl8 {
+struct BothNotViable {
+ int x{4};
+};
+constexpr const int* begin(int) { return nullptr; }
+constexpr const int* end(int) { return nullptr; }
+}
+
+namespace adl9 {
+struct BeginDeleted {
+ int x{4};
+};
+constexpr const int* begin(const BeginDeleted&) = delete; // expected-note {{candidate function has been explicitly deleted}}
+constexpr const int* end(const BeginDeleted&) { return nullptr; }
+}
+
+namespace adl10 {
+struct EndDeleted {
+ int x{4};
+};
+constexpr const int* begin(const EndDeleted&) { return nullptr; }
+constexpr const int* end(const EndDeleted&) = delete; // expected-note {{candidate function has been explicitly deleted}}
+}
+
+void unpaired_begin_end() {
+ static constexpr adl1::BeginOnly begin_only;
+ static constexpr adl2::EndOnly end_only;
+ static constexpr adl3::BeginOnlyDeleted begin_only_deleted;
+ static constexpr adl4::EndOnlyDeleted end_only_deleted;
+ static constexpr adl5::BothDeleted both_deleted;
+ static constexpr adl6::BeginNotViable begin_not_viable;
+ static constexpr adl7::EndNotViable end_not_viable;
+ static constexpr adl8::BothNotViable both_not_viable;
+ static constexpr adl9::BeginDeleted begin_deleted;
+ static constexpr adl10::EndDeleted end_deleted;
+
+ // Ok, these are destructuring because there is no valid pair.
+ template for (auto x : begin_only) {}
+ template for (auto x : begin_only_deleted) {}
+ template for (auto x : begin_not_viable) {}
+ template for (auto x : end_only) {}
+ template for (auto x : end_only_deleted) {}
+ template for (auto x : end_not_viable) {}
+
+ // This is also ok because overload resolution fails.
+ template for (auto x : both_not_viable) {}
+
+ // These are invalid because overload resolution succeeds (even though
+ // there is no usable begin() and/or end()).
+ template for (auto x : both_deleted) {} // expected-error {{call to deleted function 'begin'}} \
+ expected-note {{when looking up 'begin' function for range expression of type 'const adl5::BothDeleted'}}~
+
+ template for (auto x : begin_deleted) {} // expected-error {{call to deleted function 'begin'}} \
+ expected-note {{when looking up 'begin' function for range expression of type 'const adl9::BeginDeleted'}}
+
+ template for (auto x : end_deleted) {} // expected-error {{call to deleted function 'end'}} \
+ expected-note {{when looking up 'end' function for range expression of type 'const adl10::EndDeleted'}}
+}
+#endif // 0
+
+// Examples taken from [stmt.expand].
+namespace stmt_expand_examples {
+consteval int f(auto const&... Containers) {
+ int result = 0;
+ template for (auto const& c : {Containers...}) { // OK, enumerating expansion statement
+ result += c[0];
+ }
+ return result;
+}
+constexpr int c1[] = {1, 2, 3};
+constexpr int c2[] = {4, 3, 2, 1};
+static_assert(f(c1, c2) == 5);
+
+#if 0 // Disabled until we support iterating expansion statements.
+// TODO: This entire example should work without issuing any diagnostics once
+// we have full support for references to constexpr variables (P2686).
+consteval int f() {
+ constexpr Array<int, 3> arr {1, 2, 3}; // expected-note{{add 'static' to give it a constant address}} \
+ new-interp-note 5 {{add 'static' to give it a constant address}}
+
+ int result = 0;
+
+ // expected-error@#invalid-ref {{constexpr variable '__range1' must be initialized by a constant expression}}
+ // expected-error@#invalid-ref {{constexpr variable '__begin1' must be initialized by a constant expression}}
+ // expected-error@#invalid-ref {{constexpr variable '__end1' must be initialized by a constant expression}}
+ // expected-note@#invalid-ref {{reference to 'arr' is not a constant expression}}
+ // old-interp-error@#invalid-ref {{expansion statement size is not a constant expression}}
+ // old-interp-note@#invalid-ref {{in call to}}
+ // old-interp-note@#invalid-ref 3 {{member call on variable '__range1' whose value is not known}}
+ // old-interp-note@#invalid-ref 3 {{declared here}}
+ // new-interp-error@#invalid-ref 3 {{constexpr variable '__iter1' must be initialized by a constant expression}}
+ // new-interp-note@#invalid-ref 5 {{pointer to subobject of 'arr' is not a constant expression}}
+ // new-interp-note@#invalid-ref 3 {{in instantiation of expansion statement}}
+ template for (constexpr int s : arr) { // #invalid-ref // OK, iterating expansion statement
+ result += sizeof(char[s]);
+ }
+ return result;
+}
+static_assert(f() == 6); // old-interp-error {{static assertion failed due to requirement 'f() == 6'}} old-interp-note {{expression evaluates to '0 == 6'}}
+#endif // 0
+
+struct S {
+ int i;
+ short s;
+};
+
+consteval long f(S s) {
+ long result = 0;
+ template for (auto x : s) { // OK, destructuring expansion statement
+ result += sizeof(x);
+ }
+ return result;
+}
+static_assert(f(S{}) == sizeof(int) + sizeof(short));
+}
+
+void not_constant_expression() {
+ template for (constexpr auto x : B()) { // expected-error {{constexpr variable '[__u0]' must be initialized by a constant expression}} \
+ expected-note {{reference to temporary is not a constant expression}} \
+ expected-note {{temporary created here}} \
+ expected-error {{constexpr variable 'x' must be initialized by a constant expression}} \
+ expected-note {{in instantiation of expansion statement requested here}} \
+ old-interp-note {{read of variable '[__u0]' whose value is not known}} \
+ old-interp-note {{declared here}} \
+ new-interp-note {{cannot access field of null pointer}}
+ g(x);
+ }
+}
+
+constexpr int references_enumerating() {
+ int x = 1, y = 2, z = 3;
+ template for (auto& x : {x, y, z}) { ++x; }
+ template for (auto&& x : {x, y, z}) { ++x; }
+ return x + y + z;
+}
+
+static_assert(references_enumerating() == 12);
+
+constexpr int references_destructuring() {
+ C c;
+ template for (auto& x : c) { ++x; }
+ template for (auto&& x : c) { ++x; }
+ return c.a + c.b + c.c;
+}
+
+static_assert(references_destructuring() == 12);
+
+constexpr int break_continue() {
+ int sum = 0;
+ template for (auto x : {1, 2}) {
+ break;
+ sum += x;
+ }
+
+ template for (auto x : {3, 4}) {
+ continue;
+ sum += x;
+ }
+
+ template for (auto x : {5, 6}) {
+ if (x == 6) break;
+ sum += x;
+ }
+
+ template for (auto x : {7, 8, 9}) {
+ if (x == 8) continue;
+ sum += x;
+ }
+
+ return sum;
+}
+
+static_assert(break_continue() == 21);
+
+constexpr int break_continue_nested() {
+ int sum = 0;
+
+ template for (auto x : {1, 2}) {
+ template for (auto y : {3, 4}) {
+ if (x == 2) break;
+ sum += y;
+ }
+ sum += x;
+ }
+
+ template for (auto x : {5, 6}) {
+ template for (auto y : {7, 8}) {
+ if (x == 6) continue;
+ sum += y;
+ }
+ sum += x;
+ }
+
+ return sum;
+}
+
+static_assert(break_continue_nested() == 36);
+
+template <typename ...Ts>
+void unexpanded_pack_bad(Ts ...ts) {
+ template for (auto x : ts) {} // expected-error {{expression contains unexpanded parameter pack 'ts'}}
+ template for (Ts x : {1, 2}) {} // expected-error {{declaration type contains unexpanded parameter pack 'Ts'}}
+ template for (auto x : {ts}) {} // expected-error {{initializer contains unexpanded parameter pack}} \
+ // expected-note {{in instantiation of expansion statement requested here}}
+}
+
+struct E { int x, y; constexpr E(int x, int y) : x{x}, y{y} {}};
+
+template <typename ...Es>
+constexpr int unexpanded_pack_good(Es ...es) {
+ int sum = 0;
+ ([&] {
+ template for (auto x : es) sum += x;
+ template for (Es e : {{5, 6}, {7, 8}}) sum += e.x + e.y;
+ }(), ...);
+ return sum;
+}
+
+static_assert(unexpanded_pack_good(E{1, 2}, E{3, 4}) == 62);
+
+// Ensure that the expansion-initializer is evaluated even if it expands
+// to nothing.
+//
+// This is related to CWG 3048. Note that we currently still model this as
+// a DecompositionDecl w/ zero bindings.
+constexpr bool empty_side_effect() {
+ struct A {
+ constexpr A(bool& b) {
+ b = true;
+ }
+ };
+
+ bool constructed = false;
+ template for (auto x : A(constructed)) static_assert(false);
+ return constructed;
+}
+
+static_assert(empty_side_effect());
+
+namespace apply_lifetime_extension {
+struct T {
+ int& x;
+ constexpr T(int& x) noexcept : x(x) {}
+ constexpr ~T() noexcept { x = 42; }
+};
+
+constexpr const T& f(const T& t) noexcept { return t; }
+constexpr T g(int& x) noexcept { return T(x); }
+
+// CWG 3043:
+//
+// Lifetime extension only applies to destructuring expansion statements
+// (enumerating statements don't have a range variable, and the range variable
+// of iterating statements is constexpr).
+constexpr int lifetime_extension() {
+ int x = 5;
+ int sum = 0;
+ template for (auto e : f(g(x))) {
+ sum += x;
+ }
+ return sum + x;
+}
+
+template <typename T>
+constexpr int lifetime_extension_instantiate_expansions() {
+ int x = 5;
+ int sum = 0;
+ template for (T e : f(g(x))) {
+ sum += x;
+ }
+ return sum + x;
+}
+
+template <typename T>
+constexpr int lifetime_extension_dependent_expansion_stmt() {
+ int x = 5;
+ int sum = 0;
+ template for (int e : f(g((T&)x))) {
+ sum += x;
+ }
+ return sum + x;
+}
+
+template <typename U>
+struct foo {
+ template <typename T>
+ constexpr int lifetime_extension_multiple_instantiations() {
+ int x = 5;
+ int sum = 0;
+ template for (T e : f(g((U&)x))) {
+ sum += x;
+ }
+ return sum + x;
+ }
+};
+
+static_assert(lifetime_extension() == 47);
+static_assert(lifetime_extension_instantiate_expansions<int>() == 47);
+static_assert(lifetime_extension_dependent_expansion_stmt<int>() == 47);
+static_assert(foo<int>().lifetime_extension_multiple_instantiations<int>() == 47);
+}
+
+template <typename... Ts>
+constexpr int return_from_expansion(Ts... ts) {
+ template for (int i : {1, 2, 3}) {
+ return (ts + ...);
+ }
+ __builtin_unreachable();
+}
+
+static_assert(return_from_expansion(4, 5, 6) == 15);
+
+void not_constexpr();
+
+constexpr int empty_expansion_consteval() {
+ template for (auto _ : {}) {
+ not_constexpr();
+ }
+ return 3;
+}
+
+static_assert(empty_expansion_consteval() == 3);
+
+void nested_empty_expansion() {
+ template for (auto x1 : {})
+ template for (auto x2 : {1})
+ static_assert(false);
+
+ template for (auto x1 : {1})
+ template for (auto x2 : {})
+ template for (auto x3 : {1})
+ static_assert(false);
+
+ template for (auto x1 : {})
+ template for (auto x2 : {})
+ template for (auto x3 : {})
+ template for (auto x4 : {1})
+ static_assert(false);
+
+ template for (auto x1 : {})
+ template for (auto x2 : {1})
+ template for (auto x3 : {})
+ template for (auto x4 : {1})
+ static_assert(false);
+
+ template for (auto x1 : {})
+ template for (auto x2 : {1})
+ template for (auto x4 : {1})
+ static_assert(false);
+}
+
+struct Empty {};
+
+template <typename T>
+void nested_empty_expansion_dependent() {
+ template for (auto x1 : T())
+ template for (auto x2 : {1})
+ static_assert(false);
+
+ template for (auto x1 : {1})
+ template for (auto x2 : T())
+ template for (auto x3 : {1})
+ static_assert(false);
+
+ template for (auto x1 : T())
+ template for (auto x2 : T())
+ template for (auto x3 : T())
+ template for (auto x4 : {1})
+ static_assert(false);
+
+ template for (auto x1 : T())
+ template for (auto x2 : {1})
+ template for (auto x3 : T())
+ template for (auto x4 : {1})
+ static_assert(false);
+
+ template for (auto x1 : T())
+ template for (auto x2 : {1})
+ template for (auto x4 : {1})
+ static_assert(false);
+}
+
+void nested_empty_expansion_dependent_instantiate() {
+ nested_empty_expansion_dependent<Empty>();
+}
+
+// Destructuring expansion statements using tuple_size/tuple_element/get.
+namespace std {
+template <typename>
+struct tuple_size;
+
+template <__SIZE_TYPE__, typename>
+struct tuple_element; // expected-note {{template is declared here}}
+
+namespace get_decomposition {
+struct MemberGet {
+ int x[6]{};
+
+ template <__SIZE_TYPE__ I>
+ constexpr int& get() { return x[I * 2]; }
+};
+
+struct ADLGet {
+ long x[8]{};
+};
+
+template <__SIZE_TYPE__ I>
+constexpr long& get(ADLGet& a) { return a.x[I * 2]; }
+} // namespace get_decomposition
+
+template <>
+struct tuple_size<get_decomposition::MemberGet> {
+ static constexpr __SIZE_TYPE__ value = 3;
+};
+
+template <__SIZE_TYPE__ I>
+struct tuple_element<I, get_decomposition::MemberGet> {
+ using type = int;
+};
+
+template <>
+struct tuple_size<get_decomposition::ADLGet> {
+ static constexpr __SIZE_TYPE__ value = 4;
+};
+
+template <__SIZE_TYPE__ I>
+struct tuple_element<I, get_decomposition::ADLGet> {
+ using type = long;
+};
+
+constexpr int member() {
+ get_decomposition::MemberGet m;
+ int v = 1;
+ template for (int& i : m) {
+ i = v;
+ v++;
+ }
+ return m.x[0] + m.x[2] + m.x[4];
+}
+
+constexpr long adl() {
+ get_decomposition::ADLGet m;
+ long v = 1;
+ template for (long& i : m) {
+ i = v;
+ v++;
+ }
+ return m.x[0] + m.x[2] + m.x[4] + m.x[6];
+}
+
+static_assert(member() == 6);
+static_assert(adl() == 10);
+
+struct TupleSizeOnly {};
+
+template <>
+struct tuple_size<TupleSizeOnly> {
+ static constexpr __SIZE_TYPE__ value = 3;
+};
+
+struct TupleSizeAndGet {
+ template <__SIZE_TYPE__>
+ constexpr int get() { return 1; }
+};
+
+template <>
+struct tuple_size<TupleSizeAndGet> {
+ static constexpr __SIZE_TYPE__ value = 3;
+};
+
+void invalid() {
+ template for (auto x : TupleSizeOnly()) {} // expected-error {{use of undeclared identifier 'get'}} \
+ expected-note {{in implicit initialization of binding declaration}}
+
+ template for (auto x : TupleSizeAndGet()) {} // expected-error {{implicit instantiation of undefined template 'std::tuple_element<0, std::TupleSizeAndGet>'}} \
+ expected-note {{in implicit initialization of binding declaration}}
+}
+} // namespace std
+
+constexpr int generic_lambda() {
+ static constexpr int arr[]{1, 2, 3};
+ int sum = 0;
+ [n = 5, &sum]<class = void>() {
+ template for (constexpr auto x : arr) {
+ sum += n + x;
+ }
+ }();
+ return sum;
+}
+
+static_assert(generic_lambda() == 21);
+
+void for_range_decl_must_be_var() {
+ template for (void q() : "error") // expected-error {{expansion statement declaration must declare a variable}}
+ ;
+}
+
+void init_list_bad() {
+ template for (auto y : {{1}, {2}, {3, {4}}, {{{5}}}}); // expected-error {{cannot deduce actual type for variable 'y' with type 'auto' from initializer list}} \
+ expected-note {{in instantiation of expansion statement requested here}}
+}
+
+// Test that the init statement is evaluated even if the expansion statement
+// expands to nothing.
+constexpr int init_stmt_empty_expansion() {
+#if 0 // Disabled until we support iterating expansion statements.
+ static constexpr String empty{""};
+#endif // 0
+ int x = 0;
+ template for (int _ = x += 1; auto i : {}) {}
+#if 0 // Disabled until we support iterating expansion statements.
+ template for (int _ = x += 2; auto i : empty) {}
+#endif // 0
+ template for (int _ = x += 3; auto i : Empty()) {}
+ return x;
+}
+
+static_assert(init_stmt_empty_expansion() == 4);
+
+void vla(int n) {
+ int a[n];
+ template for (int x : a) {} // expected-error {{cannot expand variable length array type 'int[n]'}}
+}
+
+template <typename T>
+void template_vla(T& a) { // expected-note {{variably modified type 'int[n]' cannot be used as a template argument}}
+ template for (int x : a) {}
+}
+
+void instantiate_template_vla(int n) {
+ int a[n];
+ template_vla(a); // expected-error {{no matching function for call to 'template_vla'}}
+}
+
+struct Incomplete; // expected-note 2 {{forward declaration of 'Incomplete'}}
+void incomplete_type(Incomplete& s) {
+ template for (int x : s) {} // expected-error {{cannot expand expression of incomplete type 'Incomplete'}}
+}
+
+template <typename T>
+void dependent_incomplete_type(T& s) {
+ template for (int x : s) {} // expected-error {{cannot expand expression of incomplete type 'Incomplete'}}
+}
+
+template void dependent_incomplete_type<Incomplete>(Incomplete&); // expected-note {{in instantiation of function template specialization 'dependent_incomplete_type<Incomplete>' requested here}}
+
+template <typename T>
+void lambda_template(T a) {
+ template for (auto x : a) {} // expected-error {{cannot expand lambda closure type}}
+}
+
+void lambda_template_call() {
+ lambda_template([]{}); // expected-note {{in instantiation of function template specialization}}
+}
+
+#if 0 // Disabled until we support iterating expansion statements.
+// CWG 3131 makes it possible to expand over non-constexpr ranges.
+namespace cwg3131 {
+constexpr int f1() {
+ int j = 0;
+ template for (auto i : Array<int, 3>{1, 2, 3}) j +=i;
+ return j;
+}
+
+constexpr int f2() {
+ Array<int, 3> a{1, 2, 3};
+ int j = 0;
+ template for (auto i : a) j +=i; // new-interp-error {{expansion statement size is not a constant expression}} \
+ new-interp-note {{initializer of '__range1' is not a constant expression}} \
+ new-interp-note {{declared here}}
+ return j;
+}
+
+static_assert(f1() == 6);
+static_assert(f2() == 6); // new-interp-error {{static assertion failed due to requirement 'f2() == 6'}} \
+ new-interp-note {{expression evaluates to '0 == 6'}}
+
+template <typename T>
+struct Span {
+ T* data;
+ __SIZE_TYPE__ size;
+
+ template <__SIZE_TYPE__ N>
+ constexpr Span(T(&a)[N]) : data{a}, size{N} {}
+
+ constexpr auto begin() const -> T* { return data; }
+ constexpr auto end() const -> T* { return data + size; }
+};
+
+constexpr int arr[3] = { 1, 2, 3 };
+consteval Span<const int> foo() {
+ return Span<const int>(arr);
+}
+
+constexpr int f3() {
+ int r = 0;
+ template for (constexpr auto m : foo())
+ r += m;
+ return r;
+}
+
+static_assert(f3() == 6);
+}
+
+// Test that we actually do 'begin + decltype(begin - begin){i}'.
+namespace cwg3044 {
+struct DifferenceType {
+ int v;
+ explicit constexpr DifferenceType(__PTRDIFF_TYPE__ v) : v(int(v)) {}
+};
+
+struct Range {
+ struct iterator {
+ DifferenceType v;
+
+ constexpr iterator& operator++() {
+ v.v++;
+ return *this;
+ }
+
+ constexpr int operator*() const { return int(v.v); }
+ friend constexpr bool operator!=(iterator a, iterator b) { return a.v.v != b.v.v; }
+ friend constexpr iterator operator+(iterator a, DifferenceType b) {
+ return iterator{DifferenceType{int(a.v.v) + int(b.v)}};
+ }
+ friend constexpr DifferenceType operator-(iterator a, iterator b) {
+ return DifferenceType{int(a.v.v) - int(b.v.v)};
+ }
+ };
+
+ constexpr auto begin() const { return iterator{DifferenceType{1}}; }
+ constexpr auto end() const { return iterator{DifferenceType{5}}; }
+};
+
+constexpr int f() {
+ int val = 0;
+ template for (auto v : Range()) val += v;
+ template for (constexpr auto v : Range()) val += v;
+ return val;
+}
+
+static_assert(f() == 20);
+}
+
+// Test that 'iter' is an lvalue, because we used to not actually create a
+// variable for it.
+namespace cwg3140 {
+struct IterLValue {
+ struct iterator {
+ int v;
+
+ constexpr iterator& operator++() {
+ v++;
+ return *this;
+ }
+
+ constexpr int operator*() const & { return int(v); }
+ constexpr int operator*() const && = delete("'iter' must be an lvalue");
+ friend constexpr bool operator!=(iterator a, iterator b) { return a.v != b.v; }
+ friend constexpr iterator operator+(iterator a, int b) { return iterator{a.v + b}; }
+ friend constexpr int operator-(iterator a, iterator b) { return a.v - b.v; }
+ };
+
+ constexpr auto begin() const { return iterator{1}; }
+ constexpr auto end() const { return iterator{5}; }
+};
+
+constexpr int f() {
+ int val = 0;
+ template for (auto v : IterLValue()) val += v;
+ template for (constexpr auto v : IterLValue()) val += v;
+ return val;
+}
+
+static_assert(f() == 20);
+}
+#endif // 0
+
+namespace cwg3149 {
+struct NotCopyable {
+ int x;
+ constexpr NotCopyable(int x) : x{x} {}
+ constexpr NotCopyable(NotCopyable&& o) : x{o.x} {}
+ NotCopyable(const NotCopyable&) = delete; // expected-note 2 {{explicitly marked deleted here}}
+};
+
+struct NotMovable {
+ int x;
+ constexpr NotMovable(int x) : x{x} {}
+ constexpr NotMovable(const NotMovable& o) : x{o.x} {}
+ NotMovable(NotMovable&&) = delete; // expected-note 2 {{explicitly marked deleted here}}
+};
+
+template <typename T>
+struct Wrapper {
+ T a;
+ T b;
+};
+
+constexpr int f() {
+ Wrapper<NotMovable> nm{{3},{4}};
+ Wrapper<NotCopyable> nc{{5}, {6}};
+ int sum = 0;
+ template for (auto x : Wrapper<NotCopyable>{{1}, {2}}) sum += x.x;
+ template for (auto& x : nc) sum += x.x;
+ template for (auto x : static_cast<Wrapper<NotCopyable>&&>(nc)) sum += x.x;
+ template for (auto&& x : static_cast<Wrapper<NotMovable>&&>(nm)) sum += x.x;
+ return sum;
+}
+
+static_assert(f() == 32);
+
+int err() {
+ Wrapper<NotCopyable> nc{{3},{4}};
+ int sum = 0;
+
+ // expected-error at +2 2 {{call to deleted constructor of 'cwg3149::NotMovable'}}
+ // expected-note at +1 2 {{in instantiation of expansion statement requested here}}
+ template for (auto x : Wrapper<NotMovable>{{1}, {2}}) sum += x.x;
+
+ // expected-error at +2 2 {{call to deleted constructor of 'cwg3149::NotCopyable'}}
+ // expected-note at +1 2 {{in instantiation of expansion statement requested here}}
+ template for (auto x : nc) sum += x.x;
+ return sum;
+}
+}
+
+namespace cwg3045 {
+void f1() {
+ int x;
+ int y;
+
+ template for (auto x : {1}) { // expected-note {{previous definition is here}}
+ int x{}; // expected-error {{redefinition of 'x'}}
+ }
+
+ { int x; }
+
+ template for (auto x : {1}) {
+ { int x{}; }
+ {
+ int x{};
+ { int x{}; }
+ }
+ }
+
+ template for (auto x : {1}) {
+ template for (auto y : {1}) { // expected-note {{previous definition is here}}
+ int x{};
+ int y{}; // expected-error {{redefinition of 'y'}}
+ }
+ }
+}
+
+void f2(int q) {
+ switch (q) {
+ case 1: template for (auto x : {1}) [[fallthrough]]; // expected-error {{fallthrough annotation does not directly precede switch label}}
+ }
+ switch (q) {
+ case 1: template for (auto x : {1}) [[fallthrough]]; // expected-error {{fallthrough annotation does not directly precede switch label}}
+ case 2:;
+ }
+
+ switch (q) {
+ case 1: template for (auto x : {1}) { [[fallthrough]]; } // expected-error {{fallthrough annotation does not directly precede switch label}}
+ }
+
+ switch (q) {
+ case 1: template for (auto x : {1}) {
+ switch (q) {
+ case 1: [[fallthrough]];
+ case 2:;
+ }
+ }
+ }
+}
+}
+
+#if 0 // Disabled until we support iterating expansion statements.
+// Check that we apply lifetime extension to iterating expansions statements.
+//
+// The new constant interpreter erroring on this is likely https://github.com/llvm/llvm-project/issues/187775.
+namespace cwg3140 {
+constexpr const char* arr = "1";
+struct T {
+ int& x;
+ constexpr T(int& x) noexcept : x(x) {}
+ constexpr ~T() noexcept { x = 42; }
+ constexpr const char* begin() const { return arr; }
+ constexpr const char* end() const { return arr + 1; }
+};
+
+constexpr const T& f(const T& t) noexcept { return t; }
+constexpr T g(int& x) noexcept { return T(x); }
+
+constexpr int lifetime_extension_iterating() {
+ int x = 5;
+ int sum = 0;
+ // new-interp-error at +3 {{expansion statement size is not a constant expression}}
+ // new-interp-note at +2 {{initializer of '__range1' is not a constant expression}}
+ // new-interp-note at +1 {{declared here}}
+ template for (auto e : f(g(x))) {
+ sum += x;
+ }
+ return sum + x;
+}
+
+template <typename T>
+constexpr int lifetime_extension_iterating_dependent() {
+ int x = 5;
+ int sum = 0;
+ // new-interp-error at +3 {{expansion statement size is not a constant expression}}
+ // new-interp-note at +2 {{initializer of '__range0' is not a constant expression}}
+ // new-interp-note at +1 {{declared here}}
+ template for (auto e : T(f(g(x)))) {
+ sum += x;
+ }
+ return sum + x;
+}
+
+template <typename T>
+constexpr int lifetime_extension_iterating_instantiation() {
+ int x = 5;
+ int sum = 0;
+ // new-interp-error at +3 {{expansion statement size is not a constant expression}}
+ // new-interp-note at +2 {{initializer of '__range2' is not a constant expression}}
+ // new-interp-note at +1 {{declared here}}
+ template for (auto e : f(g(x))) {
+ sum += x;
+ }
+ return sum + x;
+}
+
+static_assert(lifetime_extension_iterating() == 47); // new-interp-error {{static assertion failed}} new-interp-note {{expression evaluates to}}
+static_assert(lifetime_extension_iterating_dependent<const T&>() == 47); // new-interp-error {{static assertion expression is not an integral constant expression}} new-interp-note {{in instantiation of}}
+static_assert(lifetime_extension_iterating_instantiation<void>() == 47); // new-interp-error {{static assertion expression is not an integral constant expression}} new-interp-note {{in instantiation of}}
+}
+#endif // 0
+
+// Tests that make sure that certain parts of an expansion statement end up in
+// the right DeclContext.
+namespace decl_context {
+struct S { int x{}, y{}; };
+
+void dependent_context() {
+ // The init-statement should never be in a dependent context.
+ template for (int _ = ({ static_assert(false); 4; }); auto x : {}) {} // expected-error {{static assertion failed}}
+ template for (({ static_assert(false); }); auto x : {}) {} // expected-error {{static assertion failed}}
+
+ // Likewise, the expansion-initializer is not dependent.
+ template for (auto x : { ({ static_assert(false); 4; }) }) {} // expected-error {{static assertion failed}}
+ template for (auto x : ({ static_assert(false); S(); })) {} // expected-error {{static assertion failed}}
+
+ // The for-range-declaration *is* dependent because it only appears within the
+ // expansion(s), which means that it is discarded entirely if the expansion
+ // size is 0.
+ template for (decltype(({ static_assert(false); 42; })) _ : {}) {}
+ template for (decltype(({ static_assert(false); 42; })) _ : {1}) {} // expected-error {{static assertion failed}} expected-note {{in instantiation of expansion statement}}
+}
+
+template <typename>
+void not_instantiated() {
+ template for (int _ = ({ static_assert(false); 4; }); auto x : {}) {}
+ template for (({ static_assert(false); }); auto x : {}) {}
+ template for (auto x : { ({ static_assert(false); 4; }) }) {}
+ template for (auto x : ({ static_assert(false); S(); })) {}
+ template for (decltype(({ static_assert(false); 42; })) _ : {}) {}
+ template for (decltype(({ static_assert(false); 42; })) _ : {1}) {}
+}
+
+template <typename>
+void instantiated() {
+ template for (int _ = ({ static_assert(false); 4; }); auto x : {}) {} // expected-error {{static assertion failed}}
+ template for (({ static_assert(false); }); auto x : {}) {} // expected-error {{static assertion failed}}
+ template for (auto x : { ({ static_assert(false); 4; }) }) {} // expected-error {{static assertion failed}}
+ template for (auto x : ({ static_assert(false); S(); })) {} // expected-error {{static assertion failed}}
+ template for (decltype(({ static_assert(false); 42; })) _ : {}) {}
+ template for (decltype(({ static_assert(false); 42; })) _ : {1}) {} // expected-error {{static assertion failed}}
+}
+
+template <typename>
+struct Template {
+ template <typename>
+ void not_fully_instantiated() {
+ template for (int _ = ({ static_assert(false); 4; }); auto x : {}) {}
+ template for (({ static_assert(false); }); auto x : {}) {}
+ template for (auto x : { ({ static_assert(false); 4; }) }) {}
+ template for (auto x : ({ static_assert(false); S(); })) {}
+ template for (decltype(({ static_assert(false); 42; })) _ : {}) {}
+ template for (decltype(({ static_assert(false); 42; })) _ : {1}) {}
+ }
+
+ template <typename>
+ void instantiated() {
+ template for (int _ = ({ static_assert(false); 4; }); auto x : {}) {} // expected-error {{static assertion failed}}
+ template for (({ static_assert(false); }); auto x : {}) {} // expected-error {{static assertion failed}}
+ template for (auto x : { ({ static_assert(false); 4; }) }) {} // expected-error {{static assertion failed}}
+ template for (auto x : ({ static_assert(false); S(); })) {} // expected-error {{static assertion failed}}
+ template for (decltype(({ static_assert(false); 42; })) _ : {}) {}
+ template for (decltype(({ static_assert(false); 42; })) _ : {1}) {} // expected-error {{static assertion failed}}
+ }
+};
+
+void f() {
+ instantiated<void>(); // expected-note {{in instantiation of function template specialization 'decl_context::instantiated<void>'}}
+
+ // This should *not* produce any diagnostics.
+ Template<int>();
+
+ // Rather, the diagnostics are only emitted if we actually fully instantiate
+ // the function template.
+ Template<void>().instantiated<void>(); // expected-note {{in instantiation of function template specialization 'decl_context::Template<void>::instantiated<void>'}}
+}
+}
diff --git a/clang/test/SemaTemplate/GH176155.cpp b/clang/test/SemaTemplate/GH176155.cpp
index 12a9de2ae2d46..ce2ca2d488a03 100644
--- a/clang/test/SemaTemplate/GH176155.cpp
+++ b/clang/test/SemaTemplate/GH176155.cpp
@@ -1,26 +1,16 @@
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
+// expected-no-diagnostics
template <int> struct bad {
template <class T, auto =
- [] { // #lambda
- // expected-note@#lambda {{while substituting into a lambda expression here}}
- // expected-note@#lambda 2{{capture 'i' by value}}
- // expected-note@#lambda 2{{capture 'i' by reference}}
- // expected-note@#lambda 2{{default capture by value}}
- // expected-note@#lambda 2{{default capture by reference}}
- for (int i = 0; i < 100; ++i) { // #i
- // expected-error at -1 {{variable 'i' cannot be implicitly captured in a lambda with no capture-default specified}}
- // expected-note@#i {{'i' declared here}}
- // expected-note@#lambda {{lambda expression begins here}}
- // expected-error at -4 {{variable 'i' cannot be implicitly captured in a lambda with no capture-default specified}}
- // expected-note@#i {{'i' declared here}}
- // expected-note@#lambda {{lambda expression begins here}}
+ [] {
+ for (int i = 0; i < 100; ++i) {
struct LoopHelper {
static constexpr void process() {}
};
}
}>
- static void f(T) {} // expected-note {{in instantiation of default argument for 'f<int>' required here}}
+ static void f(T) {}
};
-int main() { bad<0>::f(0); } // expected-note {{while substituting deduced template arguments into function template 'f'}}
+int main() { bad<0>::f(0); }
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index 2c834b07f9a8f..9ff43c713d5b3 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -320,7 +320,13 @@ <h2 id="cxx26">C++2c implementation status</h2>
<tr>
<td>Expansion Statements</td>
<td><a href="https://wg21.link/P1306">P1306R5</a></td>
- <td class="none" align="center">No</td>
+ <td class="partial" align="center">
+ <details>
+ <summary>Clang 23 (Partial)</summary>
+ Iterating expansion statements currently cannot be expanded and will
+ result in a diagnostic, but other types of expansion statements work.
+ </details>
+ </td>
</tr>
<tr>
<td>constexpr virtual inheritance</td>
>From a52248a5f7f5a122914636990c0ce7ce19f32f54 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Fri, 10 Jul 2026 22:35:33 +0200
Subject: [PATCH 3/5] Fix patch after changes in main
---
clang/lib/Sema/SemaExpand.cpp | 8 +++++---
clang/lib/Sema/SemaStmt.cpp | 1 -
clang/test/SemaCXX/cxx2c-expansion-stmts.cpp | 3 ++-
3 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/clang/lib/Sema/SemaExpand.cpp b/clang/lib/Sema/SemaExpand.cpp
index 8647276149d9a..b95612d08fb10 100644
--- a/clang/lib/Sema/SemaExpand.cpp
+++ b/clang/lib/Sema/SemaExpand.cpp
@@ -266,7 +266,7 @@ static StmtResult BuildDestructuringDecompositionDecl(
TypeSourceInfo *TSI = S.Context.getTrivialTypeSourceInfo(AutoRRef);
auto *DD =
DecompositionDecl::Create(S.Context, S.CurContext, ColonLoc, ColonLoc,
- AutoRRef, TSI, SC_Auto, Bindings);
+ ColonLoc, AutoRRef, TSI, SC_Auto, Bindings);
if (VarIsConstexpr)
DD->setConstexpr(true);
@@ -307,7 +307,8 @@ Sema::BuildCXXExpansionStmtDecl(DeclContext *Ctx, SourceLocation TemplateKWLoc,
ExprResult Sema::ActOnCXXExpansionInitList(MultiExprArg SubExprs,
SourceLocation LBraceLoc,
SourceLocation RBraceLoc) {
- return new (Context) InitListExpr(Context, LBraceLoc, SubExprs, RBraceLoc);
+ return new (Context) InitListExpr(Context, LBraceLoc, SubExprs, RBraceLoc,
+ /*IsExplicit=*/true);
}
StmtResult Sema::ActOnCXXExpansionStmtPattern(
@@ -476,7 +477,8 @@ StmtResult Sema::BuildNonEnumeratingCXXExpansionStmtPattern(
}
ExprResult Select = BuildCXXExpansionSelectExpr(
- new (Context) InitListExpr(Context, ColonLoc, Bindings, ColonLoc),
+ new (Context) InitListExpr(Context, ColonLoc, Bindings, ColonLoc,
+ /*IsExplicit=*/false),
Index);
if (Select.isInvalid()) {
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index c2f0e59f5192e..a1da5e54d4980 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -2979,7 +2979,6 @@ StmtResult Sema::BuildCXXForRangeStmt(
RangeVar->getInit(), RangeVar->getLocation(), RParenLoc);
};
- auto BeginRangeRefTy = RangeVar->getType().getNonReferenceType();
ForRangeBeginEndInfo ForRangeInfo = BuildCXXForRangeBeginEndVars(
S, RangeVar, ColonLoc, CoawaitLoc, LifetimeExtendTemps, Kind,
/*Constexpr=*/false, &RebuildResult, RebuildWithDereference);
diff --git a/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp b/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp
index f98ddd0fa949d..84656396361e3 100644
--- a/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp
+++ b/clang/test/SemaCXX/cxx2c-expansion-stmts.cpp
@@ -798,7 +798,8 @@ void not_constant_expression() {
expected-note {{in instantiation of expansion statement requested here}} \
old-interp-note {{read of variable '[__u0]' whose value is not known}} \
old-interp-note {{declared here}} \
- new-interp-note {{cannot access field of null pointer}}
+ new-interp-note {{temporary created here}} \
+ new-interp-note {{read of temporary is not allowed in a constant expression outside the expression that created the temporary}}
g(x);
}
}
>From 8aebca395d933f0f93f2d8fc06a33ebb26a37dda Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Fri, 10 Jul 2026 22:37:50 +0200
Subject: [PATCH 4/5] clang-format
---
clang/include/clang/AST/DeclTemplate.h | 4 +---
clang/include/clang/AST/ExprCXX.h | 4 +---
clang/include/clang/AST/StmtCXX.h | 7 ++++---
clang/include/clang/Parse/Parser.h | 2 +-
clang/include/clang/Sema/Scope.h | 4 +---
clang/lib/AST/ASTImporter.cpp | 8 ++++----
clang/lib/AST/ByteCode/Compiler.h | 1 +
clang/lib/AST/ExprCXX.cpp | 4 ++--
clang/lib/AST/StmtCXX.cpp | 7 +++----
clang/lib/AST/StmtPrinter.cpp | 3 +--
clang/lib/Parse/ParseDecl.cpp | 2 +-
clang/lib/Parse/ParseStmt.cpp | 12 +++++-------
clang/lib/Sema/SemaExpand.cpp | 4 ++--
clang/lib/Sema/SemaStmt.cpp | 11 +++++------
clang/lib/Sema/TreeTransform.h | 2 +-
clang/lib/Serialization/ASTReaderStmt.cpp | 3 +--
clang/lib/Serialization/ASTWriterStmt.cpp | 5 ++---
17 files changed, 36 insertions(+), 47 deletions(-)
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index 3f893ecd567ca..ed1a517247943 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -3434,9 +3434,7 @@ class CXXExpansionStmtDecl : public Decl, public DeclContext {
GlobalDeclID ID);
CXXExpansionStmtPattern *getExpansionPattern() { return Pattern; }
- const CXXExpansionStmtPattern *getExpansionPattern() const {
- return Pattern;
- }
+ const CXXExpansionStmtPattern *getExpansionPattern() const { return Pattern; }
void setExpansionPattern(CXXExpansionStmtPattern *S) { Pattern = S; }
CXXExpansionStmtInstantiation *getInstantiations() { return Instantiations; }
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index a16483455cfb9..757f2137c90dc 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -5561,9 +5561,7 @@ class CXXExpansionSelectExpr : public Expr {
CXXExpansionSelectExpr(EmptyShell Empty);
CXXExpansionSelectExpr(const ASTContext &C, InitListExpr *Range, Expr *Idx);
- InitListExpr *getRangeExpr() {
- return cast<InitListExpr>(SubExprs[RANGE]);
- }
+ InitListExpr *getRangeExpr() { return cast<InitListExpr>(SubExprs[RANGE]); }
const InitListExpr *getRangeExpr() const {
return cast<InitListExpr>(SubExprs[RANGE]);
diff --git a/clang/include/clang/AST/StmtCXX.h b/clang/include/clang/AST/StmtCXX.h
index cf44268c6e695..a8371cceb784c 100644
--- a/clang/include/clang/AST/StmtCXX.h
+++ b/clang/include/clang/AST/StmtCXX.h
@@ -760,7 +760,7 @@ class CXXExpansionStmtPattern final
static CXXExpansionStmtPattern *
CreateIterating(ASTContext &Context, CXXExpansionStmtDecl *ESD, Stmt *Init,
DeclStmt *ExpansionVar, DeclStmt *Range, DeclStmt *Begin,
- DeclStmt* Iter, SourceLocation LParenLoc,
+ DeclStmt *Iter, SourceLocation LParenLoc,
SourceLocation ColonLoc, SourceLocation RParenLoc);
SourceLocation getLParenLoc() const { return LParenLoc; }
@@ -887,7 +887,6 @@ class CXXExpansionStmtPattern final
return cast<VarDecl>(getIterVarStmt()->getSingleDecl());
}
-
// Accessors for destructuring statements.
Stmt *getDecompositionDeclStmt() {
assert(isDestructuring());
@@ -1063,7 +1062,9 @@ class CXXExpansionStmtInstantiation final
return getTrailingObjects(getNumSubStmts());
}
- unsigned getNumSubStmts() const { return NumInstantiations + NumPreambleStmts; }
+ unsigned getNumSubStmts() const {
+ return NumInstantiations + NumPreambleStmts;
+ }
ArrayRef<Stmt *> getInstantiations() const {
return getTrailingObjects(NumInstantiations);
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 2cdb4d59426b4..892715a6dac26 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -1752,7 +1752,7 @@ class Parser : public CodeCompletionHandler {
SourceLocation ColonLoc;
ExprResult RangeExpr;
SmallVector<MaterializeTemporaryExpr *, 8> LifetimeExtendTemps;
- CXXExpansionStmtDecl* ExpansionStmt = nullptr;
+ CXXExpansionStmtDecl *ExpansionStmt = nullptr;
bool ParsedForRangeDecl() { return !ColonLoc.isInvalid(); }
};
struct ForRangeInfo : ForRangeInit {
diff --git a/clang/include/clang/Sema/Scope.h b/clang/include/clang/Sema/Scope.h
index 06d7a883530cd..fcf3a70ef146d 100644
--- a/clang/include/clang/Sema/Scope.h
+++ b/clang/include/clang/Sema/Scope.h
@@ -313,9 +313,7 @@ class Scope {
IsExpansionStmtScope = Value;
}
- bool isExpansionStmtScope() const {
- return IsExpansionStmtScope;
- }
+ bool isExpansionStmtScope() const { return IsExpansionStmtScope; }
/// getBreakParent - Return the closest scope that a break statement
/// would be affected by.
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 464ab01d79e95..d743717ad49de 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -7556,8 +7556,8 @@ ExpectedStmt ASTNodeImporter::VisitCXXForRangeStmt(CXXForRangeStmt *S) {
ToBody, ToForLoc, ToCoawaitLoc, ToColonLoc, ToRParenLoc);
}
-ExpectedStmt ASTNodeImporter::VisitCXXExpansionStmtPattern(
- CXXExpansionStmtPattern *S) {
+ExpectedStmt
+ASTNodeImporter::VisitCXXExpansionStmtPattern(CXXExpansionStmtPattern *S) {
Error Err = Error::success();
auto ToESD = importChecked(Err, S->getDecl());
auto ToInit = importChecked(Err, S->getInit());
@@ -9526,8 +9526,8 @@ ASTNodeImporter::VisitCXXParenListInitExpr(CXXParenListInitExpr *E) {
ToInitLoc, ToBeginLoc, ToEndLoc);
}
-ExpectedStmt ASTNodeImporter::VisitCXXExpansionSelectExpr(
- CXXExpansionSelectExpr *E) {
+ExpectedStmt
+ASTNodeImporter::VisitCXXExpansionSelectExpr(CXXExpansionSelectExpr *E) {
Error Err = Error::success();
auto ToRange = importChecked(Err, E->getRangeExpr());
auto ToIndex = importChecked(Err, E->getIndexExpr());
diff --git a/clang/lib/AST/ByteCode/Compiler.h b/clang/lib/AST/ByteCode/Compiler.h
index 5c334617c6aa7..8af918f58c06d 100644
--- a/clang/lib/AST/ByteCode/Compiler.h
+++ b/clang/lib/AST/ByteCode/Compiler.h
@@ -252,6 +252,7 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
bool visitCXXTryStmt(const CXXTryStmt *S);
bool
visitCXXExpansionStmtInstantiation(const CXXExpansionStmtInstantiation *S);
+
protected:
bool visitStmt(const Stmt *S);
bool visitExpr(const Expr *E, bool DestroyToplevelScope) override;
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index b7d6c3d18acd5..a1ea91994969c 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -2035,8 +2035,8 @@ CXXFoldExpr::CXXFoldExpr(QualType T, UnresolvedLookupExpr *Callee,
CXXExpansionSelectExpr::CXXExpansionSelectExpr(EmptyShell Empty)
: Expr(CXXExpansionSelectExprClass, Empty) {}
-CXXExpansionSelectExpr::CXXExpansionSelectExpr(
- const ASTContext &C, InitListExpr *Range, Expr *Idx)
+CXXExpansionSelectExpr::CXXExpansionSelectExpr(const ASTContext &C,
+ InitListExpr *Range, Expr *Idx)
: Expr(CXXExpansionSelectExprClass, C.DependentTy, VK_PRValue,
OK_Ordinary) {
setDependence(ExprDependence::TypeValueInstantiation);
diff --git a/clang/lib/AST/StmtCXX.cpp b/clang/lib/AST/StmtCXX.cpp
index 3ac68ca04a5ef..ccd542399761e 100644
--- a/clang/lib/AST/StmtCXX.cpp
+++ b/clang/lib/AST/StmtCXX.cpp
@@ -193,8 +193,8 @@ CXXExpansionStmtPattern *CXXExpansionStmtPattern::CreateEnumerating(
CXXExpansionStmtPattern *CXXExpansionStmtPattern::CreateIterating(
ASTContext &Context, CXXExpansionStmtDecl *ESD, Stmt *Init,
- DeclStmt *ExpansionVar, DeclStmt *Range, DeclStmt *Begin,
- DeclStmt *Iter, SourceLocation LParenLoc, SourceLocation ColonLoc,
+ DeclStmt *ExpansionVar, DeclStmt *Range, DeclStmt *Begin, DeclStmt *Iter,
+ SourceLocation LParenLoc, SourceLocation ColonLoc,
SourceLocation RParenLoc) {
CXXExpansionStmtPattern *Pattern =
AllocateAndConstruct(Context, ExpansionStmtKind::Iterating, ESD, Init,
@@ -209,8 +209,7 @@ SourceLocation CXXExpansionStmtPattern::getBeginLoc() const {
return ParentDecl->getLocation();
}
-DecompositionDecl *
-CXXExpansionStmtPattern::getDecompositionDecl() {
+DecompositionDecl *CXXExpansionStmtPattern::getDecompositionDecl() {
assert(isDestructuring());
return cast<DecompositionDecl>(
cast<DeclStmt>(getDecompositionDeclStmt())->getSingleDecl());
diff --git a/clang/lib/AST/StmtPrinter.cpp b/clang/lib/AST/StmtPrinter.cpp
index d6ed69ede4cd9..9a97d52fd3020 100644
--- a/clang/lib/AST/StmtPrinter.cpp
+++ b/clang/lib/AST/StmtPrinter.cpp
@@ -475,8 +475,7 @@ void StmtPrinter::VisitCXXExpansionStmtInstantiation(
llvm_unreachable("should never be printed");
}
-void StmtPrinter::VisitCXXExpansionSelectExpr(
- CXXExpansionSelectExpr *Node) {
+void StmtPrinter::VisitCXXExpansionSelectExpr(CXXExpansionSelectExpr *Node) {
PrintExpr(Node->getRangeExpr());
}
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index b06022bfd73bb..e3fddf04e555b 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -31,9 +31,9 @@
#include "clang/Sema/SemaCodeCompletion.h"
#include "clang/Sema/SemaObjC.h"
#include "clang/Sema/SemaOpenMP.h"
+#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/StringSwitch.h"
-#include "llvm/ADT/ScopeExit.h"
#include <optional>
using namespace clang;
diff --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 1333512fb2046..df01c91c065a1 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -1968,10 +1968,9 @@ void Parser::ParseForRangeInitializerAfterColon(ForRangeInit &FRI,
std::move(Actions.ExprEvalContexts.back().ForRangeLifetimeExtendTemps);
}
-StmtResult
-Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
- LabelDecl *PrecedingLabel,
- CXXExpansionStmtDecl *ESD) {
+StmtResult Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
+ LabelDecl *PrecedingLabel,
+ CXXExpansionStmtDecl *ESD) {
assert(Tok.is(tok::kw_for) && "Not a for stmt!");
SourceLocation ForLoc = ConsumeToken(); // eat the 'for'.
@@ -2144,7 +2143,7 @@ Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
} else {
// An expression here should not be inside the expansion statement context.
ExpansionStmtContextRAII EnterParentContext{
- Actions, ForRangeInfo, Actions.CurContext->getParent()};
+ Actions, ForRangeInfo, Actions.CurContext->getParent()};
ProhibitAttributes(attrs);
Value = ParseExpression();
@@ -2182,8 +2181,7 @@ Parser::ParseForStatement(SourceLocation *TrailingElseLoc,
// User tried to write the reasonable, but ill-formed, for-range-statement
// for (expr : expr) { ... }
Diag(Tok, diag::err_for_range_expected_decl)
- << (ESD != nullptr)
- << FirstPart.get()->getSourceRange();
+ << (ESD != nullptr) << FirstPart.get()->getSourceRange();
SkipUntil(tok::r_paren, StopBeforeMatch);
SecondPart = Sema::ConditionError();
} else {
diff --git a/clang/lib/Sema/SemaExpand.cpp b/clang/lib/Sema/SemaExpand.cpp
index b95612d08fb10..9d8893dbdb731 100644
--- a/clang/lib/Sema/SemaExpand.cpp
+++ b/clang/lib/Sema/SemaExpand.cpp
@@ -289,8 +289,8 @@ Sema::ActOnCXXExpansionStmtDecl(unsigned TemplateDepth,
auto *TParam = NonTypeTemplateParmDecl::Create(
Context, Context.getTranslationUnitDecl(), TemplateKWLoc, TemplateKWLoc,
- TemplateDepth, /*Position=*/0, /*Id=*/nullptr, ParmTy, /*ParameterPack=*/false,
- ParmTI);
+ TemplateDepth, /*Position=*/0, /*Id=*/nullptr, ParmTy,
+ /*ParameterPack=*/false, ParmTI);
return BuildCXXExpansionStmtDecl(CurContext, TemplateKWLoc, TParam);
}
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index a1da5e54d4980..e6afcd4404501 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -2464,8 +2464,7 @@ StmtResult Sema::BuildCXXForRangeRangeVar(Scope *S, Expr *Range, QualType Type,
IdentifierInfo *Name =
PP.getIdentifierInfo(std::string("__range") + DepthStr);
SourceLocation RangeLoc = Range->getBeginLoc();
- VarDecl *RangeVar = BuildForRangeVarDecl(
- RangeLoc, Type, Name, IsConstexpr);
+ VarDecl *RangeVar = BuildForRangeVarDecl(RangeLoc, Type, Name, IsConstexpr);
if (FinishForRangeVarDecl(*this, RangeVar, Range, RangeLoc,
diag::err_for_range_deduction_failure))
@@ -2769,10 +2768,10 @@ Sema::ForRangeBeginEndInfo Sema::BuildCXXForRangeBeginEndVars(
BeginName = PP.getIdentifierInfo(std::string("__begin") + DepthStr);
if (!EndName)
EndName = PP.getIdentifierInfo(std::string("__end") + DepthStr);
- VarDecl *BeginVar = BuildForRangeVarDecl(
- ColonLoc, AutoType, BeginName, IsConstexpr);
- VarDecl *EndVar = BuildForRangeVarDecl(
- ColonLoc, AutoType, EndName, IsConstexpr);
+ VarDecl *BeginVar =
+ BuildForRangeVarDecl(ColonLoc, AutoType, BeginName, IsConstexpr);
+ VarDecl *EndVar =
+ BuildForRangeVarDecl(ColonLoc, AutoType, EndName, IsConstexpr);
// Build begin-expr and end-expr and attach to __begin and __end variables.
ExprResult BeginExpr, EndExpr;
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index aa6c6000f6b2b..1dd7be50c9ff2 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -9403,7 +9403,7 @@ StmtResult TreeTransform<Derived>::TransformCXXExpansionStmtPattern(
// This is required because some parts of an expansion statement (e.g. the
// init-statement) are not in a dependent context and must thus be transformed
// in the parent context.
- auto TransformStmtInParentContext = [&] (Stmt *SubStmt) -> StmtResult {
+ auto TransformStmtInParentContext = [&](Stmt *SubStmt) -> StmtResult {
Sema::ContextRAII CtxGuard(SemaRef, SemaRef.CurContext->getParent(),
/*NewThis=*/false);
return getDerived().TransformStmt(SubStmt);
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 1cf9c918bc815..87cec16a76323 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -1803,8 +1803,7 @@ void ASTStmtReader::VisitCXXExpansionStmtInstantiation(
S->setShouldApplyLifetimeExtensionToPreamble(Record.readBool());
}
-void ASTStmtReader::VisitCXXExpansionSelectExpr(
- CXXExpansionSelectExpr *E) {
+void ASTStmtReader::VisitCXXExpansionSelectExpr(CXXExpansionSelectExpr *E) {
VisitExpr(E);
E->setRangeExpr(cast<InitListExpr>(Record.readSubExpr()));
E->setIndexExpr(Record.readSubExpr());
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index b04e2db048440..70477f4cf4001 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -1764,7 +1764,7 @@ void ASTStmtWriter::VisitCXXExpansionStmtPattern(CXXExpansionStmtPattern *S) {
Record.AddSourceLocation(S->getColonLoc());
Record.AddSourceLocation(S->getRParenLoc());
Record.AddDeclRef(S->getDecl());
- for (Stmt* SubStmt : S->children())
+ for (Stmt *SubStmt : S->children())
Record.AddStmt(SubStmt);
Code = serialization::STMT_CXX_EXPANSION_PATTERN;
}
@@ -1781,8 +1781,7 @@ void ASTStmtWriter::VisitCXXExpansionStmtInstantiation(
Code = serialization::STMT_CXX_EXPANSION_INSTANTIATION;
}
-void ASTStmtWriter::VisitCXXExpansionSelectExpr(
- CXXExpansionSelectExpr *E) {
+void ASTStmtWriter::VisitCXXExpansionSelectExpr(CXXExpansionSelectExpr *E) {
VisitExpr(E);
Record.AddStmt(E->getRangeExpr());
Record.AddStmt(E->getIndexExpr());
>From 09f6be7736270dd071248b8859166329aa4d4198 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Fri, 10 Jul 2026 22:49:04 +0200
Subject: [PATCH 5/5] Move implementation into .cpp file
---
clang/include/clang/AST/StmtCXX.h | 9 ++-------
clang/lib/AST/StmtCXX.cpp | 8 ++++++++
2 files changed, 10 insertions(+), 7 deletions(-)
diff --git a/clang/include/clang/AST/StmtCXX.h b/clang/include/clang/AST/StmtCXX.h
index a8371cceb784c..60cfd3ee85d4d 100644
--- a/clang/include/clang/AST/StmtCXX.h
+++ b/clang/include/clang/AST/StmtCXX.h
@@ -1082,13 +1082,8 @@ class CXXExpansionStmtInstantiation final
ShouldApplyLifetimeExtensionToPreamble = Apply;
}
- SourceLocation getBeginLoc() const {
- return Parent->getExpansionPattern()->getBeginLoc();
- }
-
- SourceLocation getEndLoc() const {
- return Parent->getExpansionPattern()->getEndLoc();
- }
+ SourceLocation getBeginLoc() const;
+ SourceLocation getEndLoc() const;
CXXExpansionStmtDecl *getParent() { return Parent; }
const CXXExpansionStmtDecl *getParent() const { return Parent; }
diff --git a/clang/lib/AST/StmtCXX.cpp b/clang/lib/AST/StmtCXX.cpp
index ccd542399761e..704d0d4c6b0df 100644
--- a/clang/lib/AST/StmtCXX.cpp
+++ b/clang/lib/AST/StmtCXX.cpp
@@ -280,3 +280,11 @@ CXXExpansionStmtInstantiation::CreateEmpty(ASTContext &C, EmptyShell Empty,
return new (Mem)
CXXExpansionStmtInstantiation(Empty, NumInstantiations, NumPreambleStmts);
}
+
+SourceLocation CXXExpansionStmtInstantiation::getBeginLoc() const {
+ return Parent->getExpansionPattern()->getBeginLoc();
+}
+
+SourceLocation CXXExpansionStmtInstantiation::getEndLoc() const {
+ return Parent->getExpansionPattern()->getEndLoc();
+}
More information about the cfe-commits
mailing list