Commit 1e5a494603

Andrew Kelley <andrew@ziglang.org>
2022-02-10 02:19:03
Sema: implement comptime ptr store to optional payload
and error union payload
1 parent 7f0cf39
src/Sema.zig
@@ -15069,8 +15069,74 @@ fn beginComptimePtrMutation(
                 else => unreachable,
             }
         },
-        .eu_payload_ptr => return sema.fail(block, src, "TODO comptime store to eu_payload_ptr", .{}),
-        .opt_payload_ptr => return sema.fail(block, src, "TODO comptime store opt_payload_ptr", .{}),
+        .eu_payload_ptr => {
+            const eu_ptr_val = ptr_val.castTag(.eu_payload_ptr).?.data;
+            var parent = try beginComptimePtrMutation(sema, block, src, eu_ptr_val);
+            const payload_ty = parent.ty.errorUnionPayload();
+            switch (parent.val.tag()) {
+                else => {
+                    // An error union has been initialized to undefined at comptime and now we
+                    // are for the first time setting the payload. We must change the
+                    // representation of the error union from `undef` to `opt_payload`.
+                    const arena = parent.beginArena(sema.gpa);
+                    defer parent.finishArena();
+
+                    const payload = try arena.create(Value.Payload.SubValue);
+                    payload.* = .{
+                        .base = .{ .tag = .eu_payload },
+                        .data = Value.undef,
+                    };
+
+                    parent.val.* = Value.initPayload(&payload.base);
+
+                    return ComptimePtrMutationKit{
+                        .decl_ref_mut = parent.decl_ref_mut,
+                        .val = &payload.data,
+                        .ty = payload_ty,
+                    };
+                },
+                .eu_payload => return ComptimePtrMutationKit{
+                    .decl_ref_mut = parent.decl_ref_mut,
+                    .val = &parent.val.castTag(.eu_payload).?.data,
+                    .ty = payload_ty,
+                },
+            }
+        },
+        .opt_payload_ptr => {
+            const opt_ptr_val = ptr_val.castTag(.opt_payload_ptr).?.data;
+            var parent = try beginComptimePtrMutation(sema, block, src, opt_ptr_val);
+            const payload_ty = try parent.ty.optionalChildAlloc(sema.arena);
+            switch (parent.val.tag()) {
+                .undef, .null_value => {
+                    // An optional has been initialized to undefined at comptime and now we
+                    // are for the first time setting the payload. We must change the
+                    // representation of the optional from `undef` to `opt_payload`.
+                    const arena = parent.beginArena(sema.gpa);
+                    defer parent.finishArena();
+
+                    const payload = try arena.create(Value.Payload.SubValue);
+                    payload.* = .{
+                        .base = .{ .tag = .opt_payload },
+                        .data = Value.undef,
+                    };
+
+                    parent.val.* = Value.initPayload(&payload.base);
+
+                    return ComptimePtrMutationKit{
+                        .decl_ref_mut = parent.decl_ref_mut,
+                        .val = &payload.data,
+                        .ty = payload_ty,
+                    };
+                },
+                .opt_payload => return ComptimePtrMutationKit{
+                    .decl_ref_mut = parent.decl_ref_mut,
+                    .val = &parent.val.castTag(.opt_payload).?.data,
+                    .ty = payload_ty,
+                },
+
+                else => unreachable,
+            }
+        },
         .decl_ref => unreachable, // isComptimeMutablePtr() has been checked already
         else => unreachable,
     }
