================
@@ -0,0 +1,1176 @@
+// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -fdeclspec -fblocks
-fexpansion-limit=32 -verify
+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>;
+
+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; }
+};
+
+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; }
+};
+
+void expansion_size() {
+ static constexpr Array<int, 32> almost_too_big;
+ template for (auto x : almost_too_big) g(x);
+ template for (constexpr auto x : almost_too_big) g(x);
+
+ static constexpr Array<int, 33> too_big;
+ template for (auto x : too_big) g(x); // expected-error {{expansion size 33
exceeds maximum configured size 32}} expected-note {{use -fexpansion-limit=N to
adjust this limit}}
+ template for (constexpr auto x : too_big) g(x); // expected-error
{{expansion size 33 exceeds maximum configured size 32}} expected-note {{use
-fexpansion-limit=N to adjust this limit}}
+
+ static constexpr String
big{"1234567890123456789012345678901234567890234567890"};
+ template for (auto x : big) g(x); // expected-error {{expansion size 49
exceeds maximum configured size 32}} expected-note {{use -fexpansion-limit=N to
adjust this limit}}
+ template for (constexpr auto x : big) g(x); // expected-error {{expansion
size 49 exceeds maximum configured size 32}} expected-note {{use
-fexpansion-limit=N to adjust this limit}}
+
+ static constexpr NegativeSize n;
+ template for (auto x : n) g(x); // expected-error {{expansion size must not
be negative (was -3)}}
+ template for (constexpr auto x : n) g(x); // expected-error {{expansion size
must not be negative (was -3)}}
+
+ template for (auto x : {1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
+ 31, 32}) g(x);
+ template for (constexpr auto x : {1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
+ 31, 32}) g(x);
+
+ template for (auto x : {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // expected-error
{{expansion size 33 exceeds maximum configured size 32}} expected-note {{use
-fexpansion-limit=N to adjust this limit}}
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
+ 31, 32, 33}) g(x);
+ template for (constexpr auto x : {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, //
expected-error {{expansion size 33 exceeds maximum configured size 32}}
expected-note {{use -fexpansion-limit=N to adjust this limit}}
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
+ 31, 32, 33}) g(x);
+
+ int huge[1'000'000'000];
+ template for (auto x : huge) {} // expected-error {{expansion size
1000000000 exceeds maximum configured size 32}} expected-note {{use
-fexpansion-limit=N to adjust this limit}}
+}
+
+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}}
+}
+
+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 2 {{declared private here}}
+ constexpr const int* end() const { return integers.end(); } // expected-note
2 {{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 2 {{declared protected here}}
+ constexpr const int* end() const { return integers.end(); } // expected-note
2 {{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 2 {{'begin' is a private
member of 'Private'}} expected-error 2 {{'end' is a private member of
'Private'}}
+
+ static constexpr Protected p2;
+ template for (auto x : p2) g(x); // expected-error 2 {{'begin' is a
protected member of 'Protected'}} expected-error 2 {{'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; }
+ int constexpr operator*() const { return 7; }
+
+ // NOT constexpr!
+ friend int operator-(iterator, iterator) { return 7; } // expected-note
{{declared here}}
+ friend int operator!=(iterator, iterator) { return 7; }
+ };
+ constexpr iterator begin() const { return {}; }
+ constexpr iterator end() const { return {}; }
+};
+
+struct PlusMissing {
+ struct iterator {
+ int constexpr operator*() const { return 7; }
+ };
+ constexpr iterator begin() const { return {}; }
+ constexpr iterator end() const { return {}; }
+};
+
+struct DerefMissing {
+ struct iterator {
+ friend constexpr iterator operator+(iterator a, __PTRDIFF_TYPE__) { return
a; }
+ };
+ 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;
+
+ // TODO: This message should start complaining about '!=' once we support the
+ // proper way of computing the size.
+ template for (auto x : s1) g(x); // expected-error {{expansion size is not a
constant expression}} \
+ expected-note {{non-constexpr function
'operator-' cannot be used in a constant expression}}
+
+ 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)}}
+}
+
+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);
+
+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);
+
+void array_too_big() {
+ int ok[32];
+ int too_big[33];
+
+ template for (auto x : ok) {}
+ template for (auto x : too_big) {} // expected-error {{expansion size 33
exceeds maximum configured size 32}} \
+ expected-note {{use
-fexpansion-limit=N to adjust this limit}}
+}
+
+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'}}
+}
+
+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 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}}
+}
+
+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'}}
+}
+
+// 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);
+
+// 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}}
+
+ 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-error@#invalid-ref {{expansion size is not a constant
expression}}
+ // expected-note@#invalid-ref 2 {{member call on variable '__range1' whose
value is not known}}
+ // expected-note@#invalid-ref 1 {{initializer of '__end1' is not a constant
expression}}
+ // expected-note@#invalid-ref 3 {{declared here}}
+ // expected-note@#invalid-ref {{reference to 'arr' is not a constant
expression}}
+ template for (constexpr int s : arr) { // #invalid-ref // OK,
iterating expansion statement
+ result += sizeof(char[s]);
+ }
+ return result;
+}
+static_assert(f() == 6); // expected-error {{static assertion failed due to
requirement 'f() == 6'}} expected-note {{expression evaluates to '0 == 6'}}
+
+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}} \
+ expected-note {{read of variable
'[__u0]' whose value is not known}} \
+ expected-note {{declared here}}
+ 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);
+
+
+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}}
+ }
+ }
+}
+
+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
----------------
frederick-vs-ja wrote:
Should we test "using capture in a expansion statement in a generic lambda"
like the following?
```C++
constexpr void test() {
static constexpr int arr[]{1, 2, 3};
[n = 42]<class = void>() {
template for (constexpr auto _ : arr) {
(void)n;
}
}();
}
```
I noticed that P2996 branch get this wrong (https://godbolt.org/z/5jaEnvsbc)
(2025-11-19), so I guess it might be better to add some "regression" test first.
https://github.com/llvm/llvm-project/pull/165195
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits