File: bit_cast.pass.cpp

package info (click to toggle)
llvm-toolchain-15 1%3A15.0.6-4
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 1,554,644 kB
  • sloc: cpp: 5,922,452; ansic: 1,012,136; asm: 674,362; python: 191,568; objc: 73,855; f90: 42,327; lisp: 31,913; pascal: 11,973; javascript: 10,144; sh: 9,421; perl: 7,447; ml: 5,527; awk: 3,523; makefile: 2,520; xml: 885; cs: 573; fortran: 567
file content (260 lines) | stat: -rw-r--r-- 9,428 bytes parent folder | download | duplicates (3)
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17

// <bit>
//
// template<class To, class From>
//   constexpr To bit_cast(const From& from) noexcept; // C++20

#include <array>
#include <bit>
#include <cassert>
#include <cmath>
#include <cstdint>
#include <cstring>
#include <limits>

#include "test_macros.h"

// std::bit_cast does not preserve padding bits, so if T has padding bits,
// the results might not memcmp cleanly.
template<bool HasUniqueObjectRepresentations = true, typename T>
void test_roundtrip_through_buffer(T from) {
    struct Buffer { char buffer[sizeof(T)]; };
    Buffer middle = std::bit_cast<Buffer>(from);
    T to = std::bit_cast<T>(middle);
    Buffer middle2 = std::bit_cast<Buffer>(to);

    assert((from == to) == (from == from)); // because NaN

    if constexpr (HasUniqueObjectRepresentations) {
        assert(std::memcmp(&from, &middle, sizeof(T)) == 0);
        assert(std::memcmp(&to, &middle, sizeof(T)) == 0);
        assert(std::memcmp(&middle, &middle2, sizeof(T)) == 0);
    }
}

template<bool HasUniqueObjectRepresentations = true, typename T>
void test_roundtrip_through_nested_T(T from) {
    struct Nested { T x; };
    static_assert(sizeof(Nested) == sizeof(T));

    Nested middle = std::bit_cast<Nested>(from);
    T to = std::bit_cast<T>(middle);
    Nested middle2 = std::bit_cast<Nested>(to);

    assert((from == to) == (from == from)); // because NaN

    if constexpr (HasUniqueObjectRepresentations) {
        assert(std::memcmp(&from, &middle, sizeof(T)) == 0);
        assert(std::memcmp(&to, &middle, sizeof(T)) == 0);
        assert(std::memcmp(&middle, &middle2, sizeof(T)) == 0);
    }
}

template <typename Intermediate, bool HasUniqueObjectRepresentations = true, typename T>
void test_roundtrip_through(T from) {
    static_assert(sizeof(Intermediate) == sizeof(T));

    Intermediate middle = std::bit_cast<Intermediate>(from);
    T to = std::bit_cast<T>(middle);
    Intermediate middle2 = std::bit_cast<Intermediate>(to);

    assert((from == to) == (from == from)); // because NaN

    if constexpr (HasUniqueObjectRepresentations) {
        assert(std::memcmp(&from, &middle, sizeof(T)) == 0);
        assert(std::memcmp(&to, &middle, sizeof(T)) == 0);
        assert(std::memcmp(&middle, &middle2, sizeof(T)) == 0);
    }
}

template <typename T>
constexpr std::array<T, 10> generate_signed_integral_values() {
    return {std::numeric_limits<T>::min(),
            std::numeric_limits<T>::min() + 1,
            static_cast<T>(-2), static_cast<T>(-1),
            static_cast<T>(0), static_cast<T>(1),
            static_cast<T>(2), static_cast<T>(3),
            std::numeric_limits<T>::max() - 1,
            std::numeric_limits<T>::max()};
}

template <typename T>
constexpr std::array<T, 6> generate_unsigned_integral_values() {
    return {static_cast<T>(0), static_cast<T>(1),
            static_cast<T>(2), static_cast<T>(3),
            std::numeric_limits<T>::max() - 1,
            std::numeric_limits<T>::max()};
}

