mirror of
https://github.com/ziglang/zig.git
synced 2026-01-01 19:13:16 +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.
272 lines
7.4 KiB
Zig
272 lines
7.4 KiB
Zig
const builtin = @import("builtin");
|
|
const std = @import("std");
|
|
const expect = std.testing.expect;
|
|
const mem = std.mem;
|
|
const maxInt = std.math.maxInt;
|
|
const native_endian = builtin.target.cpu.arch.endian();
|
|
|
|
test "pointer reinterpret const float to int" {
|
|
// The hex representation is 0x3fe3333333333303.
|
|
const float: f64 = 5.99999999999994648725e-01;
|
|
const float_ptr = &float;
|
|
const int_ptr = @ptrCast(*const i32, float_ptr);
|
|
const int_val = int_ptr.*;
|
|
if (native_endian == .Little)
|
|
try expect(int_val == 0x33333303)
|
|
else
|
|
try expect(int_val == 0x3fe33333);
|
|
}
|
|
|
|
test "@floatToInt" {
|
|
try testFloatToInts();
|
|
comptime try testFloatToInts();
|
|
}
|
|
|
|
fn testFloatToInts() !void {
|
|
try expectFloatToInt(f16, 255.1, u8, 255);
|
|
try expectFloatToInt(f16, 127.2, i8, 127);
|
|
try expectFloatToInt(f16, -128.2, i8, -128);
|
|
}
|
|
|
|
fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) !void {
|
|
try expect(@floatToInt(I, f) == i);
|
|
}
|
|
|
|
test "implicit cast from [*]T to ?*anyopaque" {
|
|
var a = [_]u8{ 3, 2, 1 };
|
|
var runtime_zero: usize = 0;
|
|
incrementVoidPtrArray(a[runtime_zero..].ptr, 3);
|
|
try expect(std.mem.eql(u8, &a, &[_]u8{ 4, 3, 2 }));
|
|
}
|
|
|
|
fn incrementVoidPtrArray(array: ?*anyopaque, len: usize) void {
|
|
var n: usize = 0;
|
|
while (n < len) : (n += 1) {
|
|
@ptrCast([*]u8, array.?)[n] += 1;
|
|
}
|
|
}
|
|
|
|
test "compile time int to ptr of function" {
|
|
if (builtin.zig_backend == .stage1) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) return error.SkipZigTest; // TODO
|
|
|
|
try foobar(FUNCTION_CONSTANT);
|
|
}
|
|
|
|
pub const FUNCTION_CONSTANT = @intToPtr(PFN_void, maxInt(usize));
|
|
pub const PFN_void = *const fn (*anyopaque) callconv(.C) void;
|
|
|
|
fn foobar(func: PFN_void) !void {
|
|
try std.testing.expect(@ptrToInt(func) == maxInt(usize));
|
|
}
|
|
|
|
test "implicit ptr to *anyopaque" {
|
|
var a: u32 = 1;
|
|
var ptr: *align(@alignOf(u32)) anyopaque = &a;
|
|
var b: *u32 = @ptrCast(*u32, ptr);
|
|
try expect(b.* == 1);
|
|
var ptr2: ?*align(@alignOf(u32)) anyopaque = &a;
|
|
var c: *u32 = @ptrCast(*u32, ptr2.?);
|
|
try expect(c.* == 1);
|
|
}
|
|
|
|
const A = struct {
|
|
a: i32,
|
|
};
|
|
test "return null from fn() anyerror!?&T" {
|
|
const a = returnNullFromOptionalTypeErrorRef();
|
|
const b = returnNullLitFromOptionalTypeErrorRef();
|
|
try expect((try a) == null and (try b) == null);
|
|
}
|
|
fn returnNullFromOptionalTypeErrorRef() anyerror!?*A {
|
|
const a: ?*A = null;
|
|
return a;
|
|
}
|
|
fn returnNullLitFromOptionalTypeErrorRef() anyerror!?*A {
|
|
return null;
|
|
}
|
|
|
|
test "peer type resolution: [0]u8 and []const u8" {
|
|
try expect(peerTypeEmptyArrayAndSlice(true, "hi").len == 0);
|
|
try expect(peerTypeEmptyArrayAndSlice(false, "hi").len == 1);
|
|
comptime {
|
|
try expect(peerTypeEmptyArrayAndSlice(true, "hi").len == 0);
|
|
try expect(peerTypeEmptyArrayAndSlice(false, "hi").len == 1);
|
|
}
|
|
}
|
|
fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 {
|
|
if (a) {
|
|
return &[_]u8{};
|
|
}
|
|
|
|
return slice[0..1];
|
|
}
|
|
|
|
test "implicitly cast from [N]T to ?[]const T" {
|
|
try expect(mem.eql(u8, castToOptionalSlice().?, "hi"));
|
|
comptime try expect(mem.eql(u8, castToOptionalSlice().?, "hi"));
|
|
}
|
|
|
|
fn castToOptionalSlice() ?[]const u8 {
|
|
return "hi";
|
|
}
|
|
|
|
test "cast u128 to f128 and back" {
|
|
comptime try testCast128();
|
|
try testCast128();
|
|
}
|
|
|
|
fn testCast128() !void {
|
|
try expect(cast128Int(cast128Float(0x7fff0000000000000000000000000000)) == 0x7fff0000000000000000000000000000);
|
|
}
|
|
|
|
fn cast128Int(x: f128) u128 {
|
|
return @bitCast(u128, x);
|
|
}
|
|
|
|
fn cast128Float(x: u128) f128 {
|
|
return @bitCast(f128, x);
|
|
}
|
|
|
|
test "implicit cast from *[N]T to ?[*]T" {
|
|
var x: ?[*]u16 = null;
|
|
var y: [4]u16 = [4]u16{ 0, 1, 2, 3 };
|
|
|
|
x = &y;
|
|
try expect(std.mem.eql(u16, x.?[0..4], y[0..4]));
|
|
x.?[0] = 8;
|
|
y[3] = 6;
|
|
try expect(std.mem.eql(u16, x.?[0..4], y[0..4]));
|
|
}
|
|
|
|
test "implicit cast from *T to ?*anyopaque" {
|
|
var a: u8 = 1;
|
|
incrementVoidPtrValue(&a);
|
|
try std.testing.expect(a == 2);
|
|
}
|
|
|
|
fn incrementVoidPtrValue(value: ?*anyopaque) void {
|
|
@ptrCast(*u8, value.?).* += 1;
|
|
}
|
|
|
|
test "implicit cast *[0]T to E![]const u8" {
|
|
var x = @as(anyerror![]const u8, &[0]u8{});
|
|
try expect((x catch unreachable).len == 0);
|
|
}
|
|
|
|
var global_array: [4]u8 = undefined;
|
|
test "cast from array reference to fn" {
|
|
const f = @ptrCast(*const fn () callconv(.C) void, &global_array);
|
|
try expect(@ptrToInt(f) == @ptrToInt(&global_array));
|
|
}
|
|
|
|
test "*const [N]null u8 to ?[]const u8" {
|
|
const S = struct {
|
|
fn doTheTest() !void {
|
|
var a = "Hello";
|
|
var b: ?[]const u8 = a;
|
|
try expect(mem.eql(u8, b.?, "Hello"));
|
|
}
|
|
};
|
|
try S.doTheTest();
|
|
comptime try S.doTheTest();
|
|
}
|
|
|
|
test "cast between [*c]T and ?[*:0]T on fn parameter" {
|
|
const S = struct {
|
|
const Handler = ?fn ([*c]const u8) callconv(.C) void;
|
|
fn addCallback(handler: Handler) void {
|
|
_ = handler;
|
|
}
|
|
|
|
fn myCallback(cstr: ?[*:0]const u8) callconv(.C) void {
|
|
_ = cstr;
|
|
}
|
|
|
|
fn doTheTest() void {
|
|
addCallback(myCallback);
|
|
}
|
|
};
|
|
S.doTheTest();
|
|
}
|
|
|
|
var global_struct: struct { f0: usize } = undefined;
|
|
test "assignment to optional pointer result loc" {
|
|
var foo: struct { ptr: ?*anyopaque } = .{ .ptr = &global_struct };
|
|
try expect(foo.ptr.? == @ptrCast(*anyopaque, &global_struct));
|
|
}
|
|
|
|
test "cast between *[N]void and []void" {
|
|
var a: [4]void = undefined;
|
|
var b: []void = &a;
|
|
try expect(b.len == 4);
|
|
}
|
|
|
|
test "peer resolve arrays of different size to const slice" {
|
|
try expect(mem.eql(u8, boolToStr(true), "true"));
|
|
try expect(mem.eql(u8, boolToStr(false), "false"));
|
|
comptime try expect(mem.eql(u8, boolToStr(true), "true"));
|
|
comptime try expect(mem.eql(u8, boolToStr(false), "false"));
|
|
}
|
|
fn boolToStr(b: bool) []const u8 {
|
|
return if (b) "true" else "false";
|
|
}
|
|
|
|
test "cast f16 to wider types" {
|
|
const S = struct {
|
|
fn doTheTest() !void {
|
|
var x: f16 = 1234.0;
|
|
try expect(@as(f32, 1234.0) == x);
|
|
try expect(@as(f64, 1234.0) == x);
|
|
try expect(@as(f128, 1234.0) == x);
|
|
}
|
|
};
|
|
try S.doTheTest();
|
|
comptime try S.doTheTest();
|
|
}
|
|
|
|
test "cast f128 to narrower types" {
|
|
const S = struct {
|
|
fn doTheTest() !void {
|
|
var x: f128 = 1234.0;
|
|
try expect(@as(f16, 1234.0) == @floatCast(f16, x));
|
|
try expect(@as(f32, 1234.0) == @floatCast(f32, x));
|
|
try expect(@as(f64, 1234.0) == @floatCast(f64, x));
|
|
}
|
|
};
|
|
try S.doTheTest();
|
|
comptime try S.doTheTest();
|
|
}
|
|
|
|
test "peer type resolution: unreachable, null, slice" {
|
|
const S = struct {
|
|
fn doTheTest(num: usize, word: []const u8) !void {
|
|
const result = switch (num) {
|
|
0 => null,
|
|
1 => word,
|
|
else => unreachable,
|
|
};
|
|
try expect(mem.eql(u8, result.?, "hi"));
|
|
}
|
|
};
|
|
try S.doTheTest(1, "hi");
|
|
}
|
|
|
|
test "cast i8 fn call peers to i32 result" {
|
|
const S = struct {
|
|
fn doTheTest() !void {
|
|
var cond = true;
|
|
const value: i32 = if (cond) smallBoi() else bigBoi();
|
|
try expect(value == 123);
|
|
}
|
|
fn smallBoi() i8 {
|
|
return 123;
|
|
}
|
|
fn bigBoi() i16 {
|
|
return 1234;
|
|
}
|
|
};
|
|
try S.doTheTest();
|
|
comptime try S.doTheTest();
|
|
}
|