mirror of
https://github.com/ziglang/zig.git
synced 2025-12-31 18:43:18 +00:00
This commit updates stage2 to enforce the property that the syntax `fn()void` is a function *body* not a *pointer*. To get a pointer, the syntax `*const fn()void` is required. ZIR puts function alignment into the func instruction rather than the decl because this way it makes it into function types. LLVM backend respects function alignments. Struct and Union have methods `fieldSrcLoc` to help look up source locations of their fields. These trigger full loading, tokenization, and parsing of source files, so should only be called once it is confirmed that an error message needs to be printed. There are some nice new error hints for explaining why a type is required to be comptime, particularly for structs that contain function body types. `Type.requiresComptime` is now moved into Sema because it can fail and might need to trigger field type resolution. Comptime pointer loading takes into account types that do not have a well-defined memory layout and does not try to compute a byte offset for them. `fn()void` syntax no longer secretly makes a pointer. You get a function body type, which requires comptime. However a pointer to a function body can be runtime known (obviously). Compile errors that report "expected pointer, found ..." are factored out into convenience functions `checkPtrOperand` and `checkPtrType` and have a note about function pointers. Implemented `Value.hash` for functions, enum literals, and undefined values. stage1 is not updated to this (yet?), so some workarounds and disabled tests are needed to keep everything working. Should we update stage1 to these new type semantics? Yes probably because I don't want to add too much conditional compilation logic in the std lib for the different backends.
204 lines
5.5 KiB
Zig
204 lines
5.5 KiB
Zig
const std = @import("std");
|
|
const expect = std.testing.expect;
|
|
const builtin = @import("builtin");
|
|
const native_arch = builtin.target.cpu.arch;
|
|
|
|
var foo: u8 align(4) = 100;
|
|
|
|
test "global variable alignment" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
|
|
comptime try expect(@typeInfo(@TypeOf(&foo)).Pointer.alignment == 4);
|
|
comptime try expect(@TypeOf(&foo) == *align(4) u8);
|
|
{
|
|
const slice = @as(*[1]u8, &foo)[0..];
|
|
comptime try expect(@TypeOf(slice) == *align(4) [1]u8);
|
|
}
|
|
{
|
|
var runtime_zero: usize = 0;
|
|
const slice = @as(*[1]u8, &foo)[runtime_zero..];
|
|
comptime try expect(@TypeOf(slice) == []align(4) u8);
|
|
}
|
|
}
|
|
|
|
test "default alignment allows unspecified in type syntax" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
|
|
try expect(*u32 == *align(@alignOf(u32)) u32);
|
|
}
|
|
|
|
test "implicitly decreasing pointer alignment" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
|
|
const a: u32 align(4) = 3;
|
|
const b: u32 align(8) = 4;
|
|
try expect(addUnaligned(&a, &b) == 7);
|
|
}
|
|
|
|
fn addUnaligned(a: *align(1) const u32, b: *align(1) const u32) u32 {
|
|
return a.* + b.*;
|
|
}
|
|
|
|
test "@alignCast pointers" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
|
|
var x: u32 align(4) = 1;
|
|
expectsOnly1(&x);
|
|
try expect(x == 2);
|
|
}
|
|
fn expectsOnly1(x: *align(1) u32) void {
|
|
expects4(@alignCast(4, x));
|
|
}
|
|
fn expects4(x: *align(4) u32) void {
|
|
x.* += 1;
|
|
}
|
|
|
|
test "alignment of structs" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
|
|
try expect(@alignOf(struct {
|
|
a: i32,
|
|
b: *i32,
|
|
}) == @alignOf(usize));
|
|
}
|
|
|
|
test "alignment of >= 128-bit integer type" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
|
|
try expect(@alignOf(u128) == 16);
|
|
try expect(@alignOf(u129) == 16);
|
|
}
|
|
|
|
test "alignment of struct with 128-bit field" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
|
|
try expect(@alignOf(struct {
|
|
x: u128,
|
|
}) == 16);
|
|
|
|
comptime {
|
|
try expect(@alignOf(struct {
|
|
x: u128,
|
|
}) == 16);
|
|
}
|
|
}
|
|
|
|
test "size of extern struct with 128-bit field" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
|
|
try expect(@sizeOf(extern struct {
|
|
x: u128,
|
|
y: u8,
|
|
}) == 32);
|
|
|
|
comptime {
|
|
try expect(@sizeOf(extern struct {
|
|
x: u128,
|
|
y: u8,
|
|
}) == 32);
|
|
}
|
|
}
|
|
|
|
test "@ptrCast preserves alignment of bigger source" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
|
|
var x: u32 align(16) = 1234;
|
|
const ptr = @ptrCast(*u8, &x);
|
|
try expect(@TypeOf(ptr) == *align(16) u8);
|
|
}
|
|
|
|
test "alignstack" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
|
|
try expect(fnWithAlignedStack() == 1234);
|
|
}
|
|
|
|
fn fnWithAlignedStack() i32 {
|
|
@setAlignStack(256);
|
|
return 1234;
|
|
}
|
|
|
|
test "implicitly decreasing slice alignment" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
|
|
const a: u32 align(4) = 3;
|
|
const b: u32 align(8) = 4;
|
|
try expect(addUnalignedSlice(@as(*const [1]u32, &a)[0..], @as(*const [1]u32, &b)[0..]) == 7);
|
|
}
|
|
fn addUnalignedSlice(a: []align(1) const u32, b: []align(1) const u32) u32 {
|
|
return a[0] + b[0];
|
|
}
|
|
|
|
test "specifying alignment allows pointer cast" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
|
|
try testBytesAlign(0x33);
|
|
}
|
|
fn testBytesAlign(b: u8) !void {
|
|
var bytes align(4) = [_]u8{ b, b, b, b };
|
|
const ptr = @ptrCast(*u32, &bytes[0]);
|
|
try expect(ptr.* == 0x33333333);
|
|
}
|
|
|
|
test "@alignCast slices" {
|
|
if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
|
|
var array align(4) = [_]u32{ 1, 1 };
|
|
const slice = array[0..];
|
|
sliceExpectsOnly1(slice);
|
|
try expect(slice[0] == 2);
|
|
}
|
|
fn sliceExpectsOnly1(slice: []align(1) u32) void {
|
|
sliceExpects4(@alignCast(4, slice));
|
|
}
|
|
fn sliceExpects4(slice: []align(4) u32) void {
|
|
slice[0] += 1;
|
|
}
|
|
|
|
test "return error union with 128-bit integer" {
|
|
if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
|
|
try expect(3 == try give());
|
|
}
|
|
fn give() anyerror!u128 {
|
|
return 3;
|
|
}
|
|
|
|
test "page aligned array on stack" {
|
|
if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm or
|
|
builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
|
|
|
|
// Large alignment value to make it hard to accidentally pass.
|
|
var array align(0x1000) = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
|
|
var number1: u8 align(16) = 42;
|
|
var number2: u8 align(16) = 43;
|
|
|
|
try expect(@ptrToInt(&array[0]) & 0xFFF == 0);
|
|
try expect(array[3] == 4);
|
|
|
|
try expect(@truncate(u4, @ptrToInt(&number1)) == 0);
|
|
try expect(@truncate(u4, @ptrToInt(&number2)) == 0);
|
|
try expect(number1 == 42);
|
|
try expect(number2 == 43);
|
|
}
|
|
|
|
fn derp() align(@sizeOf(usize) * 2) i32 {
|
|
return 1234;
|
|
}
|
|
fn noop1() align(1) void {}
|
|
fn noop4() align(4) void {}
|
|
|
|
test "function alignment" {
|
|
// function alignment is a compile error on wasm32/wasm64
|
|
if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest;
|
|
|
|
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
|
|
try expect(derp() == 1234);
|
|
try expect(@TypeOf(noop1) == fn () align(1) void);
|
|
try expect(@TypeOf(noop4) == fn () align(4) void);
|
|
noop1();
|
|
noop4();
|
|
}
|