Commit 5529febab0

Andrew Kelley <andrew@ziglang.org>
2021-09-14 05:08:44
stage2: implement Value.copy for structs and unions
The stage2_os hack inside `@import("builtin")` is no longer needed.
1 parent f011f13
Changed files (4)
lib/std/start.zig
@@ -92,7 +92,7 @@ fn _start2() callconv(.Naked) noreturn {
 }
 
 fn exit2(code: usize) noreturn {
-    switch (builtin.stage2_os) {
+    switch (native_os) {
         .linux => switch (builtin.stage2_arch) {
             .x86_64 => {
                 asm volatile ("syscall"
src/Compilation.zig
@@ -3671,8 +3671,6 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Alloc
         \\pub const zig_is_stage2 = {};
         \\/// Temporary until self-hosted supports the `cpu.arch` value.
         \\pub const stage2_arch: std.Target.Cpu.Arch = .{};
-        \\/// Temporary until self-hosted supports the `os.tag` value.
-        \\pub const stage2_os: std.Target.Os.Tag = .{};
         \\
         \\pub const output_mode = std.builtin.OutputMode.{};
         \\pub const link_mode = std.builtin.LinkMode.{};
@@ -3688,7 +3686,6 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: *Allocator) Alloc
         build_options.version,
         !use_stage1,
         std.zig.fmtId(@tagName(target.cpu.arch)),
-        std.zig.fmtId(@tagName(target.os.tag)),
         std.zig.fmtId(@tagName(comp.bin_file.options.output_mode)),
         std.zig.fmtId(@tagName(comp.bin_file.options.link_mode)),
         comp.bin_file.options.is_test,
src/Sema.zig
@@ -6313,7 +6313,7 @@ fn zirTypeInfo(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
                         sema.arena,
                         @enumToInt(@typeInfo(std.builtin.TypeInfo).Union.tag_type.?.Fn),
                     ),
-                    .val = try Value.Tag.@"struct".create(sema.arena, field_values.ptr),
+                    .val = try Value.Tag.@"struct".create(sema.arena, field_values),
                 }),
             );
         },
@@ -6335,7 +6335,7 @@ fn zirTypeInfo(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
                         sema.arena,
                         @enumToInt(@typeInfo(std.builtin.TypeInfo).Union.tag_type.?.Int),
                     ),
-                    .val = try Value.Tag.@"struct".create(sema.arena, field_values.ptr),
+                    .val = try Value.Tag.@"struct".create(sema.arena, field_values),
                 }),
             );
         },
@@ -6943,7 +6943,7 @@ fn zirStructInit(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref:
             for (field_inits) |field_init, i| {
                 values[i] = (sema.resolveMaybeUndefVal(block, src, field_init) catch unreachable).?;
             }
-            return sema.addConstant(resolved_ty, try Value.Tag.@"struct".create(sema.arena, values.ptr));
+            return sema.addConstant(resolved_ty, try Value.Tag.@"struct".create(sema.arena, values));
         }
 
         return mod.fail(&block.base, src, "TODO: Sema.zirStructInit for runtime-known struct values", .{});
src/value.zig
@@ -344,7 +344,9 @@ pub const Value = extern union {
         return null;
     }
 
