Timm =?utf-8?q?Bäder?= <tbae...@redhat.com>, Timm =?utf-8?q?Bäder?= <tbae...@redhat.com> Message-ID: In-Reply-To: <llvm.org/llvm/llvm-project/pull/112...@github.com>
================ @@ -0,0 +1,399 @@ +// RUN: %clang_cc1 -verify=ref,both -std=c++2a -fsyntax-only %s +// RUN: %clang_cc1 -verify=ref,both -std=c++2a -fsyntax-only -triple aarch64_be-linux-gnu %s +// RUN: %clang_cc1 -verify=ref,both -std=c++2a -fsyntax-only -triple powerpc64le-unknown-unknown -mabi=ieeelongdouble %s +// RUN: %clang_cc1 -verify=ref,both -std=c++2a -fsyntax-only -triple powerpc64-unknown-unknown -mabi=ieeelongdouble %s + +// RUN: %clang_cc1 -verify=expected,both -std=c++2a -fsyntax-only -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; + +static_assert(sizeof(int) == 4); +static_assert(sizeof(long long) == 8); + +template <class To, class From> +constexpr To bit_cast(const From &from) { + static_assert(sizeof(To) == sizeof(From)); + return __builtin_bit_cast(To, from); +} + +template <class Intermediate, class Init> +constexpr bool check_round_trip(const Init &init) { + return bit_cast<Init>(bit_cast<Intermediate>(init)) == init; +} + +template <class Intermediate, class Init> +constexpr Init round_trip(const Init &init) { + return bit_cast<Init>(bit_cast<Intermediate>(init)); +} + +namespace std { +enum byte : unsigned char {}; +} // namespace std + +using uint8_t = unsigned char; + +template<int N> +struct bytes { + using size_t = unsigned int; + unsigned char d[N]; + + constexpr unsigned char &operator[](size_t index) { + if (index < N) + return d[index]; + } +}; + + +template <int N, typename T = unsigned char, int Pad = 0> +struct bits { + T : Pad; + T bits : N; + + constexpr bool operator==(const T& rhs) const { + return bits == rhs; + } +}; + +template <int N, typename T, int P> +constexpr bool operator==(const struct bits<N, T, P>& lhs, const struct bits<N, T, P>& rhs) { + return lhs.bits == rhs.bits; +} + + +namespace simple { + constexpr int A = __builtin_bit_cast(int, 10); + static_assert(A == 10); + + static_assert(__builtin_bit_cast(unsigned, 1.0F) == 1065353216); + + struct Bytes { + char a, b, c, d; + }; + constexpr unsigned B = __builtin_bit_cast(unsigned, Bytes{10, 12, 13, 14}); + static_assert(B == (LITTLE_END ? 235736074 : 168561934)); + + + constexpr unsigned C = __builtin_bit_cast(unsigned, (_BitInt(32))12); + static_assert(C == 12); + + struct BitInts { + _BitInt(16) a; + _BitInt(16) b; + }; + constexpr unsigned D = __builtin_bit_cast(unsigned, BitInts{12, 13}); + static_assert(D == (LITTLE_END ? 851980 : 786445)); + + + + static_assert(__builtin_bit_cast(char, true) == 1); + + static_assert(check_round_trip<unsigned>((int)-1)); + static_assert(check_round_trip<unsigned>((int)0x12345678)); + static_assert(check_round_trip<unsigned>((int)0x87654321)); + static_assert(check_round_trip<unsigned>((int)0x0C05FEFE)); + // static_assert(round_trip<float>((int)0x0C05FEFE)); + + + /// This works in GCC and in the bytecode interpreter, but the current interpreter + /// diagnoses it. + static_assert(__builtin_bit_cast(intptr_t, nullptr) == 0); // ref-error {{not an integral constant expression}} \ + // ref-note {{indeterminate value can only initialize an object}} +} + +namespace Fail { + constexpr int a = 1/0; // both-error {{must be initialized by a constant expression}} \ + // both-note {{division by zero}} \ + // both-note {{declared here}} + constexpr int b = __builtin_bit_cast(int, a); // both-error {{must be initialized by a constant expression}} \ + // both-note {{initializer of 'a' is not a constant expression}} +} + +namespace NullPtr { + constexpr nullptr_t N = __builtin_bit_cast(nullptr_t, (intptr_t)1u); + static_assert(N == nullptr); + static_assert(__builtin_bit_cast(nullptr_t, (_BitInt(sizeof(void*) * 8))12) == __builtin_bit_cast(nullptr_t, (unsigned _BitInt(sizeof(void*) * 8))0)); + static_assert(__builtin_bit_cast(nullptr_t, nullptr) == nullptr); +} + +namespace bitint { + constexpr _BitInt(sizeof(int) * 8) BI = ~0; + constexpr unsigned int I = __builtin_bit_cast(unsigned int, BI); + static_assert(I == ~0u, ""); + + constexpr _BitInt(sizeof(int) * 8) IB = __builtin_bit_cast(_BitInt(sizeof(int) * 8), I); // ref-error {{must be initialized by a constant expression}} \ + // ref-note {{constexpr bit cast involving type '_BitInt(32)' is not yet supported}} \ + // ref-note {{declared here}} + static_assert(IB == ~0u, ""); // ref-error {{not an integral constant expression}} \ + // ref-note {{initializer of 'IB' is not a constant expression}} +} + +namespace BitFields { + struct BitFields { + unsigned a : 2; + unsigned b : 30; + }; + + constexpr unsigned A = __builtin_bit_cast(unsigned, BitFields{3, 16}); // ref-error {{must be initialized by a constant expression}} \ + // ref-note {{not yet supported}} \ + // ref-note {{declared here}} + static_assert(A == (LITTLE_END ? 67 : 3221225488)); // ref-error {{not an integral constant expression}} \ + // ref-note {{initializer of 'A'}} + + + void bitfield_indeterminate() { + struct BF { unsigned char z : 2; }; + enum byte : unsigned char {}; + + constexpr BF bf = {0x3}; + /// Requires bitcasts to composite types. + // static_assert(bit_cast<bits<2>>(bf).bits == bf.z); + // static_assert(bit_cast<unsigned char>(bf)); + +#if 0 + // static_assert(__builtin_bit_cast(byte, bf)); + + 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<M>(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<bytes<4>>(B); + }; + + static_assert(f()[0] + f()[1] + f()[2] == 0xc0 + 0xff + 0xee); + { + // 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 = 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<B>(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; + } +#endif + } +} + +struct int_splicer { + unsigned x; + unsigned y; + + constexpr int_splicer() : x(1), y(2) {} + constexpr int_splicer(unsigned x, unsigned y) : x(x), y(y) {} + + constexpr bool operator==(const int_splicer &other) const { + return other.x == x && other.y == y; + } +}; + +constexpr int_splicer splice(0x0C05FEFE, 0xCAFEBABE); + +#if 0 +static_assert(bit_cast<unsigned long long>(splice) == (LITTLE_END + ? 0xCAFEBABE0C05FEFE + : 0x0C05FEFECAFEBABE)); + +constexpr int_splicer IS = bit_cast<int_splicer>(0xCAFEBABE0C05FEFE); +static_assert(bit_cast<int_splicer>(0xCAFEBABE0C05FEFE).x == (LITTLE_END + ? 0x0C05FEFE + : 0xCAFEBABE)); + +static_assert(round_trip<unsigned long long>(splice)); +static_assert(round_trip<long long>(splice)); +#endif + + + +/// --------------------------------------------------------------------------- +/// From here on, it's things copied from test/SemaCXX/constexpr-builtin-bit.cast.cpp + +void test_int() { + static_assert(round_trip<unsigned>((int)-1)); + static_assert(round_trip<unsigned>((int)0x12345678)); + static_assert(round_trip<unsigned>((int)0x87654321)); + static_assert(round_trip<unsigned>((int)0x0C05FEFE)); +} + +void test_array() { + constexpr unsigned char input[] = {0xCA, 0xFE, 0xBA, 0xBE}; + constexpr unsigned expected = LITTLE_END ? 0xBEBAFECA : 0xCAFEBABE; + static_assert(bit_cast<unsigned>(input) == expected); + + /// Same things but with a composite array. + struct US { unsigned char I; }; + constexpr US input2[] = {{0xCA}, {0xFE}, {0xBA}, {0xBE}}; + static_assert(bit_cast<unsigned>(input2) == expected); +} + +void test_record() { + struct int_splicer { + unsigned x; + unsigned y; + + constexpr bool operator==(const int_splicer &other) const { + return other.x == x && other.y == y; + } + }; + + constexpr int_splicer splice{0x0C05FEFE, 0xCAFEBABE}; + + static_assert(bit_cast<unsigned long long>(splice) == (LITTLE_END + ? 0xCAFEBABE0C05FEFE + : 0x0C05FEFECAFEBABE)); + + /// FIXME: Bit casts to composite types. + // static_assert(bit_cast<int_splicer>(0xCAFEBABE0C05FEFE).x == (LITTLE_END + // ? 0x0C05FEFE + // : 0xCAFEBABE)); + + // static_assert(check_round_trip<unsigned long long>(splice)); + // static_assert(check_round_trip<long long>(splice)); + + struct base2 { + }; + + struct base3 { + unsigned z; + }; + + struct bases : int_splicer, base2, base3 { + unsigned doublez; + }; + + struct tuple4 { + unsigned x, y, z, doublez; + + bool operator==(tuple4 const &other) const = default; + constexpr bool operator==(bases const &other) const { + return x == other.x && y == other.y && + z == other.z && doublez == other.doublez; + } + }; + // constexpr bases b = {{1, 2}, {}, {3}, 4}; + // constexpr tuple4 t4 = bit_cast<tuple4>(b); + // static_assert(t4 == tuple4{1, 2, 3, 4}); + // static_assert(round_trip<tuple4>(b)); + + // constexpr auto b2 = bit_cast<bases>(t4); + // static_assert(t4 == b2); +} + +void test_partially_initialized() { + struct pad { + signed char x; + int y; + }; + + struct no_pad { + signed char x; + signed char p1, p2, p3; + int y; + }; + + static_assert(sizeof(pad) == sizeof(no_pad)); + +#if 0 + constexpr pad pir{4, 4}; + constexpr int piw = bit_cast<no_pad>(pir).x; // both-error {{constexpr variable 'piw' must be initialized by a constant expression}} \ + // both-note {{in call to 'bit_cast<no_pad, pad>(pir)'}} + + + constexpr no_pad bad = bit_cast<no_pad>(pir); // both-error {{constexpr variable 'bad' must be initialized by a constant expression}} \ + // both-note {{in call to 'bit_cast<no_pad, pad>(pir)'}} + // constexpr pad fine = bit_cast<pad>(no_pad{1, 2, 3, 4, 5}); + // static_assert(fine.x == 1 && fine.y == 5); +#endif +} + + +void bad_types() { +#if 0 + union X { + int x; + }; + + struct G { + int g; + }; + // expected-error@+2 {{constexpr variable 'g' must be initialized by a constant expression}} + // expected-note@+1 {{bit_cast from a union type is not allowed in a constant expression}} + constexpr G g = __builtin_bit_cast(G, X{0}); ---------------- tbaederr wrote: It's bitcasting _to_ a non-primitive type, that's not part of this PR. https://github.com/llvm/llvm-project/pull/112126 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits