Commit 12f3c461a4

Veikka Tuominen <git@vexu.eu>
2022-03-19 14:13:27
Sema: implement zirSwitchCaptureElse for error sets
1 parent c9b6f1b
lib/std/c/darwin.zig
@@ -624,8 +624,7 @@ pub const pthread_attr_t = extern struct {
     __opaque: [56]u8,
 };
 
-const pthread_t = std.c.pthread_t;
-pub extern "c" fn pthread_threadid_np(thread: ?pthread_t, thread_id: *u64) c_int;
+pub extern "c" fn pthread_threadid_np(thread: ?std.c.pthread_t, thread_id: *u64) c_int;
 pub extern "c" fn pthread_setname_np(name: [*:0]const u8) E;
 pub extern "c" fn pthread_getname_np(thread: std.c.pthread_t, name: [*:0]u8, len: usize) E;
 
@@ -921,12 +920,17 @@ pub const siginfo_t = extern struct {
 
 /// Renamed from `sigaction` to `Sigaction` to avoid conflict with function name.
 pub const Sigaction = extern struct {
-    pub const handler_fn = fn (c_int) callconv(.C) void;
-    pub const sigaction_fn = fn (c_int, *const siginfo_t, ?*const anyopaque) callconv(.C) void;
+    pub usingnamespace if (builtin.zig_backend == .stage1) struct {
+        pub const handler_fn = fn (c_int) callconv(.C) void;
+        pub const sigaction_fn = fn (c_int, *const siginfo_t, ?*const anyopaque) callconv(.C) void;
+    } else struct {
+        pub const handler_fn = *const fn (c_int) callconv(.C) void;
+        pub const sigaction_fn = *const fn (c_int, *const siginfo_t, ?*const anyopaque) callconv(.C) void;
+    };
 
     handler: extern union {
-        handler: ?handler_fn,
-        sigaction: ?sigaction_fn,
+        handler: ?Sigaction.handler_fn,
+        sigaction: ?Sigaction.sigaction_fn,
     },
     mask: sigset_t,
     flags: c_uint,
lib/std/heap/general_purpose_allocator.zig
@@ -341,15 +341,9 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
                             const slot_index = @intCast(SlotIndex, used_bits_byte * 8 + bit_index);
                             const stack_trace = bucketStackTrace(bucket, size_class, slot_index, .alloc);
                             const addr = bucket.page + slot_index * size_class;
-                            if (builtin.zig_backend == .stage1 or builtin.os.tag == .linux) {
-                                log.err("memory address 0x{x} leaked: {s}", .{
-                                    @ptrToInt(addr), stack_trace,
-                                });
-                            } else { // TODO
-                                log.err("memory address 0x{x} leaked", .{
-                                    @ptrToInt(addr),
-                                });
-                            }
+                            log.err("memory address 0x{x} leaked: {s}", .{
+                                @ptrToInt(addr), stack_trace,
+                            });
                             leaks = true;
                         }
                         if (bit_index == math.maxInt(u3))
@@ -379,15 +373,9 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
             while (it.next()) |large_alloc| {
                 if (config.retain_metadata and large_alloc.freed) continue;
                 const stack_trace = large_alloc.getStackTrace(.alloc);
-                if (builtin.zig_backend == .stage1 or builtin.os.tag == .linux) {
-                    log.err("memory address 0x{x} leaked: {s}", .{
-                        @ptrToInt(large_alloc.bytes.ptr), stack_trace,
-                    });
-                } else { // TODO
-                    log.err("memory address 0x{x} leaked", .{
-                        @ptrToInt(large_alloc.bytes.ptr),
-                    });
-                }
+                log.err("memory address 0x{x} leaked: {s}", .{
+                    @ptrToInt(large_alloc.bytes.ptr), stack_trace,
+                });
                 leaks = true;
             }
             return leaks;
lib/std/builtin.zig
@@ -753,9 +753,7 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn
     @setCold(true);
     // Until self-hosted catches up with stage1 language features, we have a simpler
     // default panic function:
-    const panic_works = builtin.zig_backend == .stage1 or
-        (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .linux);
-    if (!panic_works) {
+    if (builtin.zig_backend != .stage1 and builtin.zig_backend != .stage2_llvm) {
         while (true) {
             @breakpoint();
         }
lib/std/macho.zig
@@ -624,7 +624,9 @@ pub const segment_command_64 = extern struct {
     cmd: LC = .SEGMENT_64,
 
     /// includes sizeof section_64 structs
-    cmdsize: u32 = @sizeOf(segment_command_64),
+    cmdsize: u32,
+    // TODO lazy values in stage2
+    // cmdsize: u32 = @sizeOf(segment_command_64),
 
     /// segment name
     segname: [16]u8,
src/link/MachO/DebugSymbols.zig
@@ -148,6 +148,7 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void
                     .vmsize = needed_size,
                     .fileoff = fileoff,
                     .filesize = needed_size,
+                    .cmdsize = @sizeOf(macho.segment_command_64),
                 },
             },
         });
