const builtin = @import("builtin"); const std = @import("std"); const testing = std.testing; const assert = std.debug.assert; 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 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = struct { fn entry() bool { const x: i32 = 1234; return foo(x); } fn foo(x: ?i32) bool { return x.? == 1234; } }; try expect(S.entry()); comptime assert(S.entry()); } pub const EmptyStruct = struct {}; test "optional pointer to size zero struct" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; var e = EmptyStruct{}; const o: ?*EmptyStruct = &e; try expect(o != null); } test "equality compare optional pointers" { if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; try testNullPtrsEql(); try comptime 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 zero-bit type" { if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; const S = struct { fn doTheTest(comptime ZeroBit: type, comptime zero_bit: ZeroBit) !void { const WithRuntime = struct { zero_bit: ZeroBit, runtime: u1, }; var with_runtime: WithRuntime = undefined; with_runtime = .{ .zero_bit = zero_bit, .runtime = 0 }; const Opt = struct { opt: ?ZeroBit }; var opt: Opt = .{ .opt = null }; try expect(opt.opt == null); try expect(opt.opt != zero_bit); try expect(opt.opt != with_runtime.zero_bit); opt.opt = zero_bit; try expect(opt.opt != null); try expect(opt.opt == zero_bit); try expect(opt.opt == with_runtime.zero_bit); opt = .{ .opt = zero_bit }; try expect(opt.opt != null); try expect(opt.opt == zero_bit); try expect(opt.opt == with_runtime.zero_bit); opt.opt = with_runtime.zero_bit; try expect(opt.opt != null); try expect(opt.opt == zero_bit); try expect(opt.opt == with_runtime.zero_bit); opt = .{ .opt = with_runtime.zero_bit }; try expect(opt.opt != null); try expect(opt.opt == zero_bit); try expect(opt.opt == with_runtime.zero_bit); var two: ?struct { ZeroBit, ZeroBit } = undefined; two = .{ with_runtime.zero_bit, with_runtime.zero_bit }; try expect(two != null); try expect(two.?[0] == zero_bit); try expect(two.?[0] == with_runtime.zero_bit); try expect(two.?[1] == zero_bit); try expect(two.?[1] == with_runtime.zero_bit); } }; try S.doTheTest(void, {}); try comptime S.doTheTest(void, {}); try S.doTheTest(enum { only }, .only); try comptime S.doTheTest(enum { only }, .only); } 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_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; 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_sparc64) return error.SkipZigTest; // TODO const S2 = struct { y: u8, }; const S1 = struct { x: ?S2, }; var s = S1{ .x = S2{ .y = 127 }, }; _ = &s; try expect(s.x.?.y == 127); } test "equality compare optionals and non-optionals" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn doTheTest() !void { var five: isize = 5; var ten: isize = 10; var opt_null: ?isize = null; var opt_ten: ?isize = 10; _ = .{ &five, &ten, &opt_null, &opt_ten }; try expect(opt_null != five); try expect(opt_null != ten); try expect(opt_ten != five); try expect(opt_ten == ten); var opt_int: ?isize = null; try expect(opt_int != five); try expect(opt_int != ten); try expect(opt_int == opt_null); try expect(opt_int != opt_ten); opt_int = 10; try expect(opt_int != five); try expect(opt_int == ten); try expect(opt_int != opt_null); try expect(opt_int == opt_ten); opt_int = five; try expect(opt_int == five); try expect(opt_int != ten); try expect(opt_int != opt_null); try expect(opt_int != opt_ten); // 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); }; } }; try S.doTheTest(); try comptime S.doTheTest(); } test "compare optionals with modified payloads" { if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; var lhs: ?bool = false; const lhs_payload = &lhs.?; var rhs: ?bool = true; const rhs_payload = &rhs.?; try expect(lhs != rhs and !(lhs == rhs)); lhs = null; lhs_payload.* = false; rhs = false; try expect(lhs != rhs and !(lhs == rhs)); lhs = true; rhs = null; rhs_payload.* = true; try expect(lhs != rhs and !(lhs == rhs)); lhs = null; lhs_payload.* = false; rhs = null; rhs_payload.* = true; try expect(lhs == rhs and !(lhs != rhs)); } test "unwrap function call with optional pointer return value" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) 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(); try comptime S.entry(); } test "nested orelse" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) 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(); try comptime S.entry(); } test "self-referential struct through a slice of optional" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; const S = struct { const Node = struct { children: []?Node, data: ?u8, fn new() Node { return Node{ .children = undefined, .data = null, }; } }; }; const 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_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; 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(); try comptime S.doTheTest(); } test "0-bit child type coerced to optional return ptr result location" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = struct { fn doTheTest() !void { var y = Foo{}; const 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(); try comptime S.doTheTest(); } test "0-bit child type coerced to optional" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; 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(); try comptime S.doTheTest(); } test "array of optional unaligned types" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; 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_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; 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_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; 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 == .stage2_sparc64) return error.SkipZigTest; // TODO NoReturn.a = 64; if (NoReturn.loop()) |_| { @compileError("bad"); } else { try expect(true); } } test "optional of noreturn used with orelse" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO NoReturn.a = 64; const val = NoReturn.testOrelse(); try expect(val == 123); } test "mutable optional of noreturn" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO var a: ?noreturn = null; if (a) |*ptr| { _ = ptr; @compileError("bad"); } else { // this is what we expect to hit return; } @compileError("bad"); } 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_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { const I = extern struct { x: i128 }; fn foo() ?I { var i: I = .{ .x = 1234 }; _ = &i; return i; } }; try expect(S.foo().?.x == 1234); } test "Optional slice size is optimized" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; try expect(@sizeOf(?[]u8) == @sizeOf([]u8)); var a: ?[]const u8 = null; try expect(a == null); a = "hello"; try expectEqualStrings(a.?, "hello"); } test "Optional slice passed to function" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; const S = struct { fn foo(a: ?[]const u8) !void { try std.testing.expectEqualStrings(a.?, "foo"); } fn bar(a: ?[]allowzero const u8) !void { try std.testing.expectEqualStrings(@ptrCast(a.?), "bar"); } }; try S.foo("foo"); try S.bar("bar"); } test "peer type resolution in nested if expressions" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; const Thing = struct { n: i32 }; var a = false; var b = false; _ = .{ &a, &b }; const result1 = if (a) Thing{ .n = 1 } else null; try expect(result1 == null); try expect(@TypeOf(result1) == ?Thing); const result2 = if (a) Thing{ .n = 0 } else if (b) Thing{ .n = 1 } else null; try expect(result2 == null); try expect(@TypeOf(result2) == ?Thing); } test "cast slice to const slice nested in error union and optional" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { fn inner() !?[]u8 { return error.Foo; } fn outer() !?[]const u8 { return inner(); } }; try std.testing.expectError(error.Foo, S.outer()); } test "variable of optional of noreturn" { var null_opv: ?noreturn = null; _ = &null_opv; try std.testing.expectEqual(@as(?noreturn, null), null_opv); } test "copied optional doesn't alias source" { 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_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; var opt_x: ?[3]f32 = [_]f32{0.0} ** 3; const x = opt_x.?; opt_x.?[0] = 15.0; try expect(x[0] == 0.0); } test "result location initialization of optional with OPV payload" { 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_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; const S = struct { x: u0, }; const a: ?S = .{ .x = 0 }; comptime assert(a.?.x == 0); comptime { var b: ?S = .{ .x = 0 }; _ = &b; assert(b.?.x == 0); } var c: ?S = .{ .x = 0 }; _ = &c; try expectEqual(0, (c orelse return error.TestFailed).x); } test "global comptime only optional" { const S = struct { const @"null": ?*type = null; const @"void": ?*const type = &void; }; comptime { assert(S.null == null); assert(S.void.?.* == void); } }