[llvm-branch-commits] [clang] [Clang] [C++26] Expansion Statements (Part 7: Constexpr support and tests) (PR #169686)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Fri Jul 10 13:01:40 PDT 2026
https://github.com/Sirraide updated https://github.com/llvm/llvm-project/pull/169686
>From b8c1300f987c6d28e9f8e12b3c28d7a2ab5eadc1 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Wed, 26 Nov 2025 17:09:56 +0100
Subject: [PATCH 1/2] [Clang] [C++26] Expansion Statements (Part 7)
---
clang/lib/AST/ByteCode/Compiler.cpp | 42 +
clang/lib/AST/ByteCode/Compiler.h | 3 +-
clang/lib/AST/ExprConstant.cpp | 40 +
clang/lib/Sema/SemaDeclCXX.cpp | 3 +
.../cxx2c-expansion-statements-shadow.cpp | 11 +
clang/test/SemaCXX/cxx2c-expansion-stmts.cpp | 1590 +++++++++++++++++
6 files changed, 1688 insertions(+), 1 deletion(-)
create mode 100644 clang/test/SemaCXX/cxx2c-expansion-statements-shadow.cpp
create mode 100644 clang/test/SemaCXX/cxx2c-expansion-stmts.cpp
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/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/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.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>'}}
+}
+}
>From d0f8ae2153d81be0e0f220c005e4e85974547bd5 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Fri, 10 Jul 2026 22:01:30 +0200
Subject: [PATCH 2/2] [Clang] [C++26] Expansion Statements (Part 8: Codegen)
(#169687)
This implements codegen for expansion statements and adds some codegen
tests.
---
clang/docs/ReleaseNotes.rst | 2 +
.../clang/Basic/DiagnosticCommonKinds.td | 4 -
.../clang/Basic/DiagnosticSemaKinds.td | 6 +
clang/include/clang/Sema/ScopeInfo.h | 9 +-
clang/include/clang/Sema/Sema.h | 6 +-
clang/lib/CodeGen/CGDecl.cpp | 8 +-
clang/lib/CodeGen/CGStmt.cpp | 29 +-
clang/lib/CodeGen/CodeGenFunction.h | 3 +
clang/lib/Parse/ParseStmt.cpp | 12 +-
clang/lib/Sema/SemaLookup.cpp | 47 +-
clang/lib/Sema/SemaStmt.cpp | 30 +-
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 ++++++
.../cxx2c-expansion-stmts-control-flow.cpp | 135 ++
clang/www/cxx_status.html | 8 +-
21 files changed, 3814 insertions(+), 25 deletions(-)
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/SemaCXX/cxx2c-expansion-stmts-control-flow.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/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td
index f1ee130eb1f11..cb267e3ee05c1 100644
--- a/clang/include/clang/Basic/DiagnosticCommonKinds.td
+++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td
@@ -22,10 +22,6 @@ def select_constexpr_spec_kind : TextSubstitution<
def fatal_too_many_errors
: Error<"too many errors emitted, stopping now">, DefaultFatal;
-// TODO: Remove this.
-def err_expansion_statements_todo : Error<
- "TODO (expansion statements)">;
-
def warn_stack_exhausted : Warning<
"stack nearly exhausted; compilation time may suffer, and "
"crashes due to stack overflow are likely">,
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 5a553f92fb0e8..bfe0fe75389fa 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3746,6 +3746,12 @@ 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 "
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 050da05a2e82b..d41cf7cdbcc00 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -9610,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.
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/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 756d194a767cf..32cab6782f9e5 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -738,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;
@@ -783,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();
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 d53e355fbec70..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();
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/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/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>
More information about the llvm-branch-commits
mailing list