master
1const std = @import("std");
2const expect = std.testing.expect;
3const builtin = @import("builtin");
4const native_arch = builtin.target.cpu.arch;
5const assert = std.debug.assert;
6
7var foo: u8 align(4) = 100;
8
9test "global variable alignment" {
10 comptime assert(@typeInfo(@TypeOf(&foo)).pointer.alignment == 4);
11 comptime assert(@TypeOf(&foo) == *align(4) u8);
12 {
13 const slice = @as(*align(4) [1]u8, &foo)[0..];
14 comptime assert(@TypeOf(slice) == *align(4) [1]u8);
15 }
16}
17
18test "large alignment of local constant" {
19 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
20 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // flaky
21
22 const x: f32 align(128) = 12.34;
23 try std.testing.expect(@intFromPtr(&x) % 128 == 0);
24}
25
26test "slicing array of length 1 can not assume runtime index is always zero" {
27 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
28 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // flaky
29
30 var runtime_index: usize = 1;
31 _ = &runtime_index;
32 const slice = @as(*align(4) [1]u8, &foo)[runtime_index..];
33 try expect(@TypeOf(slice) == []u8);
34 try expect(slice.len == 0);
35 try expect(@as(u2, @truncate(@intFromPtr(slice.ptr) - 1)) == 0);
36}
37
38test "default alignment allows unspecified in type syntax" {
39 try expect(*u32 == *align(@alignOf(u32)) u32);
40}
41
42test "implicitly decreasing pointer alignment" {
43 const a: u32 align(4) = 3;
44 const b: u32 align(8) = 4;
45 try expect(addUnaligned(&a, &b) == 7);
46}
47
48fn addUnaligned(a: *align(1) const u32, b: *align(1) const u32) u32 {
49 return a.* + b.*;
50}
51
52test "@alignCast pointers" {
53 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
54
55 var x: u32 align(4) = 1;
56 expectsOnly1(&x);
57 try expect(x == 2);
58}
59fn expectsOnly1(x: *align(1) u32) void {
60 expects4(@alignCast(x));
61}
62fn expects4(x: *align(4) u32) void {
63 x.* += 1;
64}
65
66test "alignment of struct with pointer has same alignment as usize" {
67 try expect(@alignOf(struct {
68 a: i32,
69 b: *i32,
70 }) == @alignOf(usize));
71}
72
73test "alignment and size of structs with 128-bit fields" {
74 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
75 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
76
77 const A = struct {
78 x: u128,
79 };
80 const B = extern struct {
81 x: u128,
82 y: u8,
83 };
84 const expected = switch (builtin.cpu.arch) {
85 .amdgcn,
86 .arm,
87 .armeb,
88 .thumb,
89 .thumbeb,
90 .hexagon,
91 .lanai,
92 .mips,
93 .mipsel,
94 .powerpc,
95 .powerpcle,
96 .riscv32,
97 .s390x,
98 => .{
99 .a_align = 8,
100 .a_size = 16,
101
102 .b_align = 16,
103 .b_size = 32,
104
105 .u128_align = 8,
106 .u128_size = 16,
107 .u129_align = 8,
108 .u129_size = 24,
109 },
110
111 .aarch64,
112 .aarch64_be,
113 .bpfel,
114 .bpfeb,
115 .mips64,
116 .mips64el,
117 .nvptx,
118 .nvptx64,
119 .powerpc64,
120 .powerpc64le,
121 .sparc,
122 .sparc64,
123 .riscv64,
124 .wasm32,
125 .wasm64,
126 .x86,
127 .x86_64,
128 => .{
129 .a_align = 16,
130 .a_size = 16,
131
132 .b_align = 16,
133 .b_size = 32,
134
135 .u128_align = 16,
136 .u128_size = 16,
137 .u129_align = 16,
138 .u129_size = 32,
139 },
140
141 else => return error.SkipZigTest,
142 };
143 const min_struct_align = if (builtin.zig_backend == .stage2_c) 16 else 0;
144 comptime {
145 assert(@alignOf(A) == @max(expected.a_align, min_struct_align));
146 assert(@sizeOf(A) == expected.a_size);
147
148 assert(@alignOf(B) == @max(expected.b_align, min_struct_align));
149 assert(@sizeOf(B) == expected.b_size);
150
151 assert(@alignOf(u128) == expected.u128_align);
152 assert(@sizeOf(u128) == expected.u128_size);
153
154 assert(@alignOf(u129) == expected.u129_align);
155 assert(@sizeOf(u129) == expected.u129_size);
156 }
157}
158
159test "implicitly decreasing slice alignment" {
160 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
161 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
162
163 const a: u32 align(4) = 3;
164 const b: u32 align(8) = 4;
165 try expect(addUnalignedSlice(@as(*const [1]u32, &a)[0..], @as(*const [1]u32, &b)[0..]) == 7);
166}
167fn addUnalignedSlice(a: []align(1) const u32, b: []align(1) const u32) u32 {
168 return a[0] + b[0];
169}
170
171test "specifying alignment allows pointer cast" {
172 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
173 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
174
175 try testBytesAlign(0x33);
176}
177fn testBytesAlign(b: u8) !void {
178 var bytes align(4) = [_]u8{ b, b, b, b };
179 const ptr = @as(*u32, @ptrCast(&bytes[0]));
180 try expect(ptr.* == 0x33333333);
181}
182
183test "@alignCast slices" {
184 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
185 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
186 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
187
188 var array align(4) = [_]u32{ 1, 1 };
189 const slice = array[0..];
190 sliceExpectsOnly1(slice);
191 try expect(slice[0] == 2);
192}
193fn sliceExpectsOnly1(slice: []align(1) u32) void {
194 sliceExpects4(@alignCast(slice));
195}
196fn sliceExpects4(slice: []align(4) u32) void {
197 slice[0] += 1;
198}
199
200test "return error union with 128-bit integer" {
201 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
202 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
203 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
204 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
205
206 try expect(3 == try give());
207}
208fn give() anyerror!u128 {
209 return 3;
210}
211
212test "page aligned array on stack" {
213 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
214 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
215 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
216 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
217
218 // Large alignment value to make it hard to accidentally pass.
219 var array align(0x1000) = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
220 var number1: u8 align(16) = 42;
221 var number2: u8 align(16) = 43;
222
223 try expect(@intFromPtr(&array[0]) & 0xFFF == 0);
224 try expect(array[3] == 4);
225
226 try expect(@as(u4, @truncate(@intFromPtr(&number1))) == 0);
227 try expect(@as(u4, @truncate(@intFromPtr(&number2))) == 0);
228 try expect(number1 == 42);
229 try expect(number2 == 43);
230}
231
232test "function alignment" {
233 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
234 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
235 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
236
237 // function alignment is a compile error on wasm
238 if (native_arch.isWasm()) return error.SkipZigTest;
239
240 const S = struct {
241 fn alignExpr() align(@sizeOf(usize) * 2) i32 {
242 return 1234;
243 }
244 fn align1() align(1) void {}
245 fn align4() align(4) void {}
246 };
247
248 try expect(S.alignExpr() == 1234);
249 try expect(@TypeOf(S.alignExpr) == fn () i32);
250 try expect(@TypeOf(&S.alignExpr) == *align(@sizeOf(usize) * 2) const fn () i32);
251
252 S.align1();
253 try expect(@TypeOf(S.align1) == fn () void);
254 try expect(@TypeOf(&S.align1) == *align(1) const fn () void);
255
256 S.align4();
257 try expect(@TypeOf(S.align4) == fn () void);
258 try expect(@TypeOf(&S.align4) == *align(4) const fn () void);
259}
260
261test "implicitly decreasing fn alignment" {
262 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
263 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
264 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
265
266 // function alignment is a compile error on wasm
267 if (native_arch.isWasm()) return error.SkipZigTest;
268
269 try testImplicitlyDecreaseFnAlign(alignedSmall, 1234);
270 try testImplicitlyDecreaseFnAlign(alignedBig, 5678);
271}
272
273fn testImplicitlyDecreaseFnAlign(ptr: *align(1) const fn () i32, answer: i32) !void {
274 try expect(ptr() == answer);
275}
276
277fn alignedSmall() align(8) i32 {
278 return 1234;
279}
280fn alignedBig() align(16) i32 {
281 return 5678;
282}
283
284test "@alignCast functions" {
285 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
286 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
287 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
288
289 // function alignment is a compile error on wasm
290 if (native_arch.isWasm()) return error.SkipZigTest;
291 if (native_arch.isThumb()) return error.SkipZigTest;
292
293 try expect(fnExpectsOnly1(simple4) == 0x19);
294}
295fn fnExpectsOnly1(ptr: *align(1) const fn () i32) i32 {
296 return fnExpects4(@alignCast(ptr));
297}
298fn fnExpects4(ptr: *align(4) const fn () i32) i32 {
299 return ptr();
300}
301fn simple4() align(4) i32 {
302 return 0x19;
303}
304
305test "runtime-known array index has best alignment possible" {
306 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
307 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
308
309 // take full advantage of over-alignment
310 var array align(4) = [_]u8{ 1, 2, 3, 4 };
311 comptime assert(@TypeOf(&array[0]) == *align(4) u8);
312 comptime assert(@TypeOf(&array[1]) == *u8);
313 comptime assert(@TypeOf(&array[2]) == *align(2) u8);
314 comptime assert(@TypeOf(&array[3]) == *u8);
315
316 // because align is too small but we still figure out to use 2
317 var bigger align(2) = [_]u64{ 1, 2, 3, 4 };
318 comptime assert(@TypeOf(&bigger[0]) == *align(2) u64);
319 comptime assert(@TypeOf(&bigger[1]) == *align(2) u64);
320 comptime assert(@TypeOf(&bigger[2]) == *align(2) u64);
321 comptime assert(@TypeOf(&bigger[3]) == *align(2) u64);
322
323 // because pointer is align 2 and u32 align % 2 == 0 we can assume align 2
324 var smaller align(2) = [_]u32{ 1, 2, 3, 4 };
325 var runtime_zero: usize = 0;
326 _ = &runtime_zero;
327 comptime assert(@TypeOf(smaller[runtime_zero..]) == []align(2) u32);
328 comptime assert(@TypeOf(smaller[runtime_zero..].ptr) == [*]align(2) u32);
329 try testIndex(smaller[runtime_zero..].ptr, 0, *align(2) u32);
330 try testIndex(smaller[runtime_zero..].ptr, 1, *align(2) u32);
331 try testIndex(smaller[runtime_zero..].ptr, 2, *align(2) u32);
332 try testIndex(smaller[runtime_zero..].ptr, 3, *align(2) u32);
333
334 // has to use ABI alignment because index known at runtime only
335 try testIndex2(&array, 0, *u8);
336 try testIndex2(&array, 1, *u8);
337 try testIndex2(&array, 2, *u8);
338 try testIndex2(&array, 3, *u8);
339}
340fn testIndex(smaller: [*]align(2) u32, index: usize, comptime T: type) !void {
341 comptime assert(@TypeOf(&smaller[index]) == T);
342}
343fn testIndex2(ptr: [*]align(4) u8, index: usize, comptime T: type) !void {
344 comptime assert(@TypeOf(&ptr[index]) == T);
345}
346
347test "alignment of function with c calling convention" {
348 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
349
350 const a = @alignOf(@TypeOf(nothing));
351
352 var runtime_nothing = ¬hing;
353 _ = &runtime_nothing;
354 const casted1: *align(a) const u8 = @ptrCast(runtime_nothing);
355 const casted2: *const fn () callconv(.c) void = @ptrCast(casted1);
356 casted2();
357}
358
359fn nothing() callconv(.c) void {}
360
361const DefaultAligned = struct {
362 nevermind: u32,
363 badguy: i128,
364};
365
366test "read 128-bit field from default aligned struct in stack memory" {
367 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
368 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
369 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
370 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
371
372 var default_aligned = DefaultAligned{
373 .nevermind = 1,
374 .badguy = 12,
375 };
376 _ = &default_aligned;
377 try expect(12 == default_aligned.badguy);
378}
379
380var default_aligned_global = DefaultAligned{
381 .nevermind = 1,
382 .badguy = 12,
383};
384
385test "read 128-bit field from default aligned struct in global memory" {
386 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
387 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
388 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
389 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
390
391 try expect(12 == default_aligned_global.badguy);
392}
393
394test "struct field explicit alignment" {
395 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
396 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
397 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // flaky
398 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
399
400 const S = struct {
401 const Node = struct {
402 next: *Node,
403 massive_byte: u8 align(64),
404 };
405 };
406
407 var node: S.Node = undefined;
408 node.massive_byte = 100;
409 try expect(node.massive_byte == 100);
410 comptime assert(@TypeOf(&node.massive_byte) == *align(64) u8);
411 try expect(@intFromPtr(&node.massive_byte) % 64 == 0);
412}
413
414test "align(N) on functions" {
415 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
416 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
417 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
418
419 if (builtin.zig_backend == .stage2_c) {
420 // https://github.com/ziglang/zig/issues/16845
421 return error.SkipZigTest;
422 }
423
424 if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) {
425 // This is not supported on MSVC.
426 return error.SkipZigTest;
427 }
428
429 // function alignment is a compile error on wasm
430 if (native_arch.isWasm()) return error.SkipZigTest;
431 if (native_arch.isThumb()) return error.SkipZigTest;
432
433 try expect((@intFromPtr(&overaligned_fn) & (0x1000 - 1)) == 0);
434}
435fn overaligned_fn() align(0x1000) i32 {
436 return 42;
437}
438
439test "comptime alloc alignment" {
440 // TODO: it's impossible to test this in Zig today, since comptime vars do not have runtime addresses.
441 if (true) return error.SkipZigTest;
442 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
443 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
444 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // flaky
445
446 comptime var bytes1 = [_]u8{0};
447 _ = &bytes1;
448
449 comptime var bytes2 align(256) = [_]u8{0};
450 const bytes2_addr = @intFromPtr(&bytes2);
451 try expect(bytes2_addr & 0xff == 0);
452}
453
454test "@alignCast null" {
455 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
456 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
457
458 var ptr: ?*anyopaque = null;
459 _ = &ptr;
460 const aligned: ?*anyopaque = @alignCast(ptr);
461 try expect(aligned == null);
462}
463
464test "alignment of slice element" {
465 const a: []align(1024) const u8 = undefined;
466 try expect(@TypeOf(&a[0]) == *align(1024) const u8);
467}
468
469test "sub-aligned pointer field access" {
470 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
471 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
472 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
473
474 // Originally reported at https://github.com/ziglang/zig/issues/14904
475
476 const Header = extern struct {
477 tag: u32,
478 bytes_len: u32,
479 };
480 var buf: [9]u8 align(4) = .{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };
481 const ptr: *align(1) Header = @ptrCast(buf[1..][0..8]);
482 const x = ptr.bytes_len;
483 switch (builtin.cpu.arch.endian()) {
484 .big => try expect(x == 0x06070809),
485 .little => try expect(x == 0x09080706),
486 }
487}
488
489test "alignment of zero-bit types is respected" {
490 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
491 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;
492 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
493 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
494 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
495
496 const S = struct { arr: [0]usize = .{} };
497
498 comptime assert(@alignOf(void) == 1);
499 comptime assert(@alignOf(u0) == 1);
500 comptime assert(@alignOf([0]usize) == @alignOf(usize));
501 comptime assert(@alignOf(S) == @alignOf(usize));
502
503 var s: S = .{};
504 var v32: void align(32) = {};
505 var x32: u0 align(32) = 0;
506 var s32: S align(32) = .{};
507
508 var zero: usize = 0;
509 _ = &zero;
510
511 try expect(@intFromPtr(&s) % @alignOf(usize) == 0);
512 try expect(@intFromPtr(&s.arr) % @alignOf(usize) == 0);
513 try expect(@intFromPtr(s.arr[zero..zero].ptr) % @alignOf(usize) == 0);
514 try expect(@intFromPtr(&v32) % 32 == 0);
515 try expect(@intFromPtr(&x32) % 32 == 0);
516 try expect(@intFromPtr(&s32) % 32 == 0);
517 try expect(@intFromPtr(&s32.arr) % 32 == 0);
518 try expect(@intFromPtr(s32.arr[zero..zero].ptr) % 32 == 0);
519}
520
521test "zero-bit fields in extern struct pad fields appropriately" {
522 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
523 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
524 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
525 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
526 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
527
528 const S = extern struct {
529 x: u8,
530 a: [0]u16 = .{},
531 y: u8,
532 };
533
534 // `a` should give `S` alignment 2, and pad the `arr` field.
535 comptime assert(@alignOf(S) == 2);
536 comptime assert(@sizeOf(S) == 4);
537 comptime assert(@offsetOf(S, "x") == 0);
538 comptime assert(@offsetOf(S, "a") == 2);
539 comptime assert(@offsetOf(S, "y") == 2);
540
541 var s: S = .{ .x = 100, .y = 200 };
542
543 try expect(@intFromPtr(&s) % 2 == 0);
544 try expect(@intFromPtr(&s.y) - @intFromPtr(&s.x) == 2);
545 try expect(@intFromPtr(&s.y) == @intFromPtr(&s.a));
546 try expect(@as(*S, @fieldParentPtr("a", &s.a)) == &s);
547}
548
549test "function pointer @intFromPtr/@ptrFromInt roundtrip" {
550 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
551
552 // This only succeeds on Thumb if we handle the Thumb bit correctly; if not, the `@ptrFromInt`
553 // will incorrectly trip an alignment safety check.
554
555 const nothing_ptr: *const fn () callconv(.c) void = ¬hing;
556 const nothing_int: usize = @intFromPtr(nothing_ptr);
557 const nothing_ptr2: *const fn () callconv(.c) void = @ptrFromInt(nothing_int);
558
559 try std.testing.expectEqual(nothing_ptr, nothing_ptr2);
560}
561
562test "function pointer align mask" {
563 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
564
565 const int = if (builtin.cpu.arch.isArm() or builtin.cpu.arch.isMIPS()) 0x20202021 else 0x20202020;
566 const unaligned: *const fn () callconv(.c) void = @ptrFromInt(int);
567 const aligned: *align(16) const fn () callconv(.c) void = @alignCast(unaligned);
568 try expect(@intFromPtr(aligned) == int);
569}