src/link/MachO.zig
@@ -4301,6 +4301,7 @@ fn populateMissingMetadata(self: *MachO) !void {
                 .inner = .{
                     .segname = makeStaticString("__PAGEZERO"),
                     .vmsize = pagezero_vmsize,
+                    .cmdsize = @sizeOf(macho.segment_command_64),
                 },
             },
         });
@@ -4326,6 +4327,7 @@ fn populateMissingMetadata(self: *MachO) !void {
                     .filesize = needed_size,
                     .maxprot = macho.PROT.READ | macho.PROT.EXEC,
                     .initprot = macho.PROT.READ | macho.PROT.EXEC,
+                    .cmdsize = @sizeOf(macho.segment_command_64),
                 },
             },
         });
@@ -4431,6 +4433,7 @@ fn populateMissingMetadata(self: *MachO) !void {
                     .filesize = needed_size,
                     .maxprot = macho.PROT.READ | macho.PROT.WRITE,
                     .initprot = macho.PROT.READ | macho.PROT.WRITE,
+                    .cmdsize = @sizeOf(macho.segment_command_64),
                 },
             },
         });
@@ -4480,6 +4483,7 @@ fn populateMissingMetadata(self: *MachO) !void {
                     .filesize = needed_size,
                     .maxprot = macho.PROT.READ | macho.PROT.WRITE,
                     .initprot = macho.PROT.READ | macho.PROT.WRITE,
+                    .cmdsize = @sizeOf(macho.segment_command_64),
                 },
             },
         });
@@ -4589,6 +4593,7 @@ fn populateMissingMetadata(self: *MachO) !void {
                     .fileoff = fileoff,
                     .maxprot = macho.PROT.READ,
                     .initprot = macho.PROT.READ,
+                    .cmdsize = @sizeOf(macho.segment_command_64),
                 },
             },
         });
src/Sema.zig
@@ -131,6 +131,9 @@ pub const Block = struct {
 
     c_import_buf: ?*std.ArrayList(u8) = null,
 
+    /// type of `err` in `else => |err|`
+    switch_else_err_ty: ?Type = null,
+
     const Param = struct {
         /// `noreturn` means `anytype`.
         ty: Type,
@@ -189,6 +192,7 @@ pub const Block = struct {
             .runtime_index = parent.runtime_index,
             .want_safety = parent.want_safety,
             .c_import_buf = parent.c_import_buf,
+            .switch_else_err_ty = parent.switch_else_err_ty,
         };
     }
 
@@ -6714,12 +6718,6 @@ fn zirSwitchCapture(
 
     if (capture_info.prong_index == std.math.maxInt(@TypeOf(capture_info.prong_index))) {
         // It is the else/`_` prong.
-        switch (operand_ty.zigTypeTag()) {
-            .ErrorSet => {
-                return sema.fail(block, operand_src, "TODO implement Sema for zirSwitchCaptureElse for error sets", .{});
-            },
-            else => {},
-        }
         if (is_ref) {
             assert(operand_is_ref);
             return operand_ptr;
@@ -6730,7 +6728,10 @@ fn zirSwitchCapture(
         else
             operand_ptr;
 
-        return operand;
+        switch (operand_ty.zigTypeTag()) {
+            .ErrorSet => return sema.bitCast(block, block.switch_else_err_ty.?, operand, operand_src),
+            else => return operand,
+        }
     }
 
     if (is_multi) {
@@ -6907,6 +6908,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
 
     const operand_ty = sema.typeOf(operand);
 
+    var else_error_ty: ?Type = null;
+
     // Validate usage of '_' prongs.
     if (special_prong == .under and !operand_ty.isNonexhaustiveEnum()) {
         const msg = msg: {
@@ -7099,6 +7102,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
                         .{},
                     );
                 }
+                else_error_ty = Type.@"anyerror";
             } else {
                 var maybe_msg: ?*Module.ErrorMsg = null;
                 errdefer if (maybe_msg) |msg| msg.destroy(sema.gpa);
@@ -7143,6 +7147,17 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
                         .{},
                     );
                 }
+
+                const error_names = operand_ty.errorSetNames();
+                var names: Module.ErrorSet.NameMap = .{};
+                try names.ensureUnusedCapacity(sema.arena, error_names.len);
+                for (error_names) |error_name| {
+                    if (seen_errors.contains(error_name)) continue;
+
+                    names.putAssumeCapacityNoClobber(error_name, {});
+                }
+
+                else_error_ty = try Type.Tag.error_set_merged.create(sema.arena, names);
             }
         },
         .Union => return sema.fail(block, src, "TODO validate switch .Union", .{}),
@@ -7420,6 +7435,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
         .label = &label,
         .inlining = block.inlining,
         .is_comptime = block.is_comptime,
+        .switch_else_err_ty = else_error_ty,
     };
     const merges = &child_block.label.?.merges;
     defer child_block.instructions.deinit(gpa);