test/behavior/optional.zig
@@ -1,9 +1,13 @@
+const builtin = @import("builtin");
 const std = @import("std");
 const testing = std.testing;
 const expect = testing.expect;
 const expectEqual = testing.expectEqual;
 
 test "passing an optional integer as a parameter" {
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+
     const S = struct {
         fn entry() bool {
             var x: i32 = 1234;
@@ -21,12 +25,18 @@ test "passing an optional integer as a parameter" {
 pub const EmptyStruct = struct {};
 
 test "optional pointer to size zero struct" {
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+
     var e = EmptyStruct{};
     var o: ?*EmptyStruct = &e;
     try expect(o != null);
 }
 
 test "equality compare optional pointers" {
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+
     try testNullPtrsEql();
     comptime try testNullPtrsEql();
 }
@@ -48,6 +58,9 @@ fn testNullPtrsEql() !void {
 }
 
 test "optional with void type" {
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+
     const Foo = struct {
         x: ?void,
     };
@@ -56,6 +69,9 @@ test "optional with void type" {
 }
 
 test "address of unwrap optional" {
+    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,
@@ -73,6 +89,9 @@ test "address of unwrap optional" {
 }
 
 test "nested optional field in struct" {
+    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,
     };
@@ -86,6 +105,9 @@ test "nested optional field in struct" {
 }
 
 test "equality compare optional with non-optional" {
+    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();
 }
@@ -120,6 +142,9 @@ fn test_cmp_optional_non_optional() !void {
 }
 
 test "unwrap function call with optional pointer return value" {
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+
     const S = struct {
         fn entry() !void {
             try expect(foo().?.* == 1234);
@@ -138,6 +163,9 @@ test "unwrap function call with optional pointer return value" {
 }
 
 test "nested orelse" {
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+
     const S = struct {
         fn entry() !void {
             try expect(func() == null);
@@ -159,3 +187,138 @@ test "nested orelse" {
     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
+    if (builtin.zig_backend == .stage2_x86_64) 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 != .stage1) 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 != .stage1) 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 != .stage1) 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 != .stage1) 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 expectEqual(Enum.one, values[i].?.Num);
+    i += 1;
+    try expectEqual(Enum.two, values[i].?.Num);
+    i += 1;
+    try expectEqual(Enum.three, values[i].?.Num);
+    i += 1;
+    try expectEqual(Enum.one, values[i].?.Num);
+    i += 1;
+    try expectEqual(Enum.two, values[i].?.Num);
+    i += 1;
+    try expectEqual(Enum.three, values[i].?.Num);
+}
test/behavior/optional_llvm.zig
@@ -1,27 +0,0 @@
-const std = @import("std");
-const testing = std.testing;
-const expect = testing.expect;
-const expectEqual = testing.expectEqual;
-const builtin = @import("builtin");
-
-test "self-referential struct through a slice of optional" {
-    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
-    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
-    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/behavior/optional_stage1.zig
@@ -1,108 +0,0 @@
-const std = @import("std");
-const testing = std.testing;
-const expect = testing.expect;
-const expectEqual = testing.expectEqual;
-
-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" {
-    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" {
-    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" {
-    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" {
-    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 expectEqual(Enum.one, values[i].?.Num);
-    i += 1;
-    try expectEqual(Enum.two, values[i].?.Num);
-    i += 1;
-    try expectEqual(Enum.three, values[i].?.Num);
-    i += 1;
-    try expectEqual(Enum.one, values[i].?.Num);
-    i += 1;
-    try expectEqual(Enum.two, values[i].?.Num);
-    i += 1;
-    try expectEqual(Enum.three, values[i].?.Num);
-}
test/behavior.zig
@@ -33,7 +33,7 @@ test {
     _ = @import("behavior/hasdecl.zig");
     _ = @import("behavior/hasfield.zig");
     _ = @import("behavior/namespace_depends_on_compile_var.zig");
-    _ = @import("behavior/optional_llvm.zig");
+    _ = @import("behavior/optional.zig");
     _ = @import("behavior/prefetch.zig");
     _ = @import("behavior/pub_enum.zig");
     _ = @import("behavior/slice_sentinel_comptime.zig");
@@ -69,7 +69,6 @@ test {
         _ = @import("behavior/inttoptr.zig");
         _ = @import("behavior/member_func.zig");
         _ = @import("behavior/null.zig");
-        _ = @import("behavior/optional.zig");
         _ = @import("behavior/pointers.zig");
         _ = @import("behavior/ptrcast.zig");
         _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig");
@@ -154,7 +153,6 @@ test {
                     _ = @import("behavior/ir_block_deps.zig");
                     _ = @import("behavior/misc.zig");
                     _ = @import("behavior/muladd.zig");
-                    _ = @import("behavior/optional_stage1.zig");
                     _ = @import("behavior/popcount_stage1.zig");
                     _ = @import("behavior/reflection.zig");
                     _ = @import("behavior/select.zig");