mirror of
https://github.com/ziglang/zig.git
synced 2024-11-27 07:32:44 +00:00
436 lines
12 KiB
Zig
436 lines
12 KiB
Zig
const builtin = @import("builtin");
|
|
const std = @import("std");
|
|
const testing = std.testing;
|
|
const expect = testing.expect;
|
|
const expectEqual = testing.expectEqual;
|
|
const expectEqualStrings = std.testing.expectEqualStrings;
|
|
|
|
test "passing an optional integer as a parameter" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
|
|
const S = struct {
|
|
fn entry() bool {
|
|
var x: i32 = 1234;
|
|
return foo(x);
|
|
}
|
|
|
|
fn foo(x: ?i32) bool {
|
|
return x.? == 1234;
|
|
}
|
|
};
|
|
try expect(S.entry());
|
|
comptime try expect(S.entry());
|
|
}
|
|
|
|
pub const EmptyStruct = struct {};
|
|
|
|
test "optional pointer to size zero struct" {
|
|
var e = EmptyStruct{};
|
|
var o: ?*EmptyStruct = &e;
|
|
try expect(o != null);
|
|
}
|
|
|
|
test "equality compare optional pointers" {
|
|
try testNullPtrsEql();
|
|
comptime try testNullPtrsEql();
|
|
}
|
|
|
|
fn testNullPtrsEql() !void {
|
|
var number: i32 = 1234;
|
|
|
|
var x: ?*i32 = null;
|
|
var y: ?*i32 = null;
|
|
try expect(x == y);
|
|
y = &number;
|
|
try expect(x != y);
|
|
try expect(x != &number);
|
|
try expect(&number != x);
|
|
x = &number;
|
|
try expect(x == y);
|
|
try expect(x == &number);
|
|
try expect(&number == x);
|
|
}
|
|
|
|
test "optional with void type" {
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
|
|
const Foo = struct {
|
|
x: ?void,
|
|
};
|
|
var x = Foo{ .x = null };
|
|
try expect(x.x == null);
|
|
}
|
|
|
|
test "address of unwrap optional" {
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
|
|
|
const S = struct {
|
|
const Foo = struct {
|
|
a: i32,
|
|
};
|
|
|
|
var global: ?Foo = null;
|
|
|
|
pub fn getFoo() anyerror!*Foo {
|
|
return &global.?;
|
|
}
|
|
};
|
|
S.global = S.Foo{ .a = 1234 };
|
|
const foo = S.getFoo() catch unreachable;
|
|
try expect(foo.a == 1234);
|
|
}
|
|
|
|
test "nested optional field in struct" {
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
|
|
|
const S2 = struct {
|
|
y: u8,
|
|
};
|
|
const S1 = struct {
|
|
x: ?S2,
|
|
};
|
|
var s = S1{
|
|
.x = S2{ .y = 127 },
|
|
};
|
|
try expect(s.x.?.y == 127);
|
|
}
|
|
|
|
test "equality compare optional with non-optional" {
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
|
|
|
try test_cmp_optional_non_optional();
|
|
comptime try test_cmp_optional_non_optional();
|
|
}
|
|
|
|
fn test_cmp_optional_non_optional() !void {
|
|
var ten: i32 = 10;
|
|
var opt_ten: ?i32 = 10;
|
|
var five: i32 = 5;
|
|
var int_n: ?i32 = null;
|
|
|
|
try expect(int_n != ten);
|
|
try expect(opt_ten == ten);
|
|
try expect(opt_ten != five);
|
|
|
|
// test evaluation is always lexical
|
|
// ensure that the optional isn't always computed before the non-optional
|
|
var mutable_state: i32 = 0;
|
|
_ = blk1: {
|
|
mutable_state += 1;
|
|
break :blk1 @as(?f64, 10.0);
|
|
} != blk2: {
|
|
try expect(mutable_state == 1);
|
|
break :blk2 @as(f64, 5.0);
|
|
};
|
|
_ = blk1: {
|
|
mutable_state += 1;
|
|
break :blk1 @as(f64, 10.0);
|
|
} != blk2: {
|
|
try expect(mutable_state == 2);
|
|
break :blk2 @as(?f64, 5.0);
|
|
};
|
|
}
|
|
|
|
test "unwrap function call with optional pointer return value" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
|
|
const S = struct {
|
|
fn entry() !void {
|
|
try expect(foo().?.* == 1234);
|
|
try expect(bar() == null);
|
|
}
|
|
const global: i32 = 1234;
|
|
fn foo() ?*const i32 {
|
|
return &global;
|
|
}
|
|
fn bar() ?*i32 {
|
|
return null;
|
|
}
|
|
};
|
|
try S.entry();
|
|
comptime try S.entry();
|
|
}
|
|
|
|
test "nested orelse" {
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
|
|
const S = struct {
|
|
fn entry() !void {
|
|
try expect(func() == null);
|
|
}
|
|
fn maybe() ?Foo {
|
|
return null;
|
|
}
|
|
fn func() ?Foo {
|
|
const x = maybe() orelse
|
|
maybe() orelse
|
|
return null;
|
|
_ = x;
|
|
unreachable;
|
|
}
|
|
const Foo = struct {
|
|
field: i32,
|
|
};
|
|
};
|
|
try S.entry();
|
|
comptime try S.entry();
|
|
}
|
|
|
|
test "self-referential struct through a slice of optional" {
|
|
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
|
|
const S = struct {
|
|
const Node = struct {
|
|
children: []?Node,
|
|
data: ?u8,
|
|
|
|
fn new() Node {
|
|
return Node{
|
|
.children = undefined,
|
|
.data = null,
|
|
};
|
|
}
|
|
};
|
|
};
|
|
|
|
var n = S.Node.new();
|
|
try expect(n.data == null);
|
|
}
|
|
|
|
test "assigning to an unwrapped optional field in an inline loop" {
|
|
comptime var maybe_pos_arg: ?comptime_int = null;
|
|
inline for ("ab") |x| {
|
|
_ = x;
|
|
maybe_pos_arg = 0;
|
|
if (maybe_pos_arg.? != 0) {
|
|
@compileError("bad");
|
|
}
|
|
maybe_pos_arg.? = 10;
|
|
}
|
|
}
|
|
|
|
test "coerce an anon struct literal to optional struct" {
|
|
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
|
|
const S = struct {
|
|
const Struct = struct {
|
|
field: u32,
|
|
};
|
|
fn doTheTest() !void {
|
|
var maybe_dims: ?Struct = null;
|
|
maybe_dims = .{ .field = 1 };
|
|
try expect(maybe_dims.?.field == 1);
|
|
}
|
|
};
|
|
try S.doTheTest();
|
|
comptime try S.doTheTest();
|
|
}
|
|
|
|
test "0-bit child type coerced to optional return ptr result location" {
|
|
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
|
|
const S = struct {
|
|
fn doTheTest() !void {
|
|
var y = Foo{};
|
|
var z = y.thing();
|
|
try expect(z != null);
|
|
}
|
|
|
|
const Foo = struct {
|
|
pub const Bar = struct {
|
|
field: *Foo,
|
|
};
|
|
|
|
pub fn thing(self: *Foo) ?Bar {
|
|
return Bar{ .field = self };
|
|
}
|
|
};
|
|
};
|
|
try S.doTheTest();
|
|
comptime try S.doTheTest();
|
|
}
|
|
|
|
test "0-bit child type coerced to optional" {
|
|
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
|
|
|
const S = struct {
|
|
fn doTheTest() !void {
|
|
var it: Foo = .{
|
|
.list = undefined,
|
|
};
|
|
try expect(it.foo() != null);
|
|
}
|
|
|
|
const Empty = struct {};
|
|
const Foo = struct {
|
|
list: [10]Empty,
|
|
|
|
fn foo(self: *Foo) ?*Empty {
|
|
const data = &self.list[0];
|
|
return data;
|
|
}
|
|
};
|
|
};
|
|
try S.doTheTest();
|
|
comptime try S.doTheTest();
|
|
}
|
|
|
|
test "array of optional unaligned types" {
|
|
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
|
|
const Enum = enum { one, two, three };
|
|
|
|
const SomeUnion = union(enum) {
|
|
Num: Enum,
|
|
Other: u32,
|
|
};
|
|
|
|
const values = [_]?SomeUnion{
|
|
SomeUnion{ .Num = .one },
|
|
SomeUnion{ .Num = .two },
|
|
SomeUnion{ .Num = .three },
|
|
SomeUnion{ .Num = .one },
|
|
SomeUnion{ .Num = .two },
|
|
SomeUnion{ .Num = .three },
|
|
};
|
|
|
|
// The index must be a runtime value
|
|
var i: usize = 0;
|
|
try expect(Enum.one == values[i].?.Num);
|
|
i += 1;
|
|
try expect(Enum.two == values[i].?.Num);
|
|
i += 1;
|
|
try expect(Enum.three == values[i].?.Num);
|
|
i += 1;
|
|
try expect(Enum.one == values[i].?.Num);
|
|
i += 1;
|
|
try expect(Enum.two == values[i].?.Num);
|
|
i += 1;
|
|
try expect(Enum.three == values[i].?.Num);
|
|
}
|
|
|
|
test "optional pointer to zero bit optional payload" {
|
|
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
|
|
const B = struct {
|
|
fn foo(_: *@This()) void {}
|
|
};
|
|
const A = struct {
|
|
b: ?B = .{},
|
|
};
|
|
var a: A = .{};
|
|
var a_ptr = &a;
|
|
if (a_ptr.b) |*some| {
|
|
some.foo();
|
|
}
|
|
}
|
|
|
|
test "optional pointer to zero bit error union payload" {
|
|
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
|
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
|
|
|
|
const B = struct {
|
|
fn foo(_: *@This()) void {}
|
|
};
|
|
const A = struct {
|
|
b: anyerror!B = .{},
|
|
};
|
|
var a: A = .{};
|
|
var a_ptr = &a;
|
|
if (a_ptr.b) |*some| {
|
|
some.foo();
|
|
} else |_| {}
|
|
}
|
|
|
|
const NoReturn = struct {
|
|
var a: u32 = undefined;
|
|
fn someData() bool {
|
|
a -= 1;
|
|
return a == 0;
|
|
}
|
|
fn loop() ?noreturn {
|
|
while (true) {
|
|
if (someData()) return null;
|
|
}
|
|
}
|
|
fn testOrelse() u32 {
|
|
loop() orelse return 123;
|
|
@compileError("bad");
|
|
}
|
|
};
|
|
|
|
test "optional of noreturn used with if" {
|
|
if (builtin.zig_backend == .stage1) return error.SkipZigTest;
|
|
|
|
NoReturn.a = 64;
|
|
if (NoReturn.loop()) |_| {
|
|
@compileError("bad");
|
|
} else {
|
|
try expect(true);
|
|
}
|
|
}
|
|
|
|
test "optional of noreturn used with orelse" {
|
|
if (builtin.zig_backend == .stage1) return error.SkipZigTest;
|
|
|
|
NoReturn.a = 64;
|
|
const val = NoReturn.testOrelse();
|
|
try expect(val == 123);
|
|
}
|
|
|
|
test "orelse on C pointer" {
|
|
// TODO https://github.com/ziglang/zig/issues/6597
|
|
const foo: [*c]const u8 = "hey";
|
|
const d = foo orelse @compileError("bad");
|
|
try expectEqual([*c]const u8, @TypeOf(d));
|
|
}
|
|
|
|
test "alignment of wrapping an optional payload" {
|
|
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
|
|
const S = struct {
|
|
const I = extern struct { x: i128 };
|
|
|
|
fn foo() ?I {
|
|
var i: I = .{ .x = 1234 };
|
|
return i;
|
|
}
|
|
};
|
|
try expect(S.foo().?.x == 1234);
|
|
}
|
|
|
|
test "Optional slice size is optimized" {
|
|
if (builtin.zig_backend == .stage1) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
|
|
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
|
|
|
|
try expect(@sizeOf(?[]u8) == @sizeOf([]u8));
|
|
var a: ?[]const u8 = null;
|
|
try expect(a == null);
|
|
a = "hello";
|
|
try expectEqualStrings(a.?, "hello");
|
|
}
|