llvm-project/clang/test/SemaCXX/cxx2c-binding-pack.cpp
cor3ntin 80ccf01c33
[Clang] Do not try to transform invalid bindings (#125658)
In the presence of an invalid structured binding decomposition, some
binding packs may be invalid and trying to transform them would produce
a recovery expression that does not contains a pack, leading to
assertions in places where we would expect a pack at that stage.

Fixes #125165
2025-02-19 16:47:18 +01:00

236 lines
4.8 KiB
C++

// RUN: %clang_cc1 -fsyntax-only -std=c++26 %s -verify
template <typename T>
struct type_ { };
template <typename ...T>
auto sum(T... t) { return (t + ...); }
struct my_struct {
int a;
int b;
int c;
int d;
};
struct fake_tuple {
int arr[4] = {1, 2, 3, 6};
template <unsigned i>
int get() {
return arr[i];
}
};
namespace std {
template <typename T>
struct tuple_size;
template <unsigned i, typename T>
struct tuple_element;
template <>
struct tuple_size<fake_tuple> {
static constexpr unsigned value = 4;
};
template <unsigned i>
struct tuple_element<i, fake_tuple> {
using type = int;
};
}
template <typename T>
void decompose_tuple() {
auto tup = T{{1, 2, 3, 6}};
auto&& [x, ...rest, y] = tup;
((void)type_<int>(type_<decltype(rest)>{}), ...);
T arrtup[2] = {T{{1, 2, 3, 6}},
T{{7, 9, 10, 11}}};
int sum = 0;
for (auto [...xs] : arrtup) {
sum += (xs + ...);
}
}
template <typename T>
void decompose_struct() {
T obj{1, 2, 3, 6};
auto [x, ...rest, y] = obj;
static_assert(sizeof...(rest) == 2);
auto [...empty] = type_<int>{};
static_assert(sizeof...(empty) == 0);
}
template <typename T>
void decompose_array() {
int arr[4] = {1, 2, 3, 6};
auto [x, ...rest, y] = arr;
static_assert(sizeof...(rest) == 2);
int size = sizeof...(rest);
T arr2[sizeof...(rest)] = {rest...};
auto [...pack] = arr2;
// Array of size 1.
int arr1[1] = {1};
auto [a, ...b] = arr1;
static_assert(sizeof...(b) == 0);
auto [...c] = arr1;
static_assert(sizeof...(c) == 1);
auto [a1, ...b1, c1] = arr1; // expected-error{{decomposes into 1 element, but 3 names were provided}}
}
// Test case by Younan Zhang.
template <unsigned... P>
struct S {
template <unsigned... Q>
struct N {
void foo() {
int arr[] = {P..., Q...};
auto [x, y, ...rest] = arr;
[&]() {
static_assert(sizeof...(rest) + 2 == sizeof...(P) + sizeof...(Q));
}();
}
};
};
struct bit_fields {
int a : 4 {1};
int b : 4 {2};
int c : 4 {3};
int d : 4 {4};
};
template <typename T>
void decompose_bit_field() {
auto [...x] = T{};
static_assert(sizeof...(x) == 4);
int a = x...[0];
int b = x...[1];
int c = x...[2];
int d = x...[3];
}
template <typename T>
void lambda_capture() {
auto [...x] = T{};
[=] { (void)sum(x...); }();
[&] { (void)sum(x...); }();
[x...] { (void)sum(x...); }();
[&x...] { (void)sum(x...); }();
}
struct S2 {
int a, b, c;
};
auto X = [] <typename = void> () {
auto [...pack] = S2{};
};
int main() {
decompose_array<int>();
decompose_tuple<fake_tuple>();
decompose_struct<my_struct>();
S<1, 2, 3, 4>::N<5, 6>().foo();
decompose_bit_field<bit_fields>();
lambda_capture<int[5]>();
lambda_capture<fake_tuple>();
lambda_capture<my_struct>();
X();
}
// P1061R10 Stuff
namespace {
struct C { int x, y, z; };
template <class T>
void now_i_know_my() {
auto [a, b, c] = C(); // OK, SB0 is a, SB1 is b, and SB2 is c
auto [d, ...e] = C(); // OK, SB0 is d, the pack e (v1) contains two structured bindings: SB1 and SB2
static_assert(sizeof...(e) == 2);
auto [...f, g] = C(); // OK, the pack f (v0) contains two structured bindings: SB0 and SB1, and SB2 is g
static_assert(sizeof...(e) == 2);
auto [h, i, j, ...k] = C(); // OK, the pack k is empty
static_assert(sizeof...(e) == 0);
auto [l, m, n, o, ...p] = C(); // expected-error{{{decomposes into 3 elements, but 5 names were provided}}}
}
} // namespace
namespace {
auto g() -> int(&)[4];
template <unsigned long N>
void h(int (&arr)[N]) {
auto [a, ...b, c] = arr; // a names the first element of the array,
// b is a pack referring to the second and
// third elements, and c names the fourth element
static_assert(sizeof...(b) == 2);
auto& [...e] = arr; // e is a pack referring to the four elements of the array
static_assert(sizeof...(e) == 4);
}
void call_h() {
h(g());
}
} // namespace
namespace {
struct D { };
int g(...) { return 1; }
template <typename T>
constexpr int f() {
D arr[1];
auto [...e] = arr;
return g(e...);
}
constexpr int g(D) { return 2; }
void other_main() {
static_assert(f<int>() == 2);
}
} // namespace
namespace {
struct S {
int a,b,c;
};
clsss S2 { // expected-error{{{unknown type name 'clsss'}}}
public:
int a,b,c;
};
// Should not crash.
auto X = [] <typename = void> () {
auto [...pack,a,b,c] = S{};
auto [x,y,z,...pack2] = S{};
auto [...pack3] = S2{};
static_assert(sizeof...(pack3) == 5);
};
} // namespace
namespace GH125165 {
template <typename = void>
auto f(auto t) {
const auto& [...pack] = t;
// expected-error@-1 {{cannot decompose non-class, non-array type 'char const'}}
(pack, ...);
};
void g() {
f('x'); // expected-note {{in instantiation}}
}
}