-    pub fn copy(self: Value, allocator: *Allocator) error{OutOfMemory}!Value {
+    /// It's intentional that this function is not passed a corresponding Type, so that
+    /// a Value can be copied from a Sema to a Decl prior to resolving struct/union field types.
+    pub fn copy(self: Value, arena: *Allocator) error{OutOfMemory}!Value {
         if (self.tag_if_small_enough < Tag.no_payload_count) {
             return Value{ .tag_if_small_enough = self.tag_if_small_enough };
         } else switch (self.ptr_otherwise.tag) {
@@ -421,37 +423,37 @@ pub const Value = extern union {
 
             .ty => {
                 const payload = self.castTag(.ty).?;
-                const new_payload = try allocator.create(Payload.Ty);
+                const new_payload = try arena.create(Payload.Ty);
                 new_payload.* = .{
                     .base = payload.base,
-                    .data = try payload.data.copy(allocator),
+                    .data = try payload.data.copy(arena),
                 };
                 return Value{ .ptr_otherwise = &new_payload.base };
             },
-            .int_type => return self.copyPayloadShallow(allocator, Payload.IntType),
-            .int_u64 => return self.copyPayloadShallow(allocator, Payload.U64),
-            .int_i64 => return self.copyPayloadShallow(allocator, Payload.I64),
+            .int_type => return self.copyPayloadShallow(arena, Payload.IntType),
+            .int_u64 => return self.copyPayloadShallow(arena, Payload.U64),
+            .int_i64 => return self.copyPayloadShallow(arena, Payload.I64),
             .int_big_positive, .int_big_negative => {
                 const old_payload = self.cast(Payload.BigInt).?;
-                const new_payload = try allocator.create(Payload.BigInt);
+                const new_payload = try arena.create(Payload.BigInt);
                 new_payload.* = .{
                     .base = .{ .tag = self.ptr_otherwise.tag },
-                    .data = try allocator.dupe(std.math.big.Limb, old_payload.data),
+                    .data = try arena.dupe(std.math.big.Limb, old_payload.data),
                 };
                 return Value{ .ptr_otherwise = &new_payload.base };
             },
-            .function => return self.copyPayloadShallow(allocator, Payload.Function),
-            .extern_fn => return self.copyPayloadShallow(allocator, Payload.Decl),
-            .variable => return self.copyPayloadShallow(allocator, Payload.Variable),
-            .decl_ref => return self.copyPayloadShallow(allocator, Payload.Decl),
-            .decl_ref_mut => return self.copyPayloadShallow(allocator, Payload.DeclRefMut),
+            .function => return self.copyPayloadShallow(arena, Payload.Function),
+            .extern_fn => return self.copyPayloadShallow(arena, Payload.Decl),
+            .variable => return self.copyPayloadShallow(arena, Payload.Variable),
+            .decl_ref => return self.copyPayloadShallow(arena, Payload.Decl),
+            .decl_ref_mut => return self.copyPayloadShallow(arena, Payload.DeclRefMut),
             .elem_ptr => {
                 const payload = self.castTag(.elem_ptr).?;
-                const new_payload = try allocator.create(Payload.ElemPtr);
+                const new_payload = try arena.create(Payload.ElemPtr);
                 new_payload.* = .{
                     .base = payload.base,
                     .data = .{
-                        .array_ptr = try payload.data.array_ptr.copy(allocator),
+                        .array_ptr = try payload.data.array_ptr.copy(arena),
                         .index = payload.data.index,
                     },
                 };
@@ -459,17 +461,17 @@ pub const Value = extern union {
             },
             .field_ptr => {
                 const payload = self.castTag(.field_ptr).?;
-                const new_payload = try allocator.create(Payload.FieldPtr);
+                const new_payload = try arena.create(Payload.FieldPtr);
                 new_payload.* = .{
                     .base = payload.base,
                     .data = .{
-                        .container_ptr = try payload.data.container_ptr.copy(allocator),
+                        .container_ptr = try payload.data.container_ptr.copy(arena),
                         .field_index = payload.data.field_index,
                     },
                 };
                 return Value{ .ptr_otherwise = &new_payload.base };
             },
-            .bytes => return self.copyPayloadShallow(allocator, Payload.Bytes),
+            .bytes => return self.copyPayloadShallow(arena, Payload.Bytes),
             .repeated,
             .eu_payload,
             .eu_payload_ptr,
@@ -477,61 +479,85 @@ pub const Value = extern union {
             .opt_payload_ptr,
             => {
                 const payload = self.cast(Payload.SubValue).?;
-                const new_payload = try allocator.create(Payload.SubValue);
+                const new_payload = try arena.create(Payload.SubValue);
                 new_payload.* = .{
                     .base = payload.base,
-                    .data = try payload.data.copy(allocator),
+                    .data = try payload.data.copy(arena),
                 };
                 return Value{ .ptr_otherwise = &new_payload.base };
             },
             .array => {
                 const payload = self.castTag(.array).?;
-                const new_payload = try allocator.create(Payload.Array);
+                const new_payload = try arena.create(Payload.Array);
                 new_payload.* = .{
                     .base = payload.base,
-                    .data = try allocator.alloc(Value, payload.data.len),
+                    .data = try arena.alloc(Value, payload.data.len),
                 };
                 std.mem.copy(Value, new_payload.data, payload.data);
                 return Value{ .ptr_otherwise = &new_payload.base };
             },
             .slice => {
                 const payload = self.castTag(.slice).?;
-                const new_payload = try allocator.create(Payload.Slice);
+                const new_payload = try arena.create(Payload.Slice);
                 new_payload.* = .{
                     .base = payload.base,
                     .data = .{
-                        .ptr = try payload.data.ptr.copy(allocator),
-                        .len = try payload.data.len.copy(allocator),
+                        .ptr = try payload.data.ptr.copy(arena),
+                        .len = try payload.data.len.copy(arena),
                     },
                 };
                 return Value{ .ptr_otherwise = &new_payload.base };
             },
-            .float_16 => return self.copyPayloadShallow(allocator, Payload.Float_16),
-            .float_32 => return self.copyPayloadShallow(allocator, Payload.Float_32),
-            .float_64 => return self.copyPayloadShallow(allocator, Payload.Float_64),
-            .float_128 => return self.copyPayloadShallow(allocator, Payload.Float_128),
+            .float_16 => return self.copyPayloadShallow(arena, Payload.Float_16),
+            .float_32 => return self.copyPayloadShallow(arena, Payload.Float_32),
+            .float_64 => return self.copyPayloadShallow(arena, Payload.Float_64),
+            .float_128 => return self.copyPayloadShallow(arena, Payload.Float_128),
             .enum_literal => {
                 const payload = self.castTag(.enum_literal).?;
-                const new_payload = try allocator.create(Payload.Bytes);
+                const new_payload = try arena.create(Payload.Bytes);
                 new_payload.* = .{
                     .base = payload.base,
-                    .data = try allocator.dupe(u8, payload.data),
+                    .data = try arena.dupe(u8, payload.data),
+                };
+                return Value{ .ptr_otherwise = &new_payload.base };
+            },
+            .enum_field_index => return self.copyPayloadShallow(arena, Payload.U32),
+            .@"error" => return self.copyPayloadShallow(arena, Payload.Error),
+
+            .@"struct" => {
+                const old_field_values = self.castTag(.@"struct").?.data;
+                const new_payload = try arena.create(Payload.Struct);
+                new_payload.* = .{
+                    .base = .{ .tag = .@"struct" },
+                    .data = try arena.alloc(Value, old_field_values.len),
+                };
+                for (old_field_values) |old_field_val, i| {
+                    new_payload.data[i] = try old_field_val.copy(arena);
+                }
+                return Value{ .ptr_otherwise = &new_payload.base };
+            },
+
+            .@"union" => {
+                const tag_and_val = self.castTag(.@"union").?.data;
+                const new_payload = try arena.create(Payload.Union);
+                new_payload.* = .{
+                    .base = .{ .tag = .@"union" },
+                    .data = .{
+                        .tag = try tag_and_val.tag.copy(arena),
+                        .val = try tag_and_val.val.copy(arena),
+                    },
                 };
                 return Value{ .ptr_otherwise = &new_payload.base };
             },
-            .enum_field_index => return self.copyPayloadShallow(allocator, Payload.U32),
-            .@"error" => return self.copyPayloadShallow(allocator, Payload.Error),
-            .@"struct" => @panic("TODO can't copy struct value without knowing the type"),
-            .@"union" => @panic("TODO can't copy union value without knowing the type"),
 
             .inferred_alloc => unreachable,
             .inferred_alloc_comptime => unreachable,
         }
     }
 
-    fn copyPayloadShallow(self: Value, allocator: *Allocator, comptime T: type) error{OutOfMemory}!Value {
+    fn copyPayloadShallow(self: Value, arena: *Allocator, comptime T: type) error{OutOfMemory}!Value {
         const payload = self.cast(T).?;
-        const new_payload = try allocator.create(T);
+        const new_payload = try arena.create(T);
         new_payload.* = payload.*;
         return Value{ .ptr_otherwise = &new_payload.base };
     }
@@ -1932,8 +1958,9 @@ pub const Value = extern union {
             pub const base_tag = Tag.@"struct";
 
             base: Payload = .{ .tag = base_tag },
-            /// Field values. The number and type are according to the struct type.
-            data: [*]Value,
+            /// Field values. The types are according to the struct type.
+            /// The length is provided here so that copying a Value does not depend on the Type.
+            data: []Value,
         };
 
         pub const Union = struct {