1const std = @import("std");
2const builtin = @import("builtin");
3const expect = std.testing.expect;
4const assert = std.debug.assert;
5const native_endian = builtin.target.cpu.arch.endian();
6
7test "reinterpret bytes as integer with nonzero offset" {
8 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
9 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
10
11 try testReinterpretBytesAsInteger();
12 try comptime testReinterpretBytesAsInteger();
13}
14
15fn testReinterpretBytesAsInteger() !void {
16 const bytes = "\x12\x34\x56\x78\xab";
17 const expected = switch (native_endian) {
18 .little => 0xab785634,
19 .big => 0x345678ab,
20 };
21 try expect(@as(*align(1) const u32, @ptrCast(bytes[1..5])).* == expected);
22}
23
24test "reinterpret an array over multiple elements, with no well-defined layout" {
25 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
26 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
27 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
28
29 try testReinterpretWithOffsetAndNoWellDefinedLayout();
30 try comptime testReinterpretWithOffsetAndNoWellDefinedLayout();
31}
32
33fn testReinterpretWithOffsetAndNoWellDefinedLayout() !void {
34 const bytes: ?[5]?u8 = [5]?u8{ 0x12, 0x34, 0x56, 0x78, 0x9a };
35 const ptr = &bytes.?[1];
36 const copy: [4]?u8 = @as(*const [4]?u8, @ptrCast(ptr)).*;
37 _ = copy;
38 //try expect(@ptrCast(*align(1)?u8, bytes[1..5]).* == );
39}
40
41test "reinterpret bytes inside auto-layout struct as integer with nonzero offset" {
42 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
43 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
44
45 try testReinterpretStructWrappedBytesAsInteger();
46 try comptime testReinterpretStructWrappedBytesAsInteger();
47}
48
49fn testReinterpretStructWrappedBytesAsInteger() !void {
50 const S = struct { bytes: [5:0]u8 };
51 const obj = S{ .bytes = "\x12\x34\x56\x78\xab".* };
52 const expected = switch (native_endian) {
53 .little => 0xab785634,
54 .big => 0x345678ab,
55 };
56 try expect(@as(*align(1) const u32, @ptrCast(obj.bytes[1..5])).* == expected);
57}
58
59test "reinterpret bytes of an array into an extern struct" {
60 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
61 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
62
63 try testReinterpretBytesAsExternStruct();
64 try comptime testReinterpretBytesAsExternStruct();
65}
66
67fn testReinterpretBytesAsExternStruct() !void {
68 var bytes align(2) = [_]u8{ 1, 2, 3, 4, 5, 6 };
69
70 const S = extern struct {
71 a: u8,
72 b: u16,
73 c: u8,
74 };
75
76 const ptr: *const S = @ptrCast(&bytes);
77 const val = ptr.c;
78 try expect(val == 5);
79}
80
81test "reinterpret bytes of an extern struct (with under-aligned fields) into another" {
82 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
83
84 try testReinterpretExternStructAsExternStruct();
85 try comptime testReinterpretExternStructAsExternStruct();
86}
87
88fn testReinterpretExternStructAsExternStruct() !void {
89 const S1 = extern struct {
90 a: u8,
91 b: u16,
92 c: u8,
93 };
94 comptime var bytes align(2) = S1{ .a = 0, .b = 0, .c = 5 };
95
96 const S2 = extern struct {
97 a: u32 align(2),
98 c: u8,
99 };
100 const ptr: *const S2 = @ptrCast(&bytes);
101 const val = ptr.c;
102 try expect(val == 5);
103}
104
105test "reinterpret bytes of an extern struct into another" {
106 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
107
108 try testReinterpretOverAlignedExternStructAsExternStruct();
109 try comptime testReinterpretOverAlignedExternStructAsExternStruct();
110}
111
112fn testReinterpretOverAlignedExternStructAsExternStruct() !void {
113 const S1 = extern struct {
114 a: u32,
115 b: u32,
116 c: u8,
117 };
118 comptime var bytes: S1 = .{ .a = 0, .b = 0, .c = 5 };
119
120 const S2 = extern struct {
121 a0: u32,
122 a1: u16,
123 a2: u16,
124 c: u8,
125 };
126 const ptr: *const S2 = @ptrCast(&bytes);
127 const val = ptr.c;
128 try expect(val == 5);
129}
130
131test "lower reinterpreted comptime field ptr (with under-aligned fields)" {
132 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
133 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
134
135 // Test lowering a field ptr
136 comptime var bytes align(2) = [_]u8{ 1, 2, 3, 4, 5, 6 };
137 const S = extern struct {
138 a: u32 align(2),
139 c: u8,
140 };
141 comptime var ptr = @as(*const S, @ptrCast(&bytes));
142 const val = &ptr.c;
143 try expect(val.* == 5);
144
145 // Test lowering an elem ptr
146 comptime var src_value = S{ .a = 15, .c = 5 };
147 comptime var ptr2 = @as(*[@sizeOf(S)]u8, @ptrCast(&src_value));
148 const val2 = &ptr2[4];
149 try expect(val2.* == 5);
150}
151
152test "lower reinterpreted comptime field ptr" {
153 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
154 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
155
156 // Test lowering a field ptr
157 comptime var bytes align(4) = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
158 const S = extern struct {
159 a: u32,
160 c: u8,
161 };
162 comptime var ptr = @as(*const S, @ptrCast(&bytes));
163 const val = &ptr.c;
164 try expect(val.* == 5);
165
166 // Test lowering an elem ptr
167 comptime var src_value = S{ .a = 15, .c = 5 };
168 comptime var ptr2 = @as(*[@sizeOf(S)]u8, @ptrCast(&src_value));
169 const val2 = &ptr2[4];
170 try expect(val2.* == 5);
171}
172
173test "reinterpret struct field at comptime" {
174 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
175 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
176
177 const numNative = comptime Bytes.init(0x12345678);
178 if (native_endian != .little) {
179 try expect(std.mem.eql(u8, &[_]u8{ 0x12, 0x34, 0x56, 0x78 }, &numNative.bytes));
180 } else {
181 try expect(std.mem.eql(u8, &[_]u8{ 0x78, 0x56, 0x34, 0x12 }, &numNative.bytes));
182 }
183}
184
185const Bytes = struct {
186 bytes: [4]u8,
187
188 pub fn init(v: u32) Bytes {
189 var res: Bytes = undefined;
190 @as(*align(1) u32, @ptrCast(&res.bytes)).* = v;
191
192 return res;
193 }
194};
195
196test "ptrcast of const integer has the correct object size" {
197 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
198 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
199
200 const is_value = ~@as(isize, @intCast(std.math.minInt(isize)));
201 const is_bytes = @as([*]const u8, @ptrCast(&is_value))[0..@sizeOf(isize)];
202 if (@sizeOf(isize) == 8) {
203 switch (native_endian) {
204 .little => {
205 try expect(is_bytes[0] == 0xff);
206 try expect(is_bytes[1] == 0xff);
207 try expect(is_bytes[2] == 0xff);
208 try expect(is_bytes[3] == 0xff);
209
210 try expect(is_bytes[4] == 0xff);
211 try expect(is_bytes[5] == 0xff);
212 try expect(is_bytes[6] == 0xff);
213 try expect(is_bytes[7] == 0x7f);
214 },
215 .big => {
216 try expect(is_bytes[0] == 0x7f);
217 try expect(is_bytes[1] == 0xff);
218 try expect(is_bytes[2] == 0xff);
219 try expect(is_bytes[3] == 0xff);
220
221 try expect(is_bytes[4] == 0xff);
222 try expect(is_bytes[5] == 0xff);
223 try expect(is_bytes[6] == 0xff);
224 try expect(is_bytes[7] == 0xff);
225 },
226 }
227 }
228}
229
230test "implicit optional pointer to optional anyopaque pointer" {
231 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
232 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
233 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
234
235 var buf: [4]u8 = "aoeu".*;
236 const x: ?[*]u8 = &buf;
237 const y: ?*anyopaque = x;
238 const z: *[4]u8 = @ptrCast(y);
239 try expect(std.mem.eql(u8, z, "aoeu"));
240}
241
242test "@ptrCast slice to slice" {
243 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
244 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
245 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
246
247 const S = struct {
248 fn foo(slice: []u32) []i32 {
249 return @as([]i32, @ptrCast(slice));
250 }
251 };
252 var buf: [4]u32 = .{ 0, 0, 0, 0 };
253 const alias = S.foo(&buf);
254 alias[1] = 42;
255 try expect(buf[1] == 42);
256 try expect(alias.len == 4);
257}
258
259test "comptime @ptrCast a subset of an array, then write through it" {
260 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
261 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
262
263 comptime {
264 var buff: [16]u8 align(4) = undefined;
265 const len_bytes = @as(*u32, @ptrCast(&buff));
266 len_bytes.* = 16;
267 const source = "abcdef";
268 @memcpy(buff[4 .. 4 + source.len], source);
269 }
270}
271
272test "@ptrCast undefined value at comptime" {
273 const S = struct {
274 fn transmute(comptime T: type, comptime U: type, value: T) U {
275 return @as(*const U, @ptrCast(&value)).*;
276 }
277 };
278 comptime {
279 const x = S.transmute(u64, i32, undefined);
280 _ = x;
281 }
282}
283
284test "comptime @ptrCast with packed struct leaves value unmodified" {
285 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
286
287 const S = packed struct { three: u3 };
288 const st: S = .{ .three = 6 };
289 try expect(st.three == 6);
290 const p: *const [1]u3 = @ptrCast(&st);
291 try expect(p.*[0] == 6);
292 try expect(st.three == 6);
293}
294
295test "@ptrCast restructures comptime-only array" {
296 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
297
298 {
299 const a3a2: [3][2]comptime_int = .{
300 .{ 1, 2 },
301 .{ 3, 4 },
302 .{ 5, 6 },
303 };
304 const a2a3: *const [2][3]comptime_int = @ptrCast(&a3a2);
305 comptime assert(a2a3[0][0] == 1);
306 comptime assert(a2a3[0][1] == 2);
307 comptime assert(a2a3[0][2] == 3);
308 comptime assert(a2a3[1][0] == 4);
309 comptime assert(a2a3[1][1] == 5);
310 comptime assert(a2a3[1][2] == 6);
311 }
312
313 {
314 const a6a1: [6][1]comptime_int = .{
315 .{1}, .{2}, .{3}, .{4}, .{5}, .{6},
316 };
317 const a1a2a3: *const [1][2][3]comptime_int = @ptrCast(&a6a1);
318 comptime assert(a1a2a3[0][0][0] == 1);
319 comptime assert(a1a2a3[0][0][1] == 2);
320 comptime assert(a1a2a3[0][0][2] == 3);
321 comptime assert(a1a2a3[0][1][0] == 4);
322 comptime assert(a1a2a3[0][1][1] == 5);
323 comptime assert(a1a2a3[0][1][2] == 6);
324 }
325
326 {
327 const a1: [1]comptime_int = .{123};
328 const raw: *const comptime_int = @ptrCast(&a1);
329 comptime assert(raw.* == 123);
330 }
331
332 {
333 const raw: comptime_int = 123;
334 const a1: *const [1]comptime_int = @ptrCast(&raw);
335 comptime assert(a1[0] == 123);
336 }
337}
338
339test "@ptrCast restructures sliced comptime-only array" {
340 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
341
342 const a3a2: [4][2]comptime_int = .{
343 .{ 1, 2 },
344 .{ 3, 4 },
345 .{ 5, 6 },
346 .{ 7, 8 },
347 };
348
349 const sub: *const [4]comptime_int = @ptrCast(a3a2[1..]);
350 comptime assert(sub[0] == 3);
351 comptime assert(sub[1] == 4);
352 comptime assert(sub[2] == 5);
353 comptime assert(sub[3] == 6);
354}
355
356test "@ptrCast slice multiplying length" {
357 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
358 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
359 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
360
361 const S = struct {
362 fn doTheTest(zero: u32) !void {
363 const in: []const u32 = &.{ zero, zero };
364 const out: []const u8 = @ptrCast(in);
365 try expect(out.len == 8);
366 try expect(@as([*]const u8, @ptrCast(in.ptr)) == out.ptr);
367 }
368 };
369 try S.doTheTest(0);
370 try comptime S.doTheTest(0);
371}
372
373test "@ptrCast array pointer to slice multiplying length" {
374 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
375 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
376 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
377
378 const S = struct {
379 fn doTheTest(zero: u32) !void {
380 const in: *const [2]u32 = &.{ zero, zero };
381 const out: []const u8 = @ptrCast(in);
382 try expect(out.len == 8);
383 try expect(out.ptr == @as([*]const u8, @ptrCast(in.ptr)));
384 }
385 };
386 try S.doTheTest(0);
387 try comptime S.doTheTest(0);
388}
389
390test "@ptrCast slice dividing length" {
391 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
392 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
393 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
394
395 const S = struct {
396 fn doTheTest(zero: u8) !void {
397 const in: []const u8 = &.{ zero, zero, zero, zero, zero, zero, zero, zero };
398 const out: []align(1) const u32 = @ptrCast(in);
399 try expect(out.len == 2);
400 try expect(out.ptr == @as([*]align(1) const u32, @ptrCast(in.ptr)));
401 }
402 };
403 try S.doTheTest(0);
404 try comptime S.doTheTest(0);
405}
406
407test "@ptrCast array pointer to slice dividing length" {
408 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
409 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
410 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
411
412 const S = struct {
413 fn doTheTest(zero: u8) !void {
414 const in: *const [8]u8 = &.{ zero, zero, zero, zero, zero, zero, zero, zero };
415 const out: []align(1) const u32 = @ptrCast(in);
416 try expect(out.len == 2);
417 try expect(out.ptr == @as([*]align(1) const u32, @ptrCast(in.ptr)));
418 }
419 };
420 try S.doTheTest(0);
421 try comptime S.doTheTest(0);
422}
423
424test "@ptrCast slice with complex length increase" {
425 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
426 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
427 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
428
429 const TwoBytes = [2]u8;
430 const ThreeBytes = [3]u8;
431
432 const S = struct {
433 fn doTheTest(zero: ThreeBytes) !void {
434 const in: []const ThreeBytes = &.{ zero, zero };
435 const out: []const TwoBytes = @ptrCast(in);
436 try expect(out.len == 3);
437 try expect(out.ptr == @as([*]const TwoBytes, @ptrCast(in.ptr)));
438 }
439 };
440 try S.doTheTest(@splat(0));
441 try comptime S.doTheTest(@splat(0));
442}
443
444test "@ptrCast array pointer to slice with complex length increase" {
445 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
446 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
447 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
448
449 const TwoBytes = [2]u8;
450 const ThreeBytes = [3]u8;
451
452 const S = struct {
453 fn doTheTest(zero: ThreeBytes) !void {
454 const in: *const [2]ThreeBytes = &.{ zero, zero };
455 const out: []const TwoBytes = @ptrCast(in);
456 try expect(out.len == 3);
457 try expect(out.ptr == @as([*]const TwoBytes, @ptrCast(in.ptr)));
458 }
459 };
460 try S.doTheTest(@splat(0));
461 try comptime S.doTheTest(@splat(0));
462}
463
464test "@ptrCast slice with complex length decrease" {
465 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
466 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
467 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
468
469 const TwoBytes = [2]u8;
470 const ThreeBytes = [3]u8;
471
472 const S = struct {
473 fn doTheTest(zero: TwoBytes) !void {
474 const in: []const TwoBytes = &.{ zero, zero, zero };
475 const out: []const ThreeBytes = @ptrCast(in);
476 try expect(out.len == 2);
477 try expect(out.ptr == @as([*]const ThreeBytes, @ptrCast(in.ptr)));
478 }
479 };
480 try S.doTheTest(@splat(0));
481 try comptime S.doTheTest(@splat(0));
482}
483
484test "@ptrCast array pointer to slice with complex length decrease" {
485 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
486 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
487 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
488
489 const TwoBytes = [2]u8;
490 const ThreeBytes = [3]u8;
491
492 const S = struct {
493 fn doTheTest(zero: TwoBytes) !void {
494 const in: *const [3]TwoBytes = &.{ zero, zero, zero };
495 const out: []const ThreeBytes = @ptrCast(in);
496 try expect(out.len == 2);
497 try expect(out.ptr == @as([*]const ThreeBytes, @ptrCast(in.ptr)));
498 }
499 };
500 try S.doTheTest(@splat(0));
501 try comptime S.doTheTest(@splat(0));
502}
503
504test "@ptrCast slice of zero-bit type to different slice" {
505 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
506 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
507 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
508
509 const S = struct {
510 fn doTheTest(comptime T: type, zero_bits: []const T) !void {
511 const out: []const u8 = @ptrCast(zero_bits);
512 try expect(out.len == 0);
513 }
514 };
515 try S.doTheTest(void, &.{ {}, {}, {} });
516 try S.doTheTest(u0, &.{ 0, 0, 0, 0 });
517 try S.doTheTest(packed struct(u0) {}, &.{ .{}, .{} });
518 try comptime S.doTheTest(void, &.{ {}, {}, {} });
519 try comptime S.doTheTest(u0, &.{ 0, 0, 0, 0 });
520 try comptime S.doTheTest(packed struct(u0) {}, &.{ .{}, .{} });
521}
522
523test "@ptrCast single-item pointer to slice with length 1" {
524 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
525 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
526 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
527
528 const S = struct {
529 fn doTheTest(comptime T: type, ptr: *const T) !void {
530 const slice: []const T = @ptrCast(ptr);
531 try expect(slice.len == 1);
532 try expect(&slice[0] == ptr);
533 }
534 };
535 try S.doTheTest(u8, &123);
536 try S.doTheTest(void, &{});
537 try S.doTheTest(struct { x: u32 }, &.{ .x = 123 });
538 try comptime S.doTheTest(u8, &123);
539 try comptime S.doTheTest(void, &{});
540 try comptime S.doTheTest(struct { x: u32 }, &.{ .x = 123 });
541}
542
543test "@ptrCast single-item pointer to slice of bytes" {
544 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
545 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
546 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
547
548 const S = struct {
549 fn doTheTest(comptime T: type, ptr: *const T) !void {
550 const slice: []const u8 = @ptrCast(ptr);
551 try expect(slice.len == @sizeOf(T));
552 try expect(slice.ptr == @as([*]const u8, @ptrCast(ptr)));
553 }
554 };
555 try S.doTheTest(u16, &123);
556 try S.doTheTest(void, &{});
557 try S.doTheTest(struct { x: u32 }, &.{ .x = 123 });
558 try comptime S.doTheTest(u16, &123);
559 try comptime S.doTheTest(void, &{});
560 try comptime S.doTheTest(struct { x: u32 }, &.{ .x = 123 });
561}
562
563test "@ptrCast array pointer removing sentinel" {
564 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
565
566 const in: *const [4:0]u8 = &.{ 1, 2, 3, 4 };
567 const out: []const i8 = @ptrCast(in);
568 comptime assert(out.len == 4);
569 comptime assert(out[0] == 1);
570 comptime assert(out[1] == 2);
571 comptime assert(out[2] == 3);
572 comptime assert(out[3] == 4);
573}
574
575test "@ptrcast larger type to smaller one" {
576 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
577
578 const T = packed struct { x: u17 };
579 const a: u32 = 0;
580 const b: *const T = @ptrCast(&a);
581 const c = b.x;
582 _ = c;
583}