// RUN: %clang_cc1 -verify=expected,both -std=c++2a -fsyntax-only -triple x86_64-linux-gnu -fexperimental-new-constant-interpreter %s // RUN: %clang_cc1 -verify=expected,both -std=c++2a -fsyntax-only -triple armv8 -fexperimental-new-constant-interpreter %s // RUN: %clang_cc1 -verify=expected,both -std=c++2a -fsyntax-only -triple aarch64_be-linux-gnu -fexperimental-new-constant-interpreter %s // RUN: %clang_cc1 -verify=expected,both -std=c++2a -fsyntax-only -fexperimental-new-constant-interpreter -triple powerpc64le-unknown-unknown -mabi=ieeelongdouble %s // RUN: %clang_cc1 -verify=expected,both -std=c++2a -fsyntax-only -fexperimental-new-constant-interpreter -triple powerpc64-unknown-unknown -mabi=ieeelongdouble %s #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ # define LITTLE_END 1 #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ # define LITTLE_END 0 #else # error "huh?" #endif typedef decltype(nullptr) nullptr_t; typedef __INTPTR_TYPE__ intptr_t; typedef unsigned __INT16_TYPE__ uint16_t; typedef unsigned __INT32_TYPE__ uint32_t; typedef unsigned __INT64_TYPE__ uint64_t; static_assert(sizeof(int) == 4); static_assert(sizeof(long long) == 8); template constexpr To bit_cast(const From &from) { static_assert(sizeof(To) == sizeof(From)); return __builtin_bit_cast(To, from); } template constexpr bool check_round_trip(const Init &init) { return bit_cast(bit_cast(init)) == init; } template constexpr Init round_trip(const Init &init) { return bit_cast(bit_cast(init)); } namespace std { enum byte : unsigned char {}; } // namespace std template struct bits { T : Pad; T bits : N; constexpr bool operator==(const T& rhs) const { return bits == rhs; } }; template constexpr bool operator==(const struct bits& lhs, const struct bits& rhs) { return lhs.bits == rhs.bits; } template struct bytes { using size_t = unsigned int; unsigned char d[N]; constexpr unsigned char operator[](size_t index) { if (index < N) return d[index]; // expected-note {{read of uninitialized object}} return -1; } }; namespace Sanity { /// This is just one byte, and we extract 2 bits from it. /// /// 3 is 0000'0011. /// For both LE and BE, the buffer will contain exactly that /// byte, unaltered and not reordered in any way. It contains all 8 bits. static_assert(__builtin_bit_cast(bits<2>, (unsigned char)3) == (LITTLE_END ? 3 : 0)); /// Similarly, we have one full byte of data, with the two most-significant /// bits set: /// 192 is 1100'0000 static_assert(__builtin_bit_cast(bits<2>, (unsigned char)192) == (LITTLE_END ? 0 : 3)); /// Here we are instead bitcasting two 1-bits into a destination of 8 bits. /// On LE, we should pick the two least-significant bits. On BE, the opposite. /// NOTE: Can't verify this with gcc. constexpr auto B1 = bits<2>{3}; static_assert(__builtin_bit_cast(unsigned char, B1) == (LITTLE_END ? 3 : 192)); /// This should be 0000'0110. /// On LE, this should result in 6. /// On BE, 1100'0000 = 192. constexpr auto B2 = bits<3>{6}; static_assert(__builtin_bit_cast(unsigned char, B2) == (LITTLE_END ? 6 : 192)); constexpr auto B3 = bits<4>{6}; static_assert(__builtin_bit_cast(unsigned char, B3) == (LITTLE_END ? 6 : 96)); struct B { std::byte b0 : 4; std::byte b1 : 4; }; /// We can properly decompose one byte (8 bit) int two 4-bit bitfields. constexpr struct { unsigned char b0; } T = {0xee}; constexpr B MB = __builtin_bit_cast(B, T); static_assert(MB.b0 == 0xe); static_assert(MB.b1 == 0xe); } namespace BitFields { struct BitFields { unsigned a : 2; unsigned b : 30; }; constexpr unsigned A = __builtin_bit_cast(unsigned, BitFields{3, 16}); static_assert(A == (LITTLE_END ? 67 : 3221225488)); struct S { unsigned a : 2; unsigned b : 28; unsigned c : 2; }; constexpr S s = __builtin_bit_cast(S, 0xFFFFFFFF); static_assert(s.a == 3); static_assert(s.b == 268435455); static_assert(s.c == 3); void bitfield_indeterminate() { struct BF { unsigned char z : 2; }; enum byte : unsigned char {}; constexpr BF bf = {0x3}; static_assert(bit_cast>(bf).bits == bf.z); static_assert(bit_cast(bf)); static_assert(__builtin_bit_cast(byte, bf)); // expected-error {{not an integral constant expression}} \ // expected-note {{indeterminate value can only initialize an object of type 'unsigned char' or 'std::byte'; 'byte' is invalid}} struct M { // expected-note@+1 {{subobject declared here}} unsigned char mem[sizeof(BF)]; }; // expected-error@+2 {{initialized by a constant expression}} // expected-note@+1 {{not initialized}} constexpr M m = bit_cast(bf); constexpr auto f = []() constexpr { // bits<24, unsigned int, LITTLE_END ? 0 : 8> B = {0xc0ffee}; constexpr struct { unsigned short b1; unsigned char b0; } B = {0xc0ff, 0xee}; return bit_cast>(B); }; static_assert(f()[0] + f()[1] + f()[2] == 0xc0 + 0xff + 0xee); { // expected-error@+2 {{initialized by a constant expression}} // expected-note@+1 {{in call to}} constexpr auto _bad = f()[3]; } struct B { unsigned short s0 : 8; unsigned short s1 : 8; std::byte b0 : 4; std::byte b1 : 4; std::byte b2 : 4; }; constexpr auto g = [f]() constexpr { return bit_cast(f()); }; static_assert(g().s0 + g().s1 + g().b0 + g().b1 == 0xc0 + 0xff + 0xe + 0xe); { // expected-error@+2 {{initialized by a constant expression}} // expected-note@+1 {{read of uninitialized object is not allowed in a constant expression}} constexpr auto _bad = g().b2; } } } namespace BoolVectors { typedef bool bool32 __attribute__((ext_vector_type(32))); constexpr auto v = bit_cast(0xa1c0ffee); #if LITTLE_END static_assert(!v[0]); static_assert(v[1]); static_assert(v[2]); static_assert(v[3]); static_assert(!v[4]); static_assert(v[5]); static_assert(v[6]); static_assert(v[7]); static_assert(v[8]); static_assert(v[9]); static_assert(v[10]); static_assert(v[11]); static_assert(v[12]); static_assert(v[13]); static_assert(v[14]); static_assert(v[15]); static_assert(!v[16]); static_assert(!v[17]); static_assert(!v[18]); static_assert(!v[19]); static_assert(!v[20]); static_assert(!v[21]); static_assert(v[22]); static_assert(v[23]); static_assert(v[24]); static_assert(!v[25]); static_assert(!v[26]); static_assert(!v[27]); static_assert(!v[28]); static_assert(v[29]); static_assert(!v[30]); static_assert(v[31]); #else static_assert(v[0]); static_assert(!v[1]); static_assert(v[2]); static_assert(!v[3]); static_assert(!v[4]); static_assert(!v[5]); static_assert(!v[6]); static_assert(v[7]); static_assert(v[8]); static_assert(v[9]); static_assert(!v[10]); static_assert(!v[11]); static_assert(!v[12]); static_assert(!v[13]); static_assert(!v[14]); static_assert(!v[15]); static_assert(v[16]); static_assert(v[17]); static_assert(v[18]); static_assert(v[19]); static_assert(v[20]); static_assert(v[21]); static_assert(v[22]); static_assert(v[23]); static_assert(v[24]); static_assert(v[25]); static_assert(v[26]); static_assert(!v[27]); static_assert(v[28]); static_assert(v[29]); static_assert(v[30]); static_assert(!v[31]); #endif struct pad { unsigned short s; unsigned char c; }; constexpr auto p = bit_cast(v); static_assert(p.s == (LITTLE_END ? 0xffee : 0xa1c0)); static_assert(p.c == (LITTLE_END ? 0xc0 : 0xff)); } namespace TwoShorts { struct B { unsigned short s0 : 8; unsigned short s1 : 8; }; constexpr struct { unsigned short b1;} T = {0xc0ff}; constexpr B MB = __builtin_bit_cast(B, T); #if LITTLE_END static_assert(MB.s0 == 0xff); static_assert(MB.s1 == 0xc0); #else static_assert(MB.s0 == 0xc0); static_assert(MB.s1 == 0xff); #endif } typedef bool bool8 __attribute__((ext_vector_type(8))); typedef bool bool9 __attribute__((ext_vector_type(9))); typedef bool bool16 __attribute__((ext_vector_type(16))); typedef bool bool17 __attribute__((ext_vector_type(17))); typedef bool bool32 __attribute__((ext_vector_type(32))); typedef bool bool128 __attribute__((ext_vector_type(128))); static_assert(bit_cast(bool8{1,0,1,0,1,0,1,0}) == (LITTLE_END ? 0x55 : 0xAA), ""); constexpr bool8 b8 = __builtin_bit_cast(bool8, 0x55); // both-error {{'__builtin_bit_cast' source type 'int' does not match destination type 'bool8' (vector of 8 'bool' values) (4 vs 1 bytes)}} static_assert(check_round_trip(static_cast(0)), ""); static_assert(check_round_trip(static_cast(1)), ""); static_assert(check_round_trip(static_cast(0x55)), ""); static_assert(bit_cast(bool16{1,1,1,1,1,0,0,0, 1,1,1,1,0,1,0,0}) == (LITTLE_END ? 0x2F1F : 0xF8F4), ""); static_assert(check_round_trip(static_cast(0xCAFE)), ""); static_assert(check_round_trip(static_cast(0xCAFEBABE)), ""); #ifdef __SIZEOF_INT128__ static_assert(check_round_trip(static_cast<__int128_t>(0xCAFEBABE0C05FEFEULL)), ""); #endif static_assert(bit_cast, uint16_t>(0xcafe) == (LITTLE_END ? 0x95 : 0x7f)); static_assert(bit_cast, uint16_t>(0xcafe) == (LITTLE_END ? 0x2 : 0xf)); static_assert(bit_cast, uint32_t>(0xa1cafe) == (LITTLE_END ? 0x4 : 0x5)); struct S { // little endian: // MSB .... .... LSB // |y| |x| // // big endian // MSB .... .... LSB // |x| |y| unsigned char x : 4; unsigned char y : 4; constexpr bool operator==(S const &other) const { return x == other.x && y == other.y; } }; constexpr S s{0xa, 0xb}; static_assert(bit_cast>(s) == (LITTLE_END ? 0xba : 0xab)); static_assert(bit_cast>(s) == (LITTLE_END ? 0xba & 0x7f : (0xab & 0xfe) >> 1)); static_assert(round_trip>(s) == s); struct R { unsigned int r : 31; unsigned int : 0; unsigned int : 32; constexpr bool operator==(R const &other) const { return r == other.r; } }; using T = bits<31, signed long long>; constexpr R r{0x4ac0ffee}; constexpr T t = bit_cast(r); static_assert(t == ((0xFFFFFFFF8 << 28) | 0x4ac0ffee)); // sign extension static_assert(round_trip(r) == r); static_assert(round_trip(t) == t); /// The oversized bitfield is an error on Windows and not just a warning. #if !defined(_WIN32) struct U { // expected-warning@+1 {{exceeds the width of its type}} uint32_t trunc : 33; uint32_t u : 31; constexpr bool operator==(U const &other) const { return trunc == other.trunc && u == other.u; } }; struct V { uint64_t notrunc : 32; uint64_t : 1; uint64_t v : 31; constexpr bool operator==(V const &other) const { return notrunc == other.notrunc && v == other.v; } }; constexpr U u{static_cast(~0), 0x4ac0ffee}; constexpr V v = bit_cast(u); static_assert(v.v == 0x4ac0ffee); static_assert(round_trip(u) == u); static_assert(round_trip(v) == v); constexpr auto w = bit_cast>(u); static_assert(w == (LITTLE_END ? 0x4ac0ffee & 0xFFF : (0x4ac0ffee & (0xFFF << (31 - 12))) >> (31-12) )); #endif namespace NestedStructures { struct J { struct { uint16_t k : 12; } K; struct { uint16_t l : 4; } L; }; static_assert(sizeof(J) == 4); constexpr J j = bit_cast(0x8c0ffee5); static_assert(j.K.k == (LITTLE_END ? 0xee5 : 0x8c0)); static_assert(j.L.l == 0xf /* yay symmetry */); static_assert(bit_cast>(j) == 0xf); struct N { bits<12, uint16_t> k; uint16_t : 16; }; static_assert(bit_cast(j).k == j.K.k); struct M { bits<4, uint16_t, 0> m[2]; constexpr bool operator==(const M& rhs) const { return m[0] == rhs.m[0] && m[1] == rhs.m[1]; }; }; #if LITTLE_END == 1 constexpr uint16_t want[2] = {0x5, 0xf}; #else constexpr uint16_t want[2] = {0x8000, 0xf000}; #endif static_assert(bit_cast(j) == bit_cast(want)); } namespace Enums { // ensure we're packed into the top 2 bits constexpr int pad = LITTLE_END ? 6 : 0; struct X { char : pad; enum class direction: char { left, right, up, down } direction : 2; }; constexpr X x = { X::direction::down }; static_assert(bit_cast>(x) == -1); static_assert(bit_cast>(x) == 3); static_assert( bit_cast((unsigned char)0x40).direction == X::direction::right); } namespace IndeterminateBits { struct S { unsigned a : 13; unsigned : 17; unsigned b : 2; }; constexpr unsigned A = __builtin_bit_cast(unsigned, S{12, 3}); // expected-error {{must be initialized by a constant expression}} \ // expected-note {{indeterminate value can only initialize an object of type 'unsigned char' or 'std::byte'; 'unsigned int' is invalid}} /// GCC refuses to compile this as soon as we access the indeterminate bits /// in the static_assert. MSVC accepts it. struct S2 { unsigned char a : 2; }; constexpr unsigned char B = __builtin_bit_cast(unsigned char, S2{3}); static_assert(B == (LITTLE_END ? 3 : 192)); struct S3 { unsigned a : 13; unsigned : 17; unsigned b : 2; }; struct D { unsigned a; }; constexpr D s = __builtin_bit_cast(D, S3{12, 3}); // expected-error {{must be initialized by a constant expression}} \ // expected-note {{indeterminate value can only initialize an object of type 'unsigned char' or 'std::byte'; 'unsigned int' is invalid}} }