authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-09 18:19:03-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-09 18:29:51-07:00
log1e5a494603d287ea3005dc35f0528c0311f43515
tree7e5d6799a59ca44dc11d6b280dcd4f622ef0ca42
parent7f0cf395aa74eb5ea250bd28f7525b3036790a6a

Sema: implement comptime ptr store to optional payload

and error union payload

5 files changed, 232 insertions(+), 140 deletions(-)

src/Sema.zig+68-2
......@@ -15069,8 +15069,74 @@ fn beginComptimePtrMutation(
1506915069 else => unreachable,
1507015070 }
1507115071 },
15072 .eu_payload_ptr => return sema.fail(block, src, "TODO comptime store to eu_payload_ptr", .{}),
15073 .opt_payload_ptr => return sema.fail(block, src, "TODO comptime store opt_payload_ptr", .{}),
15072 .eu_payload_ptr => {
15073 const eu_ptr_val = ptr_val.castTag(.eu_payload_ptr).?.data;
15074 var parent = try beginComptimePtrMutation(sema, block, src, eu_ptr_val);
15075 const payload_ty = parent.ty.errorUnionPayload();
15076 switch (parent.val.tag()) {
15077 else => {
15078 // An error union has been initialized to undefined at comptime and now we
15079 // are for the first time setting the payload. We must change the
15080 // representation of the error union from `undef` to `opt_payload`.
15081 const arena = parent.beginArena(sema.gpa);
15082 defer parent.finishArena();
15083
15084 const payload = try arena.create(Value.Payload.SubValue);
15085 payload.* = .{
15086 .base = .{ .tag = .eu_payload },
15087 .data = Value.undef,
15088 };
15089
15090 parent.val.* = Value.initPayload(&payload.base);
15091
15092 return ComptimePtrMutationKit{
15093 .decl_ref_mut = parent.decl_ref_mut,
15094 .val = &payload.data,
15095 .ty = payload_ty,
15096 };
15097 },
15098 .eu_payload => return ComptimePtrMutationKit{
15099 .decl_ref_mut = parent.decl_ref_mut,
15100 .val = &parent.val.castTag(.eu_payload).?.data,
15101 .ty = payload_ty,
15102 },
15103 }
15104 },
15105 .opt_payload_ptr => {
15106 const opt_ptr_val = ptr_val.castTag(.opt_payload_ptr).?.data;
15107 var parent = try beginComptimePtrMutation(sema, block, src, opt_ptr_val);
15108 const payload_ty = try parent.ty.optionalChildAlloc(sema.arena);
15109 switch (parent.val.tag()) {
15110 .undef, .null_value => {
15111 // An optional has been initialized to undefined at comptime and now we
15112 // are for the first time setting the payload. We must change the
15113 // representation of the optional from `undef` to `opt_payload`.
15114 const arena = parent.beginArena(sema.gpa);
15115 defer parent.finishArena();
15116
15117 const payload = try arena.create(Value.Payload.SubValue);
15118 payload.* = .{
15119 .base = .{ .tag = .opt_payload },
15120 .data = Value.undef,
15121 };
15122
15123 parent.val.* = Value.initPayload(&payload.base);
15124
15125 return ComptimePtrMutationKit{
15126 .decl_ref_mut = parent.decl_ref_mut,
15127 .val = &payload.data,
15128 .ty = payload_ty,
15129 };
15130 },
15131 .opt_payload => return ComptimePtrMutationKit{
15132 .decl_ref_mut = parent.decl_ref_mut,
15133 .val = &parent.val.castTag(.opt_payload).?.data,
15134 .ty = payload_ty,
15135 },
15136
15137 else => unreachable,
15138 }
15139 },
1507415140 .decl_ref => unreachable, // isComptimeMutablePtr() has been checked already
1507515141 else => unreachable,
1507615142 }
test/behavior.zig+1-3
......@@ -33,7 +33,7 @@ test {
3333 _ = @import("behavior/hasdecl.zig");
3434 _ = @import("behavior/hasfield.zig");
3535 _ = @import("behavior/namespace_depends_on_compile_var.zig");
36 _ = @import("behavior/optional_llvm.zig");
36 _ = @import("behavior/optional.zig");
3737 _ = @import("behavior/prefetch.zig");
3838 _ = @import("behavior/pub_enum.zig");
3939 _ = @import("behavior/slice_sentinel_comptime.zig");
......@@ -69,7 +69,6 @@ test {
6969 _ = @import("behavior/inttoptr.zig");
7070 _ = @import("behavior/member_func.zig");
7171 _ = @import("behavior/null.zig");
72 _ = @import("behavior/optional.zig");
7372 _ = @import("behavior/pointers.zig");
7473 _ = @import("behavior/ptrcast.zig");
7574 _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig");
......@@ -154,7 +153,6 @@ test {
154153 _ = @import("behavior/ir_block_deps.zig");
155154 _ = @import("behavior/misc.zig");
156155 _ = @import("behavior/muladd.zig");
157 _ = @import("behavior/optional_stage1.zig");
158156 _ = @import("behavior/popcount_stage1.zig");
159157 _ = @import("behavior/reflection.zig");
160158 _ = @import("behavior/select.zig");
test/behavior/optional.zig+163
......@@ -1,9 +1,13 @@
1const builtin = @import("builtin");
12const std = @import("std");
23const testing = std.testing;
34const expect = testing.expect;
45const expectEqual = testing.expectEqual;
56
67test "passing an optional integer as a parameter" {
8 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
9 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
10
711 const S = struct {
812 fn entry() bool {
913 var x: i32 = 1234;
......@@ -21,12 +25,18 @@ test "passing an optional integer as a parameter" {
2125pub const EmptyStruct = struct {};
2226
2327test "optional pointer to size zero struct" {
28 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
29 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
30
2431 var e = EmptyStruct{};
2532 var o: ?*EmptyStruct = &e;
2633 try expect(o != null);
2734}
2835
2936test "equality compare optional pointers" {
37 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
38 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
39
3040 try testNullPtrsEql();
3141 comptime try testNullPtrsEql();
3242}
......@@ -48,6 +58,9 @@ fn testNullPtrsEql() !void {
4858}
4959
5060test "optional with void type" {
61 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
62 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
63
5164 const Foo = struct {
5265 x: ?void,
5366 };
......@@ -56,6 +69,9 @@ test "optional with void type" {
5669}
5770
5871test "address of unwrap optional" {
72 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
73 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
74
5975 const S = struct {
6076 const Foo = struct {
6177 a: i32,
......@@ -73,6 +89,9 @@ test "address of unwrap optional" {
7389}
7490
7591test "nested optional field in struct" {
92 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
93 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
94
7695 const S2 = struct {
7796 y: u8,
7897 };
......@@ -86,6 +105,9 @@ test "nested optional field in struct" {
86105}
87106
88107test "equality compare optional with non-optional" {
108 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
109 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
110
89111 try test_cmp_optional_non_optional();
90112 comptime try test_cmp_optional_non_optional();
91113}
......@@ -120,6 +142,9 @@ fn test_cmp_optional_non_optional() !void {
120142}
121143
122144test "unwrap function call with optional pointer return value" {
145 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
146 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
147
123148 const S = struct {
124149 fn entry() !void {
125150 try expect(foo().?.* == 1234);
......@@ -138,6 +163,9 @@ test "unwrap function call with optional pointer return value" {
138163}
139164
140165test "nested orelse" {
166 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
167 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
168
141169 const S = struct {
142170 fn entry() !void {
143171 try expect(func() == null);
......@@ -159,3 +187,138 @@ test "nested orelse" {
159187 try S.entry();
160188 comptime try S.entry();
161189}
190
191test "self-referential struct through a slice of optional" {
192 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
193 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
194 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
195
196 const S = struct {
197 const Node = struct {
198 children: []?Node,
199 data: ?u8,
200
201 fn new() Node {
202 return Node{
203 .children = undefined,
204 .data = null,
205 };
206 }
207 };
208 };
209
210 var n = S.Node.new();
211 try expect(n.data == null);
212}
213
214test "assigning to an unwrapped optional field in an inline loop" {
215 comptime var maybe_pos_arg: ?comptime_int = null;
216 inline for ("ab") |x| {
217 _ = x;
218 maybe_pos_arg = 0;
219 if (maybe_pos_arg.? != 0) {
220 @compileError("bad");
221 }
222 maybe_pos_arg.? = 10;
223 }
224}
225
226test "coerce an anon struct literal to optional struct" {
227 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
228
229 const S = struct {
230 const Struct = struct {
231 field: u32,
232 };
233 fn doTheTest() !void {
234 var maybe_dims: ?Struct = null;
235 maybe_dims = .{ .field = 1 };
236 try expect(maybe_dims.?.field == 1);
237 }
238 };
239 try S.doTheTest();
240 comptime try S.doTheTest();
241}
242
243test "0-bit child type coerced to optional return ptr result location" {
244 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
245
246 const S = struct {
247 fn doTheTest() !void {
248 var y = Foo{};
249 var z = y.thing();
250 try expect(z != null);
251 }
252
253 const Foo = struct {
254 pub const Bar = struct {
255 field: *Foo,
256 };
257
258 pub fn thing(self: *Foo) ?Bar {
259 return Bar{ .field = self };
260 }
261 };
262 };
263 try S.doTheTest();
264 comptime try S.doTheTest();
265}
266
267test "0-bit child type coerced to optional" {
268 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
269
270 const S = struct {
271 fn doTheTest() !void {
272 var it: Foo = .{
273 .list = undefined,
274 };
275 try expect(it.foo() != null);
276 }
277
278 const Empty = struct {};
279 const Foo = struct {
280 list: [10]Empty,
281
282 fn foo(self: *Foo) ?*Empty {
283 const data = &self.list[0];
284 return data;
285 }
286 };
287 };
288 try S.doTheTest();
289 comptime try S.doTheTest();
290}
291
292test "array of optional unaligned types" {
293 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
294
295 const Enum = enum { one, two, three };
296
297 const SomeUnion = union(enum) {
298 Num: Enum,
299 Other: u32,
300 };
301
302 const values = [_]?SomeUnion{
303 SomeUnion{ .Num = .one },
304 SomeUnion{ .Num = .two },
305 SomeUnion{ .Num = .three },
306 SomeUnion{ .Num = .one },
307 SomeUnion{ .Num = .two },
308 SomeUnion{ .Num = .three },
309 };
310
311 // The index must be a runtime value
312 var i: usize = 0;
313 try expectEqual(Enum.one, values[i].?.Num);
314 i += 1;
315 try expectEqual(Enum.two, values[i].?.Num);
316 i += 1;
317 try expectEqual(Enum.three, values[i].?.Num);
318 i += 1;
319 try expectEqual(Enum.one, values[i].?.Num);
320 i += 1;
321 try expectEqual(Enum.two, values[i].?.Num);
322 i += 1;
323 try expectEqual(Enum.three, values[i].?.Num);
324}
test/behavior/optional_llvm.zig deleted-27
......@@ -1,27 +0,0 @@
1const std = @import("std");
2const testing = std.testing;
3const expect = testing.expect;
4const expectEqual = testing.expectEqual;
5const builtin = @import("builtin");
6
7test "self-referential struct through a slice of optional" {
8 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
9 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
10 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
11 const S = struct {
12 const Node = struct {
13 children: []?Node,
14 data: ?u8,
15
16 fn new() Node {
17 return Node{
18 .children = undefined,
19 .data = null,
20 };
21 }
22 };
23 };
24
25 var n = S.Node.new();
26 try expect(n.data == null);
27}
test/behavior/optional_stage1.zig deleted-108
......@@ -1,108 +0,0 @@
1const std = @import("std");
2const testing = std.testing;
3const expect = testing.expect;
4const expectEqual = testing.expectEqual;
5
6test "assigning to an unwrapped optional field in an inline loop" {
7 comptime var maybe_pos_arg: ?comptime_int = null;
8 inline for ("ab") |x| {
9 _ = x;
10 maybe_pos_arg = 0;
11 if (maybe_pos_arg.? != 0) {
12 @compileError("bad");
13 }
14 maybe_pos_arg.? = 10;
15 }
16}
17
18test "coerce an anon struct literal to optional struct" {
19 const S = struct {
20 const Struct = struct {
21 field: u32,
22 };
23 fn doTheTest() !void {
24 var maybe_dims: ?Struct = null;
25 maybe_dims = .{ .field = 1 };
26 try expect(maybe_dims.?.field == 1);
27 }
28 };
29 try S.doTheTest();
30 comptime try S.doTheTest();
31}
32
33test "0-bit child type coerced to optional return ptr result location" {
34 const S = struct {
35 fn doTheTest() !void {
36 var y = Foo{};
37 var z = y.thing();
38 try expect(z != null);
39 }
40
41 const Foo = struct {
42 pub const Bar = struct {
43 field: *Foo,
44 };
45
46 pub fn thing(self: *Foo) ?Bar {
47 return Bar{ .field = self };
48 }
49 };
50 };
51 try S.doTheTest();
52 comptime try S.doTheTest();
53}
54
55test "0-bit child type coerced to optional" {
56 const S = struct {
57 fn doTheTest() !void {
58 var it: Foo = .{
59 .list = undefined,
60 };
61 try expect(it.foo() != null);
62 }
63
64 const Empty = struct {};
65 const Foo = struct {
66 list: [10]Empty,
67
68 fn foo(self: *Foo) ?*Empty {
69 const data = &self.list[0];
70 return data;
71 }
72 };
73 };
74 try S.doTheTest();
75 comptime try S.doTheTest();
76}
77
78test "array of optional unaligned types" {
79 const Enum = enum { one, two, three };
80
81 const SomeUnion = union(enum) {
82 Num: Enum,
83 Other: u32,
84 };
85
86 const values = [_]?SomeUnion{
87 SomeUnion{ .Num = .one },
88 SomeUnion{ .Num = .two },
89 SomeUnion{ .Num = .three },
90 SomeUnion{ .Num = .one },
91 SomeUnion{ .Num = .two },
92 SomeUnion{ .Num = .three },
93 };
94
95 // The index must be a runtime value
96 var i: usize = 0;
97 try expectEqual(Enum.one, values[i].?.Num);
98 i += 1;
99 try expectEqual(Enum.two, values[i].?.Num);
100 i += 1;
101 try expectEqual(Enum.three, values[i].?.Num);
102 i += 1;
103 try expectEqual(Enum.one, values[i].?.Num);
104 i += 1;
105 try expectEqual(Enum.two, values[i].?.Num);
106 i += 1;
107 try expectEqual(Enum.three, values[i].?.Num);
108}