1const builtin = @import("builtin");
2const std = @import("std");
3const testing = std.testing;
4const assert = std.debug.assert;
5const expect = testing.expect;
6const expectEqual = testing.expectEqual;
7const expectEqualStrings = std.testing.expectEqualStrings;
8
9test "passing an optional integer as a parameter" {
10 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
11 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
12
13 const S = struct {
14 fn entry() bool {
15 const x: i32 = 1234;
16 return foo(x);
17 }
18
19 fn foo(x: ?i32) bool {
20 return x.? == 1234;
21 }
22 };
23 try expect(S.entry());
24 comptime assert(S.entry());
25}
26
27pub const EmptyStruct = struct {};
28
29test "optional pointer to size zero struct" {
30 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
31 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
32
33 var e = EmptyStruct{};
34 const o: ?*EmptyStruct = &e;
35 try expect(o != null);
36}
37
38test "equality compare optional pointers" {
39 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
40
41 try testNullPtrsEql();
42 try comptime testNullPtrsEql();
43}
44
45fn testNullPtrsEql() !void {
46 var number: i32 = 1234;
47
48 var x: ?*i32 = null;
49 var y: ?*i32 = null;
50 try expect(x == y);
51 y = &number;
52 try expect(x != y);
53 try expect(x != &number);
54 try expect(&number != x);
55 x = &number;
56 try expect(x == y);
57 try expect(x == &number);
58 try expect(&number == x);
59}
60
61test "optional with zero-bit type" {
62 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
63 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
64
65 const S = struct {
66 fn doTheTest(comptime ZeroBit: type, comptime zero_bit: ZeroBit) !void {
67 const WithRuntime = struct {
68 zero_bit: ZeroBit,
69 runtime: u1,
70 };
71 var with_runtime: WithRuntime = undefined;
72 with_runtime = .{ .zero_bit = zero_bit, .runtime = 0 };
73
74 const Opt = struct { opt: ?ZeroBit };
75 var opt: Opt = .{ .opt = null };
76 try expect(opt.opt == null);
77 try expect(opt.opt != zero_bit);
78 try expect(opt.opt != with_runtime.zero_bit);
79 opt.opt = zero_bit;
80 try expect(opt.opt != null);
81 try expect(opt.opt == zero_bit);
82 try expect(opt.opt == with_runtime.zero_bit);
83 opt = .{ .opt = zero_bit };
84 try expect(opt.opt != null);
85 try expect(opt.opt == zero_bit);
86 try expect(opt.opt == with_runtime.zero_bit);
87 opt.opt = with_runtime.zero_bit;
88 try expect(opt.opt != null);
89 try expect(opt.opt == zero_bit);
90 try expect(opt.opt == with_runtime.zero_bit);
91 opt = .{ .opt = with_runtime.zero_bit };
92 try expect(opt.opt != null);
93 try expect(opt.opt == zero_bit);
94 try expect(opt.opt == with_runtime.zero_bit);
95
96 var two: ?struct { ZeroBit, ZeroBit } = undefined;
97 two = .{ with_runtime.zero_bit, with_runtime.zero_bit };
98 try expect(two != null);
99 try expect(two.?[0] == zero_bit);
100 try expect(two.?[0] == with_runtime.zero_bit);
101 try expect(two.?[1] == zero_bit);
102 try expect(two.?[1] == with_runtime.zero_bit);
103 }
104 };
105
106 try S.doTheTest(void, {});
107 try comptime S.doTheTest(void, {});
108 try S.doTheTest(enum { only }, .only);
109 try comptime S.doTheTest(enum { only }, .only);
110}
111
112test "address of unwrap optional" {
113 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
114 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
115 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
116 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
117
118 const S = struct {
119 const Foo = struct {
120 a: i32,
121 };
122
123 var global: ?Foo = null;
124
125 pub fn getFoo() anyerror!*Foo {
126 return &global.?;
127 }
128 };
129 S.global = S.Foo{ .a = 1234 };
130 const foo = S.getFoo() catch unreachable;
131 try expect(foo.a == 1234);
132}
133
134test "nested optional field in struct" {
135 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
136 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
137
138 const S2 = struct {
139 y: u8,
140 };
141 const S1 = struct {
142 x: ?S2,
143 };
144 var s = S1{
145 .x = S2{ .y = 127 },
146 };
147 _ = &s;
148 try expect(s.x.?.y == 127);
149}
150
151test "equality compare optionals and non-optionals" {
152 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
153 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
154 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
155
156 const S = struct {
157 fn doTheTest() !void {
158 var five: isize = 5;
159 var ten: isize = 10;
160 var opt_null: ?isize = null;
161 var opt_ten: ?isize = 10;
162 _ = .{ &five, &ten, &opt_null, &opt_ten };
163 try expect(opt_null != five);
164 try expect(opt_null != ten);
165 try expect(opt_ten != five);
166 try expect(opt_ten == ten);
167
168 var opt_int: ?isize = null;
169 try expect(opt_int != five);
170 try expect(opt_int != ten);
171 try expect(opt_int == opt_null);
172 try expect(opt_int != opt_ten);
173
174 opt_int = 10;
175 try expect(opt_int != five);
176 try expect(opt_int == ten);
177 try expect(opt_int != opt_null);
178 try expect(opt_int == opt_ten);
179
180 opt_int = five;
181 try expect(opt_int == five);
182 try expect(opt_int != ten);
183 try expect(opt_int != opt_null);
184 try expect(opt_int != opt_ten);
185
186 // test evaluation is always lexical
187 // ensure that the optional isn't always computed before the non-optional
188 var mutable_state: i32 = 0;
189 _ = blk1: {
190 mutable_state += 1;
191 break :blk1 @as(?f64, 10.0);
192 } != blk2: {
193 try expect(mutable_state == 1);
194 break :blk2 @as(f64, 5.0);
195 };
196 _ = blk1: {
197 mutable_state += 1;
198 break :blk1 @as(f64, 10.0);
199 } != blk2: {
200 try expect(mutable_state == 2);
201 break :blk2 @as(?f64, 5.0);
202 };
203 }
204 };
205
206 try S.doTheTest();
207 try comptime S.doTheTest();
208}
209
210test "compare optionals with modified payloads" {
211 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
212
213 var lhs: ?bool = false;
214 const lhs_payload = &lhs.?;
215 var rhs: ?bool = true;
216 const rhs_payload = &rhs.?;
217 try expect(lhs != rhs and !(lhs == rhs));
218
219 lhs = null;
220 lhs_payload.* = false;
221 rhs = false;
222 try expect(lhs != rhs and !(lhs == rhs));
223
224 lhs = true;
225 rhs = null;
226 rhs_payload.* = true;
227 try expect(lhs != rhs and !(lhs == rhs));
228
229 lhs = null;
230 lhs_payload.* = false;
231 rhs = null;
232 rhs_payload.* = true;
233 try expect(lhs == rhs and !(lhs != rhs));
234}
235
236test "unwrap function call with optional pointer return value" {
237 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
238 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
239 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
240
241 const S = struct {
242 fn entry() !void {
243 try expect(foo().?.* == 1234);
244 try expect(bar() == null);
245 }
246 const global: i32 = 1234;
247 fn foo() ?*const i32 {
248 return &global;
249 }
250 fn bar() ?*i32 {
251 return null;
252 }
253 };
254 try S.entry();
255 try comptime S.entry();
256}
257
258test "nested orelse" {
259 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
260 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
261
262 const S = struct {
263 fn entry() !void {
264 try expect(func() == null);
265 }
266 fn maybe() ?Foo {
267 return null;
268 }
269 fn func() ?Foo {
270 const x = maybe() orelse
271 maybe() orelse
272 return null;
273 _ = x;
274 unreachable;
275 }
276 const Foo = struct {
277 field: i32,
278 };
279 };
280 try S.entry();
281 try comptime S.entry();
282}
283
284test "self-referential struct through a slice of optional" {
285 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
286 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
287 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
288
289 const S = struct {
290 const Node = struct {
291 children: []?Node,
292 data: ?u8,
293
294 fn new() Node {
295 return Node{
296 .children = undefined,
297 .data = null,
298 };
299 }
300 };
301 };
302
303 const n = S.Node.new();
304 try expect(n.data == null);
305}
306
307test "assigning to an unwrapped optional field in an inline loop" {
308 comptime var maybe_pos_arg: ?comptime_int = null;
309 inline for ("ab") |x| {
310 _ = x;
311 maybe_pos_arg = 0;
312 if (maybe_pos_arg.? != 0) {
313 @compileError("bad");
314 }
315 maybe_pos_arg.? = 10;
316 }
317}
318
319test "coerce an anon struct literal to optional struct" {
320 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
321 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
322 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
323
324 const S = struct {
325 const Struct = struct {
326 field: u32,
327 };
328 fn doTheTest() !void {
329 var maybe_dims: ?Struct = null;
330 maybe_dims = .{ .field = 1 };
331 try expect(maybe_dims.?.field == 1);
332 }
333 };
334 try S.doTheTest();
335 try comptime S.doTheTest();
336}
337
338test "0-bit child type coerced to optional return ptr result location" {
339 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
340 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
341 const S = struct {
342 fn doTheTest() !void {
343 var y = Foo{};
344 const z = y.thing();
345 try expect(z != null);
346 }
347
348 const Foo = struct {
349 pub const Bar = struct {
350 field: *Foo,
351 };
352
353 pub fn thing(self: *Foo) ?Bar {
354 return Bar{ .field = self };
355 }
356 };
357 };
358 try S.doTheTest();
359 try comptime S.doTheTest();
360}
361
362test "0-bit child type coerced to optional" {
363 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
364 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
365 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
366 const S = struct {
367 fn doTheTest() !void {
368 var it: Foo = .{
369 .list = undefined,
370 };
371 try expect(it.foo() != null);
372 }
373
374 const Empty = struct {};
375 const Foo = struct {
376 list: [10]Empty,
377
378 fn foo(self: *Foo) ?*Empty {
379 const data = &self.list[0];
380 return data;
381 }
382 };
383 };
384 try S.doTheTest();
385 try comptime S.doTheTest();
386}
387
388test "array of optional unaligned types" {
389 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
390 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
391 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
392
393 const Enum = enum { one, two, three };
394
395 const SomeUnion = union(enum) {
396 Num: Enum,
397 Other: u32,
398 };
399
400 const values = [_]?SomeUnion{
401 SomeUnion{ .Num = .one },
402 SomeUnion{ .Num = .two },
403 SomeUnion{ .Num = .three },
404 SomeUnion{ .Num = .one },
405 SomeUnion{ .Num = .two },
406 SomeUnion{ .Num = .three },
407 };
408
409 // The index must be a runtime value
410 var i: usize = 0;
411 try expect(Enum.one == values[i].?.Num);
412 i += 1;
413 try expect(Enum.two == values[i].?.Num);
414 i += 1;
415 try expect(Enum.three == values[i].?.Num);
416 i += 1;
417 try expect(Enum.one == values[i].?.Num);
418 i += 1;
419 try expect(Enum.two == values[i].?.Num);
420 i += 1;
421 try expect(Enum.three == values[i].?.Num);
422}
423
424test "optional pointer to zero bit optional payload" {
425 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
426 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
427 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
428 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
429
430 const B = struct {
431 fn foo(_: *@This()) void {}
432 };
433 const A = struct {
434 b: ?B = .{},
435 };
436 var a: A = .{};
437 var a_ptr = &a;
438 if (a_ptr.b) |*some| {
439 some.foo();
440 }
441}
442
443test "optional pointer to zero bit error union payload" {
444 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
445 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
446 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
447 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
448
449 const B = struct {
450 fn foo(_: *@This()) void {}
451 };
452 const A = struct {
453 b: anyerror!B = .{},
454 };
455 var a: A = .{};
456 var a_ptr = &a;
457 if (a_ptr.b) |*some| {
458 some.foo();
459 } else |_| {}
460}
461
462const NoReturn = struct {
463 var a: u32 = undefined;
464 fn someData() bool {
465 a -= 1;
466 return a == 0;
467 }
468 fn loop() ?noreturn {
469 while (true) {
470 if (someData()) return null;
471 }
472 }
473 fn testOrelse() u32 {
474 loop() orelse return 123;
475 @compileError("bad");
476 }
477};
478
479test "optional of noreturn used with if" {
480 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
481
482 NoReturn.a = 64;
483 if (NoReturn.loop()) |_| {
484 @compileError("bad");
485 } else {
486 try expect(true);
487 }
488}
489
490test "optional of noreturn used with orelse" {
491 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
492
493 NoReturn.a = 64;
494 const val = NoReturn.testOrelse();
495 try expect(val == 123);
496}
497
498test "mutable optional of noreturn" {
499 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
500
501 var a: ?noreturn = null;
502 if (a) |*ptr| {
503 _ = ptr;
504 @compileError("bad");
505 } else {
506 // this is what we expect to hit
507 return;
508 }
509 @compileError("bad");
510}
511
512test "orelse on C pointer" {
513 // TODO https://github.com/ziglang/zig/issues/6597
514 const foo: [*c]const u8 = "hey";
515 const d = foo orelse @compileError("bad");
516 try expectEqual([*c]const u8, @TypeOf(d));
517}
518
519test "alignment of wrapping an optional payload" {
520 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
521 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
522 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
523 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
524
525 const S = struct {
526 const I = extern struct { x: i128 };
527
528 fn foo() ?I {
529 var i: I = .{ .x = 1234 };
530 _ = &i;
531 return i;
532 }
533 };
534 try expect(S.foo().?.x == 1234);
535}
536
537test "Optional slice size is optimized" {
538 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
539 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
540 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
541
542 try expect(@sizeOf(?[]u8) == @sizeOf([]u8));
543 var a: ?[]const u8 = null;
544 try expect(a == null);
545 a = "hello";
546 try expectEqualStrings(a.?, "hello");
547}
548
549test "Optional slice passed to function" {
550 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
551 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
552 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
553 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
554
555 const S = struct {
556 fn foo(a: ?[]const u8) !void {
557 try std.testing.expectEqualStrings(a.?, "foo");
558 }
559 fn bar(a: ?[]allowzero const u8) !void {
560 try std.testing.expectEqualStrings(@ptrCast(a.?), "bar");
561 }
562 };
563 try S.foo("foo");
564 try S.bar("bar");
565}
566
567test "peer type resolution in nested if expressions" {
568 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
569
570 const Thing = struct { n: i32 };
571 var a = false;
572 var b = false;
573 _ = .{ &a, &b };
574
575 const result1 = if (a)
576 Thing{ .n = 1 }
577 else
578 null;
579 try expect(result1 == null);
580 try expect(@TypeOf(result1) == ?Thing);
581
582 const result2 = if (a)
583 Thing{ .n = 0 }
584 else if (b)
585 Thing{ .n = 1 }
586 else
587 null;
588 try expect(result2 == null);
589 try expect(@TypeOf(result2) == ?Thing);
590}
591
592test "cast slice to const slice nested in error union and optional" {
593 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
594 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
595 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
596 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
597
598 const S = struct {
599 fn inner() !?[]u8 {
600 return error.Foo;
601 }
602 fn outer() !?[]const u8 {
603 return inner();
604 }
605 };
606 try std.testing.expectError(error.Foo, S.outer());
607}
608
609test "variable of optional of noreturn" {
610 var null_opv: ?noreturn = null;
611 _ = &null_opv;
612 try std.testing.expectEqual(@as(?noreturn, null), null_opv);
613}
614
615test "copied optional doesn't alias source" {
616 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
617 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
618 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
619 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
620
621 var opt_x: ?[3]f32 = @splat(0.0);
622
623 const x = opt_x.?;
624 opt_x.?[0] = 15.0;
625
626 try expect(x[0] == 0.0);
627}
628
629test "result location initialization of optional with OPV payload" {
630 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
631 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
632 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
633 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
634 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; // TODO
635
636 const S = struct {
637 x: u0,
638 };
639
640 const a: ?S = .{ .x = 0 };
641 comptime assert(a.?.x == 0);
642
643 comptime {
644 var b: ?S = .{ .x = 0 };
645 _ = &b;
646 assert(b.?.x == 0);
647 }
648
649 var c: ?S = .{ .x = 0 };
650 _ = &c;
651 try expectEqual(0, (c orelse return error.TestFailed).x);
652}
653
654test "global comptime only optional" {
655 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
656
657 const S = struct {
658 const @"null": ?*type = null;
659 const @"void": ?*const type = &void;
660 };
661 comptime {
662 assert(S.null == null);
663 assert(S.void.?.* == void);
664 }
665}
666
667test "optional ptr payload alignment" {
668 const S = struct {
669 fn doTheTest(p: *align(1) ?u32) !void {
670 comptime assert(@TypeOf(&p.*.?) == *align(1) u32);
671 try expect(p.*.? == 10);
672 }
673 };
674 var x: ?u32 = 10;
675 try S.doTheTest(&x);
676}