[clang] [Clang] Introduce a trait to determine the structure binding size (PR #131515)
Aaron Ballman via cfe-commits
cfe-commits at lists.llvm.org
Tue Mar 18 10:17:23 PDT 2025
================
@@ -0,0 +1,210 @@
+// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -verify
+// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -verify -fexperimental-new-constant-interpreter
+
+
+struct S0 {};
+struct S1 {int a;};
+struct S2 {int a; int b; static int c;};
+struct S3 {double a; int b; int c;};
+struct S4 {int a: 1; int b :2;};
+struct S5 {int : 1; int b :2;};
+struct S6 {union {int a;}; }; // #note-anon-union
+struct S7 {int a[];};
+
+
+
+struct SD : S1 {};
+struct SE1 : S1 { int b;};
+
+class P1 {int a;}; // #note-private
+
+union U1 {};
+union U2 {int a;};
+
+template <typename T>
+concept is_destructurable = requires {
+ { __builtin_structured_binding_size(T) };
+};
+
+static_assert(__builtin_structured_binding_size(S0) == 0);
+static_assert(__is_same_as(decltype(__builtin_structured_binding_size(S0)), decltype(sizeof(void*))));
+
+static_assert(__builtin_structured_binding_size(S1) == 0);
+// expected-error at -1 {{static assertion failed due to requirement '__builtin_structured_binding_size(S1) == 0'}} \
+// expected-note at -1 {{expression evaluates to '1 == 0'}}
+static_assert(__builtin_structured_binding_size(S1) == 1);
+static_assert(__builtin_structured_binding_size(S2) == 2);
+static_assert(__builtin_structured_binding_size(S3) == 3);
+static_assert(__builtin_structured_binding_size(S4) == 2);
+static_assert(__builtin_structured_binding_size(S5) == 2);
+// expected-error at -1 {{static assertion failed due to requirement '__builtin_structured_binding_size(S5) == 2'}} \
+// expected-note at -1 {{expression evaluates to '1 == 2'}}
+static_assert(__builtin_structured_binding_size(S6) == 2);
+// expected-error at -1 {{static assertion failed due to requirement '__builtin_structured_binding_size(S6) == 2'}} \
+// expected-error at -1 {{cannot decompose class type 'S6' because it has an anonymous union member}} \
+// expected-note at -1 {{expression evaluates to '1 == 2'}}
+// expected-note@#note-anon-union {{declared here}}
+static_assert(__builtin_structured_binding_size(S7) == 1);
+
+
+static_assert(__builtin_structured_binding_size(SD) == 1);
+static_assert(__builtin_structured_binding_size(SE1) == 1);
+// expected-error at -1 {{cannot decompose class type 'SE1': both it and its base class 'S1' have non-static data members}} \
+// expected-error at -1 {{type 'SE1' cannot be decomposed}} \
+// expected-error at -1 {{static assertion expression is not an integral constant expression}}
+
+static_assert(__builtin_structured_binding_size(U1) == 0);
+// expected-error at -1 {{type 'U1' cannot be decomposed}} \
+// expected-error at -1 {{static assertion expression is not an integral constant expression}}
+static_assert(__builtin_structured_binding_size(U2) == 0);
+// expected-error at -1 {{type 'U2' cannot be decomposed}} \
+// expected-error at -1 {{static assertion expression is not an integral constant expression}}
+
+
+
+static_assert(__builtin_structured_binding_size(int[0]) == 0);
+static_assert(__builtin_structured_binding_size(int[1]) == 1);
+static_assert(__builtin_structured_binding_size(int[42]) == 42);
+
+using vec2 = int __attribute__((__vector_size__(2 * sizeof(int))));
+using vec3 = int __attribute__((__vector_size__(3 * sizeof(int))));
+static_assert(__builtin_structured_binding_size(vec2) == 2);
+static_assert(__builtin_structured_binding_size(vec3) == 3);
+static_assert(__builtin_structured_binding_size(decltype(__builtin_complex(0., 0.))) == 2);
+
+
+int VLASize; // expected-note {{declared here}}
+static_assert(__builtin_structured_binding_size(int[VLASize]) == 42);
+// expected-error at -1 {{type 'int[VLASize]' cannot be decomposed}} \
+// expected-warning at -1 {{variable length arrays in C++ are a Clang extension}} \
+// expected-note at -1 {{read of non-const variable 'VLASize' is not allowed in a constant expression}} \
+// expected-error at -1 {{static assertion expression is not an integral constant expression}}
+
+
+struct Incomplete; // expected-note {{forward declaration of 'Incomplete'}}
+static_assert(__builtin_structured_binding_size(Incomplete) == 1);
+// expected-error at -1 {{incomplete type 'Incomplete' where a complete type is required}} \
+// expected-error at -1 {{type 'Incomplete' cannot be decomposed}} \
+// expected-error at -1 {{static assertion expression is not an integral constant expression}}
+static_assert(__builtin_structured_binding_size(Incomplete[]) == 1);
+// expected-error at -1 {{type 'Incomplete[]' cannot be decomposed}} \
+// expected-error at -1 {{static assertion expression is not an integral constant expression}}
+static_assert(__builtin_structured_binding_size(Incomplete[0]) == 0);
+static_assert(__builtin_structured_binding_size(Incomplete[1]) == 1);
+static_assert(__builtin_structured_binding_size(Incomplete[42]) == 42);
+
+
+static_assert(__builtin_structured_binding_size(P1) == 0);
+// expected-error at -1 {{static assertion failed due to requirement '__builtin_structured_binding_size(P1) == 0'}} \
+// expected-note at -1 {{expression evaluates to '1 == 0'}} \
+// expected-error at -1 {{cannot decompose private member 'a' of 'P1}} \
+// expected-note@#note-private {{implicitly declared private here}}
+
+
+static_assert(is_destructurable<S0>);
+static_assert(is_destructurable<const S0>);
+static_assert(is_destructurable<volatile S0>);
+static_assert(!is_destructurable<S0&>);
+static_assert(is_destructurable<S1>);
+static_assert(!is_destructurable<S1&>);
+static_assert(!is_destructurable<SE1>);
+static_assert(!is_destructurable<int>);
+static_assert(!is_destructurable<int[]>);
+static_assert(is_destructurable<int[1]>);
+static_assert(!is_destructurable<P1>);
+
+template <typename T>
+constexpr int f() {return 0;};
+template <typename T>
+requires is_destructurable<T>
+constexpr int f() {return 1;};
+
+static_assert(f<int>() == 0);
+static_assert(f<S0>() == 1);
+
+struct T0;
+struct T1;
+struct T42;
+struct TSizeError;
+
+namespace std {
+
+template <typename>
+struct tuple_size;
+
+template <>
+struct tuple_size<T0> {
+ static constexpr int value = 0;
+};
+
+template <>
+struct tuple_size<T1> {
+ static constexpr int value = 1;
+};
+
+template <>
+struct tuple_size<T42> {
+ static constexpr int value = 42;
+};
+
+template <>
+struct tuple_size<TSizeError> {
+ static constexpr void* value = nullptr;
+};
+
+static_assert(__builtin_structured_binding_size(T0) == 0);
+
+static_assert(is_destructurable<const T0>);
+static_assert(is_destructurable<volatile T0>);
+static_assert(!is_destructurable<T0&>);
+
+
+static_assert(__builtin_structured_binding_size(T1) == 1);
+static_assert(__builtin_structured_binding_size(T42) == 42);
+static_assert(__builtin_structured_binding_size(TSizeError) == 42);
+// expected-error at -1 {{cannot decompose this type; 'std::tuple_size<TSizeError>::value' is not a valid integral constant expression}} \
+// expected-error at -1 {{type 'TSizeError' cannot be decomposed}} \
+// expected-error at -1 {{static assertion expression is not an integral constant expression}}
+static_assert(!is_destructurable<TSizeError>);
+}
+
+
+struct S {
+ int x;
+ int y;
+ static_assert(__builtin_structured_binding_size(S) == 2);
+ //expected-error at -1 {{incomplete type 'S' where a complete type is required}} \
+ // expected-error at -1 {{type 'S' cannot be decomposed}} \
+ // expected-error at -1 {{static assertion expression is not an integral constant expression}} \
+ // expected-note at -4 {{definition of 'S' is not complete until the closing '}'}}
+};
+
----------------
AaronBallman wrote:
Some other edge case tests:
```
void func(int array[14], int x = __builtin_structured_binding_size(array)); // Is x 14?
struct S {
static int array[14];
int x = __builtin_structured_binding_size(array); // Is x 14?
};
template <typename Ty, int N = __builtin_structured_binding_size(Ty)>
struct T {
static constexpr int value = N;
};
T<int> t1; // Error
static_assert(T<S3>::value == 3);
```
https://github.com/llvm/llvm-project/pull/131515
More information about the cfe-commits
mailing list