@@ -18523,8 +18539,8 @@ pub fn bitCastVal(
     const abi_size = try sema.usizeCast(block, src, old_ty.abiSize(target));
     const buffer = try sema.gpa.alloc(u8, abi_size);
     defer sema.gpa.free(buffer);
-    val.writeToMemory(old_ty, target, buffer);
-    return Value.readFromMemory(new_ty, target, buffer[buffer_offset..], sema.arena);
+    val.writeToMemory(old_ty, sema.mod, buffer);
+    return Value.readFromMemory(new_ty, sema.mod, buffer[buffer_offset..], sema.arena);
 }
 
 fn coerceArrayPtrToSlice(
src/value.zig
@@ -1042,7 +1042,8 @@ pub const Value = extern union {
         };
     }
 
-    pub fn writeToMemory(val: Value, ty: Type, target: Target, buffer: []u8) void {
+    pub fn writeToMemory(val: Value, ty: Type, mod: *Module, buffer: []u8) void {
+        const target = mod.getTarget();
         if (val.isUndef()) {
             const size = @intCast(usize, ty.abiSize(target));
             std.mem.set(u8, buffer[0..size], 0xaa);
@@ -1081,7 +1082,7 @@ pub const Value = extern union {
                 var buf_off: usize = 0;
                 while (elem_i < len) : (elem_i += 1) {
                     const elem_val = val.elemValueBuffer(elem_i, &elem_value_buf);
-                    writeToMemory(elem_val, elem_ty, target, buffer[buf_off..]);
+                    writeToMemory(elem_val, elem_ty, mod, buffer[buf_off..]);
                     buf_off += elem_size;
                 }
             },
@@ -1092,7 +1093,7 @@ pub const Value = extern union {
                     const field_vals = val.castTag(.aggregate).?.data;
                     for (fields) |field, i| {
                         const off = @intCast(usize, ty.structFieldOffset(i, target));
-                        writeToMemory(field_vals[i], field.ty, target, buffer[off..]);
+                        writeToMemory(field_vals[i], field.ty, mod, buffer[off..]);
                     }
                 },
                 .Packed => {
@@ -1105,6 +1106,12 @@ pub const Value = extern union {
                     host_int.writeTwosComplement(buffer, bit_size, abi_size, target.cpu.arch.endian());
                 },
             },
+            .ErrorSet => {
+                // TODO revisit this when we have the concept of the error tag type
+                const Int = u16;
+                const int = mod.global_error_set.get(val.castTag(.@"error").?.data.name).?;
+                std.mem.writeInt(Int, buffer[0..@sizeOf(Int)], @intCast(Int, int), target.cpu.arch.endian());
+            },
             else => @panic("TODO implement writeToMemory for more types"),
         }
     }
@@ -1153,10 +1160,11 @@ pub const Value = extern union {
 
     pub fn readFromMemory(
         ty: Type,
-        target: Target,
+        mod: *Module,
         buffer: []const u8,
         arena: Allocator,
     ) Allocator.Error!Value {
+        const target = mod.getTarget();
         switch (ty.zigTypeTag()) {
             .Int => {
                 if (buffer.len == 0) return Value.zero;
@@ -1184,7 +1192,7 @@ pub const Value = extern union {
                 const elems = try arena.alloc(Value, @intCast(usize, ty.arrayLen()));
                 var offset: usize = 0;
                 for (elems) |*elem| {
-                    elem.* = try readFromMemory(elem_ty, target, buffer[offset..], arena);
+                    elem.* = try readFromMemory(elem_ty, mod, buffer[offset..], arena);
                     offset += @intCast(usize, elem_size);
                 }
                 return Tag.aggregate.create(arena, elems);
@@ -1196,7 +1204,7 @@ pub const Value = extern union {
                     const field_vals = try arena.alloc(Value, fields.len);
                     for (fields) |field, i| {
                         const off = @intCast(usize, ty.structFieldOffset(i, target));
-                        field_vals[i] = try readFromMemory(field.ty, target, buffer[off..], arena);
+                        field_vals[i] = try readFromMemory(field.ty, mod, buffer[off..], arena);
                     }
                     return Tag.aggregate.create(arena, field_vals);
                 },
@@ -1212,6 +1220,18 @@ pub const Value = extern union {
                     return intToPackedStruct(ty, target, bigint.toConst(), arena);
                 },
             },
+            .ErrorSet => {
+                // TODO revisit this when we have the concept of the error tag type
+                const Int = u16;
+                const int = std.mem.readInt(Int, buffer[0..@sizeOf(Int)], target.cpu.arch.endian());
+
+                const payload = try arena.create(Value.Payload.Error);
+                payload.* = .{
+                    .base = .{ .tag = .@"error" },
+                    .data = .{ .name = mod.error_name_list.items[@intCast(usize, int)] },
+                };
+                return Value.initPayload(&payload.base);
+            },
             else => @panic("TODO implement readFromMemory for more types"),
         }
     }
test/behavior/switch.zig
@@ -430,7 +430,11 @@ test "switch on integer with else capturing expr" {
 }
 
 test "else prong of switch on error set excludes other cases" {
-    if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+    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_c) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
 
     const S = struct {
         fn doTheTest() !void {