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 = &nothing;
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 = &nothing;
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}