master
  1const std = @import("std");
  2const builtin = @import("builtin");
  3const assert = std.debug.assert;
  4const expect = std.testing.expect;
  5const expectEqual = std.testing.expectEqual;
  6const math = std.math;
  7const maxInt = std.math.maxInt;
  8const minInt = std.math.minInt;
  9const native_endian = builtin.target.cpu.arch.endian();
 10
 11test "@bitCast iX -> uX (32, 64)" {
 12    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
 13
 14    const bit_values = [_]usize{ 32, 64 };
 15
 16    inline for (bit_values) |bits| {
 17        try testBitCast(bits);
 18        try comptime testBitCast(bits);
 19    }
 20}
 21
 22test "@bitCast iX -> uX (8, 16, 128)" {
 23    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
 24    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
 25    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
 26    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
 27
 28    const bit_values = [_]usize{ 8, 16, 128 };
 29
 30    inline for (bit_values) |bits| {
 31        try testBitCast(bits);
 32        try comptime testBitCast(bits);
 33    }
 34}
 35
 36test "@bitCast iX -> uX exotic integers" {
 37    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
 38    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
 39    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
 40    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
 41    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
 42    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
 43
 44    const bit_values = [_]usize{ 1, 48, 27, 512, 493, 293, 125, 204, 112 };
 45
 46    inline for (bit_values) |bits| {
 47        try testBitCast(bits);
 48        try comptime testBitCast(bits);
 49    }
 50}
 51
 52fn testBitCast(comptime N: usize) !void {
 53    const iN = std.meta.Int(.signed, N);
 54    const uN = std.meta.Int(.unsigned, N);
 55
 56    try expect(conv_iN(N, -1) == maxInt(uN));
 57    try expect(conv_uN(N, maxInt(uN)) == -1);
 58
 59    try expect(conv_iN(N, maxInt(iN)) == maxInt(iN));
 60    try expect(conv_uN(N, maxInt(iN)) == maxInt(iN));
 61
 62    try expect(conv_uN(N, 1 << (N - 1)) == minInt(iN));
 63    try expect(conv_iN(N, minInt(iN)) == (1 << (N - 1)));
 64
 65    try expect(conv_uN(N, 0) == 0);
 66    try expect(conv_iN(N, 0) == 0);
 67
 68    if (N > 24) {
 69        try expect(conv_uN(N, 0xf23456) == 0xf23456);
 70    }
 71}
 72
 73fn conv_iN(comptime N: usize, x: std.meta.Int(.signed, N)) std.meta.Int(.unsigned, N) {
 74    return @as(std.meta.Int(.unsigned, N), @bitCast(x));
 75}
 76
 77fn conv_uN(comptime N: usize, x: std.meta.Int(.unsigned, N)) std.meta.Int(.signed, N) {
 78    return @as(std.meta.Int(.signed, N), @bitCast(x));
 79}
 80
 81test "bitcast uX to bytes" {
 82    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
 83    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
 84    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
 85    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
 86    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
 87    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
 88
 89    const bit_values = [_]usize{ 1, 48, 27, 512, 493, 293, 125, 204, 112 };
 90    inline for (bit_values) |bits| {
 91        try testBitCast(bits);
 92        try comptime testBitCast(bits);
 93    }
 94}
 95
 96fn testBitCastuXToBytes(comptime N: usize) !void {
 97
 98    // The location of padding bits in these layouts are technically not defined
 99    // by LLVM, but we currently allow exotic integers to be cast (at comptime)
100    // to types that expose their padding bits anyway.
101    //
102    // This test at least makes sure those bits are matched by the runtime behavior
103    // on the platforms we target. If the above behavior is restricted after all,
104    // this test should be deleted.
105
106    const T = std.meta.Int(.unsigned, N);
107    for ([_]T{ 0, ~@as(T, 0) }) |init_value| {
108        var x: T = init_value;
109        const bytes = std.mem.asBytes(&x);
110
111        const byte_count = (N + 7) / 8;
112        switch (native_endian) {
113            .little => {
114                var byte_i = 0;
115                while (byte_i < (byte_count - 1)) : (byte_i += 1) {
116                    try expect(bytes[byte_i] == 0xff);
117                }
118                try expect(((bytes[byte_i] ^ 0xff) << -%@as(u3, @truncate(N))) == 0);
119            },
120            .big => {
121                var byte_i = byte_count - 1;
122                while (byte_i > 0) : (byte_i -= 1) {
123                    try expect(bytes[byte_i] == 0xff);
124                }
125                try expect(((bytes[byte_i] ^ 0xff) << -%@as(u3, @truncate(N))) == 0);
126            },
127        }
128    }
129}
130
131test "nested bitcast" {
132    const S = struct {
133        fn moo(x: isize) !void {
134            try expect(@as(isize, @intCast(42)) == x);
135        }
136
137        fn foo(x: isize) !void {
138            try @This().moo(
139                @as(isize, @bitCast(if (x != 0) @as(usize, @bitCast(x)) else @as(usize, @bitCast(x)))),
140            );
141        }
142    };
143
144    try S.foo(42);
145    try comptime S.foo(42);
146}
147
148// issue #3010: compiler segfault
149test "bitcast literal [4]u8 param to u32" {
150    const ip = @as(u32, @bitCast([_]u8{ 255, 255, 255, 255 }));
151    try expect(ip == maxInt(u32));
152}
153
154test "bitcast generates a temporary value" {
155    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
156
157    var y: u16 = 0x55AA;
158    _ = &y;
159    const x: u16 = @bitCast(@as([2]u8, @bitCast(y)));
160    try expect(y == x);
161}
162
163test "@bitCast packed structs at runtime and comptime" {
164    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
165    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
166    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
167
168    const Full = packed struct {
169        number: u16,
170    };
171    const Divided = packed struct {
172        half1: u8,
173        quarter3: u4,
174        quarter4: u4,
175    };
176    const S = struct {
177        fn doTheTest() !void {
178            var full = Full{ .number = 0x1234 };
179            _ = &full;
180            const two_halves: Divided = @bitCast(full);
181            try expect(two_halves.half1 == 0x34);
182            try expect(two_halves.quarter3 == 0x2);
183            try expect(two_halves.quarter4 == 0x1);
184        }
185    };
186    try S.doTheTest();
187    try comptime S.doTheTest();
188}
189
190test "@bitCast extern structs at runtime and comptime" {
191    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
192    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
193    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
194
195    const Full = extern struct {
196        number: u16,
197    };
198    const TwoHalves = extern struct {
199        half1: u8,
200        half2: u8,
201    };
202    const S = struct {
203        fn doTheTest() !void {
204            var full = Full{ .number = 0x1234 };
205            _ = &full;
206            const two_halves: TwoHalves = @bitCast(full);
207            switch (native_endian) {
208                .big => {
209                    try expect(two_halves.half1 == 0x12);
210                    try expect(two_halves.half2 == 0x34);
211                },
212                .little => {
213                    try expect(two_halves.half1 == 0x34);
214                    try expect(two_halves.half2 == 0x12);
215                },
216            }
217        }
218    };
219    try S.doTheTest();
220    try comptime S.doTheTest();
221}
222
223test "bitcast packed struct to integer and back" {
224    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
225    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
226    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
227
228    const LevelUpMove = packed struct {
229        move_id: u9,
230        level: u7,
231    };
232    const S = struct {
233        fn doTheTest() !void {
234            var move = LevelUpMove{ .move_id = 1, .level = 2 };
235            _ = &move;
236            const v: u16 = @bitCast(move);
237            const back_to_a_move: LevelUpMove = @bitCast(v);
238            try expect(back_to_a_move.move_id == 1);
239            try expect(back_to_a_move.level == 2);
240        }
241    };
242    try S.doTheTest();
243    try comptime S.doTheTest();
244}
245
246test "implicit cast to error union by returning" {
247    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
248    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
249
250    const S = struct {
251        fn entry() !void {
252            try expect((func(-1) catch unreachable) == maxInt(u64));
253        }
254        pub fn func(sz: i64) anyerror!u64 {
255            return @as(u64, @bitCast(sz));
256        }
257    };
258    try S.entry();
259    try comptime S.entry();
260}
261
262test "bitcast packed struct literal to byte" {
263    const Foo = packed struct {
264        value: u8,
265    };
266    const casted = @as(u8, @bitCast(Foo{ .value = 0xF }));
267    try expect(casted == 0xf);
268}
269
270test "comptime bitcast used in expression has the correct type" {
271    const Foo = packed struct {
272        value: u8,
273    };
274    try expect(@as(u8, @bitCast(Foo{ .value = 0xF })) == 0xf);
275}
276
277test "bitcast passed as tuple element" {
278    const S = struct {
279        fn foo(args: anytype) !void {
280            comptime assert(@TypeOf(args[0]) == f32);
281            try expect(args[0] == 12.34);
282        }
283    };
284    try S.foo(.{@as(f32, @bitCast(@as(u32, 0x414570A4)))});
285}
286
287test "triple level result location with bitcast sandwich passed as tuple element" {
288    const S = struct {
289        fn foo(args: anytype) !void {
290            comptime assert(@TypeOf(args[0]) == f64);
291            try expect(args[0] > 12.33 and args[0] < 12.35);
292        }
293    };
294    try S.foo(.{@as(f64, @as(f32, @bitCast(@as(u32, 0x414570A4))))});
295}
296
297test "@bitCast packed struct of floats" {
298    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
299    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
300    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
301    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
302    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
303    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
304    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
305
306    const Foo = packed struct {
307        a: f16 = 0,
308        b: f32 = 1,
309        c: f64 = 2,
310        d: f128 = 3,
311    };
312
313    const Foo2 = packed struct {
314        a: f16 = 0,
315        b: f32 = 1,
316        c: f64 = 2,
317        d: f128 = 3,
318    };
319
320    const S = struct {
321        fn doTheTest() !void {
322            var foo = Foo{};
323            _ = &foo;
324            const v: Foo2 = @bitCast(foo);
325            try expect(v.a == foo.a);
326            try expect(v.b == foo.b);
327            try expect(v.c == foo.c);
328            try expect(v.d == foo.d);
329        }
330    };
331    try S.doTheTest();
332    try comptime S.doTheTest();
333}
334
335test "comptime @bitCast packed struct to int and back" {
336    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
337    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
338    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
339    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
340    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
341    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
342    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
343
344    const S = packed struct {
345        void: void = {},
346        uint: u8 = 13,
347        uint_bit_aligned: u3 = 2,
348        iint_pos: i4 = 1,
349        iint_neg4: i3 = -4,
350        iint_neg2: i3 = -2,
351        float: f32 = 3.14,
352        @"enum": enum(u2) { A, B = 1, C, D } = .B,
353        vectorb: @Vector(3, bool) = .{ true, false, true },
354        vectori: @Vector(2, u8) = .{ 127, 42 },
355        vectorf: @Vector(2, f16) = .{ 3.14, 2.71 },
356    };
357    const Int = @typeInfo(S).@"struct".backing_integer.?;
358
359    // S -> Int
360    var s: S = .{};
361    _ = &s;
362    try expectEqual(@as(Int, @bitCast(s)), comptime @as(Int, @bitCast(S{})));
363
364    // Int -> S
365    var i: Int = 0;
366    _ = &i;
367    const rt_cast = @as(S, @bitCast(i));
368    const ct_cast = comptime @as(S, @bitCast(@as(Int, 0)));
369    inline for (@typeInfo(S).@"struct".fields) |field| {
370        try expectEqual(@field(rt_cast, field.name), @field(ct_cast, field.name));
371    }
372}
373
374test "comptime bitcast with fields following f80" {
375    if (true) {
376        // https://github.com/ziglang/zig/issues/19387
377        return error.SkipZigTest;
378    }
379
380    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
381    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
382    if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
383
384    const FloatT = extern struct { f: f80, x: u128 align(16) };
385    const x: FloatT = .{ .f = 0.5, .x = 123 };
386    var x_as_uint: u256 = comptime @as(u256, @bitCast(x));
387    _ = &x_as_uint;
388
389    try expect(x.f == @as(FloatT, @bitCast(x_as_uint)).f);
390    try expect(x.x == @as(FloatT, @bitCast(x_as_uint)).x);
391}
392
393test "bitcast vector to integer and back" {
394    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
395    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
396    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
397    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
398    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
399    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
400    if (builtin.cpu.arch == .aarch64_be and builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;
401
402    const arr: [16]bool = [_]bool{ true, false } ++ [_]bool{true} ** 14;
403    var x: @Vector(16, bool) = @splat(true);
404    x[1] = false;
405    try expect(@as(u16, @bitCast(x)) == comptime @as(u16, @bitCast(@as(@Vector(16, bool), arr))));
406}
407
408fn bitCastWrapper16(x: f16) u16 {
409    return @as(u16, @bitCast(x));
410}
411fn bitCastWrapper32(x: f32) u32 {
412    return @as(u32, @bitCast(x));
413}
414fn bitCastWrapper64(x: f64) u64 {
415    return @as(u64, @bitCast(x));
416}
417fn bitCastWrapper128(x: f128) u128 {
418    return @as(u128, @bitCast(x));
419}
420test "bitcast nan float does not modify signaling bit" {
421    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
422    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
423    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
424    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
425
426    const snan_u16: u16 = 0x7D00;
427    const snan_u32: u32 = 0x7FA00000;
428    const snan_u64: u64 = 0x7FF4000000000000;
429    const snan_u128: u128 = 0x7FFF4000000000000000000000000000;
430
431    // 16 bit
432    const snan_f16_const = math.snan(f16);
433    try expectEqual(snan_u16, @as(u16, @bitCast(snan_f16_const)));
434    try expectEqual(snan_u16, bitCastWrapper16(snan_f16_const));
435
436    var snan_f16_var = math.snan(f16);
437    _ = &snan_f16_var;
438    try expectEqual(snan_u16, @as(u16, @bitCast(snan_f16_var)));
439    try expectEqual(snan_u16, bitCastWrapper16(snan_f16_var));
440
441    // 32 bit
442    const snan_f32_const = math.snan(f32);
443    try expectEqual(snan_u32, @as(u32, @bitCast(snan_f32_const)));
444    try expectEqual(snan_u32, bitCastWrapper32(snan_f32_const));
445
446    var snan_f32_var = math.snan(f32);
447    _ = &snan_f32_var;
448    try expectEqual(snan_u32, @as(u32, @bitCast(snan_f32_var)));
449    try expectEqual(snan_u32, bitCastWrapper32(snan_f32_var));
450
451    // 64 bit
452    const snan_f64_const = math.snan(f64);
453    try expectEqual(snan_u64, @as(u64, @bitCast(snan_f64_const)));
454    try expectEqual(snan_u64, bitCastWrapper64(snan_f64_const));
455
456    var snan_f64_var = math.snan(f64);
457    _ = &snan_f64_var;
458    try expectEqual(snan_u64, @as(u64, @bitCast(snan_f64_var)));
459    try expectEqual(snan_u64, bitCastWrapper64(snan_f64_var));
460
461    // 128 bit
462    const snan_f128_const = math.snan(f128);
463    try expectEqual(snan_u128, @as(u128, @bitCast(snan_f128_const)));
464    try expectEqual(snan_u128, bitCastWrapper128(snan_f128_const));
465
466    var snan_f128_var = math.snan(f128);
467    _ = &snan_f128_var;
468    try expectEqual(snan_u128, @as(u128, @bitCast(snan_f128_var)));
469    try expectEqual(snan_u128, bitCastWrapper128(snan_f128_var));
470}
471
472test "@bitCast of packed struct of bools all true" {
473    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
474    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
475    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
476    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // TODO
477    if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
478
479    const P = packed struct {
480        b0: bool,
481        b1: bool,
482        b2: bool,
483        b3: bool,
484    };
485    var p = std.mem.zeroes(P);
486    p.b0 = true;
487    p.b1 = true;
488    p.b2 = true;
489    p.b3 = true;
490    try expect(@as(u8, @as(u4, @bitCast(p))) == 15);
491}
492
493test "@bitCast of packed struct of bools all false" {
494    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
495    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
496    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
497    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // TODO
498
499    const P = packed struct {
500        b0: bool,
501        b1: bool,
502        b2: bool,
503        b3: bool,
504    };
505    var p = std.mem.zeroes(P);
506    p.b0 = false;
507    p.b1 = false;
508    p.b2 = false;
509    p.b3 = false;
510    try expect(@as(u8, @as(u4, @bitCast(p))) == 0);
511}
512
513test "@bitCast of packed struct containing pointer" {
514    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
515    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
516    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
517    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // TODO
518    if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // https://discourse.llvm.org/t/rfc-remove-most-constant-expressions/63179
519
520    const S = struct {
521        const A = packed struct {
522            ptr: *const u32,
523        };
524
525        const B = packed struct {
526            ptr: *const i32,
527        };
528
529        fn doTheTest() !void {
530            const x: u32 = 123;
531            var a: A = undefined;
532            a = .{ .ptr = &x };
533            const b: B = @bitCast(a);
534            try expect(b.ptr.* == 123);
535        }
536    };
537
538    try S.doTheTest();
539    try comptime S.doTheTest();
540}
541
542test "@bitCast of extern struct containing pointer" {
543    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
544    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
545    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
546    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
547    if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // TODO
548
549    const S = struct {
550        const A = extern struct {
551            ptr: *const u32,
552        };
553
554        const B = extern struct {
555            ptr: *const i32,
556        };
557
558        fn doTheTest() !void {
559            const x: u32 = 123;
560            var a: A = undefined;
561            a = .{ .ptr = &x };
562            const b: B = @bitCast(a);
563            try expect(b.ptr.* == 123);
564        }
565    };
566
567    try S.doTheTest();
568    try comptime S.doTheTest();
569}