bool tests() {
    for (bool b : {false, true}) {
        test_roundtrip_through_nested_T(b);
        test_roundtrip_through_buffer(b);
        test_roundtrip_through<char>(b);
    }

    for (char c : {'\0', 'a', 'b', 'c', 'd'}) {
        test_roundtrip_through_nested_T(c);
        test_roundtrip_through_buffer(c);
    }

    // Fundamental signed integer types
    for (signed char i : generate_signed_integral_values<signed char>()) {
        test_roundtrip_through_nested_T(i);
        test_roundtrip_through_buffer(i);
    }

    for (short i : generate_signed_integral_values<short>()) {
        test_roundtrip_through_nested_T(i);
        test_roundtrip_through_buffer(i);
    }

    for (int i : generate_signed_integral_values<int>()) {
        test_roundtrip_through_nested_T(i);
        test_roundtrip_through_buffer(i);
        test_roundtrip_through<float>(i);
    }

    for (long i : generate_signed_integral_values<long>()) {
        test_roundtrip_through_nested_T(i);
        test_roundtrip_through_buffer(i);
    }

    for (long long i : generate_signed_integral_values<long long>()) {
        test_roundtrip_through_nested_T(i);
        test_roundtrip_through_buffer(i);
        test_roundtrip_through<double>(i);
    }

    // Fundamental unsigned integer types
    for (unsigned char i : generate_unsigned_integral_values<unsigned char>()) {
        test_roundtrip_through_nested_T(i);
        test_roundtrip_through_buffer(i);
    }

    for (unsigned short i : generate_unsigned_integral_values<unsigned short>()) {
        test_roundtrip_through_nested_T(i);
        test_roundtrip_through_buffer(i);
    }

    for (unsigned int i : generate_unsigned_integral_values<unsigned int>()) {
        test_roundtrip_through_nested_T(i);
        test_roundtrip_through_buffer(i);
        test_roundtrip_through<float>(i);
    }

    for (unsigned long i : generate_unsigned_integral_values<unsigned long>()) {
        test_roundtrip_through_nested_T(i);
        test_roundtrip_through_buffer(i);
    }

    for (unsigned long long i : generate_unsigned_integral_values<unsigned long long>()) {
        test_roundtrip_through_nested_T(i);
        test_roundtrip_through_buffer(i);
        test_roundtrip_through<double>(i);
    }

    // Fixed width signed integer types
    for (std::int32_t i : generate_signed_integral_values<std::int32_t>()) {
        test_roundtrip_through_nested_T(i);
        test_roundtrip_through_buffer(i);
        test_roundtrip_through<int>(i);
        test_roundtrip_through<std::uint32_t>(i);
        test_roundtrip_through<float>(i);
    }

    for (std::int64_t i : generate_signed_integral_values<std::int64_t>()) {
        test_roundtrip_through_nested_T(i);
        test_roundtrip_through_buffer(i);
        test_roundtrip_through<long long>(i);
        test_roundtrip_through<std::uint64_t>(i);
        test_roundtrip_through<double>(i);
    }

    // Fixed width unsigned integer types
    for (std::uint32_t i : generate_unsigned_integral_values<std::uint32_t>()) {
        test_roundtrip_through_nested_T(i);
        test_roundtrip_through_buffer(i);
        test_roundtrip_through<int>(i);
        test_roundtrip_through<std::int32_t>(i);
        test_roundtrip_through<float>(i);
    }

    for (std::uint64_t i : generate_unsigned_integral_values<std::uint64_t>()) {
        test_roundtrip_through_nested_T(i);
        test_roundtrip_through_buffer(i);
        test_roundtrip_through<long long>(i);
        test_roundtrip_through<std::int64_t>(i);
        test_roundtrip_through<double>(i);
    }

    // Floating point types
    for (float i : {0.0f, 1.0f, -1.0f, 10.0f, -10.0f, 1e10f, 1e-10f, 1e20f, 1e-20f, 2.71828f, 3.14159f,
                    std::nanf(""),
                    __builtin_nanf("0x55550001"), // NaN with a payload
                    std::numeric_limits<float>::signaling_NaN(),
                    std::numeric_limits<float>::quiet_NaN()}) {
        test_roundtrip_through_nested_T(i);
        test_roundtrip_through_buffer(i);
        test_roundtrip_through<int>(i);
    }

    for (double i : {0.0, 1.0, -1.0, 10.0, -10.0, 1e10, 1e-10, 1e100, 1e-100,
                     2.718281828459045,
                     3.141592653589793238462643383279502884197169399375105820974944,
                     std::nan(""),
                     std::numeric_limits<double>::signaling_NaN(),
                     std::numeric_limits<double>::quiet_NaN()}) {
        test_roundtrip_through_nested_T(i);
        test_roundtrip_through_buffer(i);
        test_roundtrip_through<long long>(i);
    }

    for (long double i : {0.0l, 1.0l, -1.0l, 10.0l, -10.0l, 1e10l, 1e-10l, 1e100l, 1e-100l,
                          2.718281828459045l,
                          3.141592653589793238462643383279502884197169399375105820974944l,
                          std::nanl(""),
                          std::numeric_limits<long double>::signaling_NaN(),
                          std::numeric_limits<long double>::quiet_NaN()}) {
        // Note that x86's `long double` has 80 value bits and 48 padding bits.
        test_roundtrip_through_nested_T<false>(i);
        test_roundtrip_through_buffer<false>(i);

#if __SIZEOF_LONG_DOUBLE__ == __SIZEOF_DOUBLE__
        test_roundtrip_through<double, false>(i);
#endif
#if defined(__SIZEOF_INT128__) && __SIZEOF_LONG_DOUBLE__ == __SIZEOF_INT128__ &&                                       \
    !TEST_HAS_FEATURE(memory_sanitizer) // Some bits are just padding.
        test_roundtrip_through<__int128_t, false>(i);
        test_roundtrip_through<__uint128_t, false>(i);
#endif
    }

    return true;
}

// TODO: There doesn't seem to be a way to perform non-trivial correctness
//       tests inside constexpr.
constexpr bool basic_constexpr_test() {
    struct Nested { char buffer[sizeof(int)]; };
    int from = 3;
    Nested middle = std::bit_cast<Nested>(from);
    int to = std::bit_cast<int>(middle);
    assert(from == to);
    return true;
}

int main(int, char**) {
    tests();
    static_assert(basic_constexpr_test());
    return 0;
}