From 0e3d74df8a8c06bcc0b1e4bff205f4ab8e4c6c13 Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Fri, 26 Jun 2020 00:06:23 -0700 Subject: [PATCH 01/19] Add tests for using file operations on directories --- lib/std/fs/test.zig | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index b024f0f4f6..87477549d9 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -73,6 +73,38 @@ test "directory operations on files" { file.close(); } +test "file operations on directories" { + var tmp_dir = tmpDir(.{}); + defer tmp_dir.cleanup(); + + const test_dir_name = "test_dir"; + + try tmp_dir.dir.makeDir(test_dir_name); + + testing.expectError(error.IsDir, tmp_dir.dir.createFile(test_dir_name, .{})); + testing.expectError(error.IsDir, tmp_dir.dir.deleteFile(test_dir_name)); + testing.expectError(error.IsDir, tmp_dir.dir.readFileAlloc(testing.allocator, test_dir_name, std.math.maxInt(usize))); + // note: the `.write = true` is necessary to ensure the error occurs on all platforms + testing.expectError(error.IsDir, tmp_dir.dir.openFile(test_dir_name, .{ .write = true })); + + if (builtin.os.tag != .wasi) { + // TODO: use Dir's realpath function once that exists + const absolute_path = blk: { + const relative_path = try fs.path.join(testing.allocator, &[_][]const u8{ "zig-cache", "tmp", tmp_dir.sub_path[0..], test_dir_name }); + defer testing.allocator.free(relative_path); + break :blk try fs.realpathAlloc(testing.allocator, relative_path); + }; + defer testing.allocator.free(absolute_path); + + testing.expectError(error.IsDir, fs.createFileAbsolute(absolute_path, .{})); + testing.expectError(error.IsDir, fs.deleteFileAbsolute(absolute_path)); + } + + // ensure the directory still exists as a sanity check + var dir = try tmp_dir.dir.openDir(test_dir_name, .{}); + dir.close(); +} + test "openSelfExe" { if (builtin.os.tag == .wasi) return error.SkipZigTest; From 14c3c47fb7315e6199751082f9ef544972bb13e6 Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Fri, 26 Jun 2020 15:49:31 -0700 Subject: [PATCH 02/19] fs.deleteFile: Translate to error.IsDir when appropriate on POSIX systems Linux deviates from POSIX and returns EISDIR while other POSIX systems return EPERM. To make all platforms consistent in their errors when calling deleteFile on a directory, we have to do a stat to translate EPERM (AccessDenied) to EISDIR (IsDir). --- lib/std/fs.zig | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 6cb7d478b2..c574737194 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -1112,6 +1112,16 @@ pub const Dir = struct { pub fn deleteFile(self: Dir, sub_path: []const u8) DeleteFileError!void { os.unlinkat(self.fd, sub_path, 0) catch |err| switch (err) { error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR + error.AccessDenied => |e| switch (builtin.os.tag) { + // non-Linux POSIX systems return EPERM when trying to delete a directory, so + // we need to handle that case specifically and translate the error + .macosx, .ios, .freebsd, .netbsd, .dragonfly => { + const fstat = os.fstatat(self.fd, sub_path, 0) catch return e; + const is_dir = fstat.mode & os.S_IFMT == os.S_IFDIR; + return if (is_dir) error.IsDir else e; + }, + else => return e, + }, else => |e| return e, }; } @@ -1122,6 +1132,16 @@ pub const Dir = struct { pub fn deleteFileZ(self: Dir, sub_path_c: [*:0]const u8) DeleteFileError!void { os.unlinkatZ(self.fd, sub_path_c, 0) catch |err| switch (err) { error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR + error.AccessDenied => |e| switch (builtin.os.tag) { + // non-Linux POSIX systems return EPERM when trying to delete a directory, so + // we need to handle that case specifically and translate the error + .macosx, .ios, .freebsd, .netbsd, .dragonfly => { + const fstat = os.fstatatZ(self.fd, sub_path_c, 0) catch return e; + const is_dir = fstat.mode & os.S_IFMT == os.S_IFDIR; + return if (is_dir) error.IsDir else e; + }, + else => return e, + }, else => |e| return e, }; } From 505bc9817a2b78c97819f481b3cd98102c7ff964 Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Fri, 26 Jun 2020 16:08:26 -0700 Subject: [PATCH 03/19] Implement Dir.deleteFile in terms of deleteFileZ/deleteFileW Reduces duplicate code, consistent with other fn/fnZ/fnW implementations --- lib/std/fs.zig | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/lib/std/fs.zig b/lib/std/fs.zig index c574737194..e7de8bd52d 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -1110,20 +1110,18 @@ pub const Dir = struct { /// Delete a file name and possibly the file it refers to, based on an open directory handle. /// Asserts that the path parameter has no null bytes. pub fn deleteFile(self: Dir, sub_path: []const u8) DeleteFileError!void { - os.unlinkat(self.fd, sub_path, 0) catch |err| switch (err) { - error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR - error.AccessDenied => |e| switch (builtin.os.tag) { - // non-Linux POSIX systems return EPERM when trying to delete a directory, so - // we need to handle that case specifically and translate the error - .macosx, .ios, .freebsd, .netbsd, .dragonfly => { - const fstat = os.fstatat(self.fd, sub_path, 0) catch return e; - const is_dir = fstat.mode & os.S_IFMT == os.S_IFDIR; - return if (is_dir) error.IsDir else e; - }, - else => return e, - }, - else => |e| return e, - }; + if (builtin.os.tag == .windows) { + const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); + return self.deleteFileW(sub_path_w.span().ptr); + } else if (builtin.os.tag == .wasi) { + os.unlinkatWasi(self.fd, sub_path, 0) catch |err| switch (err) { + error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR + else => |e| return e, + }; + } else { + const sub_path_c = try os.toPosixPath(sub_path); + return self.deleteFileZ(&sub_path_c); + } } pub const deleteFileC = @compileError("deprecated: renamed to deleteFileZ"); From 12aca758c6ee923065d1d64a62e44c8a8fd6cd99 Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Fri, 26 Jun 2020 17:12:02 -0700 Subject: [PATCH 04/19] Dir.deleteFile: Fix symlink behavior when translating EPERM to EISDIR --- lib/std/fs.zig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/std/fs.zig b/lib/std/fs.zig index e7de8bd52d..28f2daa96a 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -1134,7 +1134,8 @@ pub const Dir = struct { // non-Linux POSIX systems return EPERM when trying to delete a directory, so // we need to handle that case specifically and translate the error .macosx, .ios, .freebsd, .netbsd, .dragonfly => { - const fstat = os.fstatatZ(self.fd, sub_path_c, 0) catch return e; + // Don't follow symlinks to match unlinkat (which acts on symlinks rather than follows them) + const fstat = os.fstatatZ(self.fd, sub_path_c, os.AT_SYMLINK_NOFOLLOW) catch return e; const is_dir = fstat.mode & os.S_IFMT == os.S_IFDIR; return if (is_dir) error.IsDir else e; }, From 74c245aea94ebcf05651a6f3420d8c3a7145f9d7 Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Sun, 28 Jun 2020 13:56:00 -0700 Subject: [PATCH 05/19] Disable wasi 'readFileAlloc on a directory' assertion for now --- lib/std/fs/test.zig | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 87477549d9..3991ddd8a3 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -83,8 +83,13 @@ test "file operations on directories" { testing.expectError(error.IsDir, tmp_dir.dir.createFile(test_dir_name, .{})); testing.expectError(error.IsDir, tmp_dir.dir.deleteFile(test_dir_name)); - testing.expectError(error.IsDir, tmp_dir.dir.readFileAlloc(testing.allocator, test_dir_name, std.math.maxInt(usize))); - // note: the `.write = true` is necessary to ensure the error occurs on all platforms + // Currently, WASI will return error.Unexpected (via ENOTCAPABLE) when attempting fd_read on a directory handle. + // TODO: Re-enable on WASI once https://github.com/bytecodealliance/wasmtime/issues/1935 is resolved. + if (builtin.os.tag != .wasi) { + testing.expectError(error.IsDir, tmp_dir.dir.readFileAlloc(testing.allocator, test_dir_name, std.math.maxInt(usize))); + } + // Note: The `.write = true` is necessary to ensure the error occurs on all platforms. + // TODO: Add a read-only test as well, see https://github.com/ziglang/zig/issues/5732 testing.expectError(error.IsDir, tmp_dir.dir.openFile(test_dir_name, .{ .write = true })); if (builtin.os.tag != .wasi) { From c2eead9629b60a394aa61e6f96b89647eddce1ea Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Sun, 28 Jun 2020 14:33:41 -0600 Subject: [PATCH 06/19] Fix issue 5741, use after free --- lib/std/heap.zig | 5 +++++ lib/std/mem.zig | 3 --- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/std/heap.zig b/lib/std/heap.zig index 260841ad2d..ea9e95c675 100644 --- a/lib/std/heap.zig +++ b/lib/std/heap.zig @@ -714,6 +714,11 @@ test "PageAllocator" { slice[127] = 0x34; allocator.free(slice); } + { + var buf = try allocator.alloc(u8, mem.page_size + 1); + defer allocator.free(buf); + buf = try allocator.realloc(buf, 1); // shrink past the page boundary + } } test "HeapAllocator" { diff --git a/lib/std/mem.zig b/lib/std/mem.zig index bf1e000056..6bde83f782 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -116,9 +116,6 @@ pub const Allocator = struct { if (isAligned(@ptrToInt(old_mem.ptr), new_alignment)) { if (new_byte_count <= old_mem.len) { const shrunk_len = self.shrinkBytes(old_mem, new_byte_count, len_align); - if (shrunk_len < old_mem.len) { - @memset(old_mem.ptr + shrunk_len, undefined, old_mem.len - shrunk_len); - } return old_mem.ptr[0..shrunk_len]; } if (self.callResizeFn(old_mem, new_byte_count, len_align)) |resized_len| { From e120b07a524f1accd4975f5206018c61c4be9345 Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Sat, 27 Jun 2020 20:36:56 -0600 Subject: [PATCH 07/19] arena_allocator: refactor and use full capacity --- lib/std/heap/arena_allocator.zig | 48 +++++++++++++++----------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/lib/std/heap/arena_allocator.zig b/lib/std/heap/arena_allocator.zig index 74ced774ed..31fbad57f9 100644 --- a/lib/std/heap/arena_allocator.zig +++ b/lib/std/heap/arena_allocator.zig @@ -15,6 +15,7 @@ pub const ArenaAllocator = struct { /// as a memory-saving optimization. pub const State = struct { buffer_list: std.SinglyLinkedList([]u8) = @as(std.SinglyLinkedList([]u8), .{}), + /// The first available index in the front buffer of `buffer_list` end_index: usize = 0, pub fn promote(self: State, child_allocator: *Allocator) ArenaAllocator { @@ -45,39 +46,36 @@ pub const ArenaAllocator = struct { } } - fn createNode(self: *ArenaAllocator, prev_len: usize, minimum_size: usize) !*BufNode { - const actual_min_size = minimum_size + (@sizeOf(BufNode) + 16); - const big_enough_len = prev_len + actual_min_size; - const len = big_enough_len + big_enough_len / 2; - const buf = try self.child_allocator.alignedAlloc(u8, @alignOf(BufNode), len); - const buf_node_slice = mem.bytesAsSlice(BufNode, buf[0..@sizeOf(BufNode)]); - const buf_node = &buf_node_slice[0]; - buf_node.* = BufNode{ - .data = buf, - .next = null, - }; + fn getBufNodeAddr(buf: []u8) usize { + return mem.alignBackward(@ptrToInt(buf.ptr) + buf.len - @sizeOf(BufNode), @alignOf(BufNode)); + } + + fn allocBuf(self: *ArenaAllocator, len: usize, ptr_align: u29) ![]u8 { + const alloc_len = len + @sizeOf(BufNode) + @alignOf(BufNode) - 1; + const buf = try self.child_allocator.callAllocFn(alloc_len, ptr_align, 1); + const buf_node = @intToPtr(*BufNode, getBufNodeAddr(buf)); + buf_node.* = .{ .data = buf, .next = null }; + assert(@ptrToInt(buf_node) - @ptrToInt(buf.ptr) >= len); self.state.buffer_list.prepend(buf_node); - self.state.end_index = 0; - return buf_node; + self.state.end_index = len; + return buf[0..len]; } fn alloc(allocator: *Allocator, n: usize, ptr_align: u29, len_align: u29) ![]u8 { const self = @fieldParentPtr(ArenaAllocator, "allocator", allocator); - var cur_node = if (self.state.buffer_list.first) |first_node| first_node else try self.createNode(0, n + ptr_align); - while (true) { - const cur_buf = cur_node.data[@sizeOf(BufNode)..]; + if (self.state.buffer_list.first) |node_full_buf| { + assert(self.state.end_index > 0); + const cur_buf = node_full_buf.data[0.. + getBufNodeAddr(node_full_buf.data) - @ptrToInt(node_full_buf.data.ptr)]; const addr = @ptrToInt(cur_buf.ptr) + self.state.end_index; - const adjusted_addr = mem.alignForward(addr, ptr_align); - const adjusted_index = self.state.end_index + (adjusted_addr - addr); - const new_end_index = adjusted_index + n; - if (new_end_index > cur_buf.len) { - cur_node = try self.createNode(cur_buf.len, n + ptr_align); - continue; + const aligned_index = self.state.end_index + (mem.alignForward(addr, ptr_align) - addr); + const aligned_end_index = aligned_index + n; + if (aligned_end_index <= cur_buf.len) { + self.state.end_index = aligned_end_index; + return cur_buf[aligned_index..aligned_end_index]; } - const result = cur_buf[adjusted_index..new_end_index]; - self.state.end_index = new_end_index; - return result; } + return try self.allocBuf(n, ptr_align); } }; From aa92446365b97e167c4162122c66a02b4cbca031 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 28 Jun 2020 19:39:54 -0400 Subject: [PATCH 08/19] stage2: implement function parameters In codegen.zig, the std.Target.Cpu.Arch is now generally available as a comptime value where needed. This is a tradeoff that causes the compiler binary to be more bloated, but gives us higher performance, since the optimizer can optimize per architecture (which is usually how compilers are designed anyway, with different code per-architecture), and it also allows us to use per-architecture types, such as a Register enum that is specific to the comptime-known architecture. Adds abiSize method to Type. --- src-self-hosted/codegen.zig | 307 ++++++++++++++++++----------- src-self-hosted/codegen/x86_64.zig | 30 ++- src-self-hosted/type.zig | 70 +++++++ 3 files changed, 291 insertions(+), 116 deletions(-) diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index 686edda373..ccd0da8ae0 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -37,6 +37,69 @@ pub fn generateSymbol( .Fn => { const module_fn = typed_value.val.cast(Value.Payload.Function).?.func; + const fn_type = module_fn.owner_decl.typed_value.most_recent.typed_value.ty; + const param_types = try bin_file.allocator.alloc(Type, fn_type.fnParamLen()); + defer bin_file.allocator.free(param_types); + fn_type.fnParamTypes(param_types); + // A parameter may be broken into multiple machine code parameters, so we don't + // know the size up front. + var mc_args = try std.ArrayList(Function.MCValue).initCapacity(bin_file.allocator, param_types.len); + defer mc_args.deinit(); + + var next_stack_offset: u64 = 0; + + switch (fn_type.fnCallingConvention()) { + .Naked => assert(mc_args.items.len == 0), + .Unspecified, .C => { + // Prepare the function parameters + switch (bin_file.options.target.cpu.arch) { + .x86_64 => { + const integer_registers = [_]Reg(.x86_64){.rdi, .rsi, .rdx, .rcx, .r8, .r9}; + var next_int_reg: usize = 0; + + for (param_types) |param_type, src_i| { + switch (param_type.zigTypeTag()) { + .Bool, .Int => { + if (next_int_reg >= integer_registers.len) { + try mc_args.append(.{ .stack_offset = next_stack_offset }); + next_stack_offset += param_type.abiSize(bin_file.options.target); + } else { + try mc_args.append(.{ .register = @enumToInt(integer_registers[next_int_reg])}); + next_int_reg += 1; + } + }, + else => return Result{ + .fail = try ErrorMsg.create( + bin_file.allocator, + src, + "TODO implement function parameters of type {}", + .{@tagName(param_type.zigTypeTag())}, + ), + } + } + } + + }, + else => return Result{ + .fail = try ErrorMsg.create( + bin_file.allocator, + src, + "TODO implement function parameters for {}", + .{bin_file.options.target.cpu.arch}, + ), + }, + } + }, + else => return Result{ + .fail = try ErrorMsg.create( + bin_file.allocator, + src, + "TODO implement {} calling convention", + .{fn_type.fnCallingConvention()}, + ), + }, + } + var function = Function{ .target = &bin_file.options.target, .bin_file = bin_file, @@ -44,16 +107,14 @@ pub fn generateSymbol( .code = code, .inst_table = std.AutoHashMap(*ir.Inst, Function.MCValue).init(bin_file.allocator), .err_msg = null, + .args = mc_args.items, }; defer function.inst_table.deinit(); - for (module_fn.analysis.success.instructions) |inst| { - const new_inst = function.genFuncInst(inst) catch |err| switch (err) { - error.CodegenFail => return Result{ .fail = function.err_msg.? }, - else => |e| return e, - }; - try function.inst_table.putNoClobber(inst, new_inst); - } + function.gen() catch |err| switch (err) { + error.CodegenFail => return Result{ .fail = function.err_msg.? }, + else => |e| return e, + }; if (function.err_msg) |em| { return Result{ .fail = em }; @@ -157,6 +218,7 @@ const Function = struct { code: *std.ArrayList(u8), inst_table: std.AutoHashMap(*ir.Inst, MCValue), err_msg: ?*ErrorMsg, + args: []MCValue, const MCValue = union(enum) { none, @@ -170,44 +232,119 @@ const Function = struct { register: usize, /// The value is in memory at a hard-coded address. memory: u64, + /// The value is one of the stack variables. + stack_offset: u64, }; - fn genFuncInst(self: *Function, inst: *ir.Inst) !MCValue { - switch (inst.tag) { - .add => return self.genAdd(inst.cast(ir.Inst.Add).?), - .arg => return self.genArg(inst.src), - .block => return self.genBlock(inst.cast(ir.Inst.Block).?), - .breakpoint => return self.genBreakpoint(inst.src), - .call => return self.genCall(inst.cast(ir.Inst.Call).?), - .unreach => return MCValue{ .unreach = {} }, - .constant => unreachable, // excluded from function bodies - .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?), - .ptrtoint => return self.genPtrToInt(inst.cast(ir.Inst.PtrToInt).?), - .bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?), - .ret => return self.genRet(inst.cast(ir.Inst.Ret).?), - .retvoid => return self.genRetVoid(inst.cast(ir.Inst.RetVoid).?), - .cmp => return self.genCmp(inst.cast(ir.Inst.Cmp).?), - .condbr => return self.genCondBr(inst.cast(ir.Inst.CondBr).?), - .isnull => return self.genIsNull(inst.cast(ir.Inst.IsNull).?), - .isnonnull => return self.genIsNonNull(inst.cast(ir.Inst.IsNonNull).?), + fn gen(self: *Function) !void { + switch (self.target.cpu.arch) { + .arm => return self.genArch(.arm), + .armeb => return self.genArch(.armeb), + .aarch64 => return self.genArch(.aarch64), + .aarch64_be => return self.genArch(.aarch64_be), + .aarch64_32 => return self.genArch(.aarch64_32), + .arc => return self.genArch(.arc), + .avr => return self.genArch(.avr), + .bpfel => return self.genArch(.bpfel), + .bpfeb => return self.genArch(.bpfeb), + .hexagon => return self.genArch(.hexagon), + .mips => return self.genArch(.mips), + .mipsel => return self.genArch(.mipsel), + .mips64 => return self.genArch(.mips64), + .mips64el => return self.genArch(.mips64el), + .msp430 => return self.genArch(.msp430), + .powerpc => return self.genArch(.powerpc), + .powerpc64 => return self.genArch(.powerpc64), + .powerpc64le => return self.genArch(.powerpc64le), + .r600 => return self.genArch(.r600), + .amdgcn => return self.genArch(.amdgcn), + .riscv32 => return self.genArch(.riscv32), + .riscv64 => return self.genArch(.riscv64), + .sparc => return self.genArch(.sparc), + .sparcv9 => return self.genArch(.sparcv9), + .sparcel => return self.genArch(.sparcel), + .s390x => return self.genArch(.s390x), + .tce => return self.genArch(.tce), + .tcele => return self.genArch(.tcele), + .thumb => return self.genArch(.thumb), + .thumbeb => return self.genArch(.thumbeb), + .i386 => return self.genArch(.i386), + .x86_64 => return self.genArch(.x86_64), + .xcore => return self.genArch(.xcore), + .nvptx => return self.genArch(.nvptx), + .nvptx64 => return self.genArch(.nvptx64), + .le32 => return self.genArch(.le32), + .le64 => return self.genArch(.le64), + .amdil => return self.genArch(.amdil), + .amdil64 => return self.genArch(.amdil64), + .hsail => return self.genArch(.hsail), + .hsail64 => return self.genArch(.hsail64), + .spir => return self.genArch(.spir), + .spir64 => return self.genArch(.spir64), + .kalimba => return self.genArch(.kalimba), + .shave => return self.genArch(.shave), + .lanai => return self.genArch(.lanai), + .wasm32 => return self.genArch(.wasm32), + .wasm64 => return self.genArch(.wasm64), + .renderscript32 => return self.genArch(.renderscript32), + .renderscript64 => return self.genArch(.renderscript64), + .ve => return self.genArch(.ve), } } - fn genAdd(self: *Function, inst: *ir.Inst.Add) !MCValue { - switch (self.target.cpu.arch) { + fn genArch(self: *Function, comptime arch: std.Target.Cpu.Arch) !void { + for (self.mod_fn.analysis.success.instructions) |inst| { + const new_inst = try self.genFuncInst(inst, arch); + try self.inst_table.putNoClobber(inst, new_inst); + } + } + + fn genFuncInst(self: *Function, inst: *ir.Inst, comptime arch: std.Target.Cpu.Arch) !MCValue { + switch (inst.tag) { + .add => return self.genAdd(inst.cast(ir.Inst.Add).?, arch), + .arg => return self.genArg(inst.cast(ir.Inst.Arg).?), + .block => return self.genBlock(inst.cast(ir.Inst.Block).?, arch), + .breakpoint => return self.genBreakpoint(inst.src, arch), + .call => return self.genCall(inst.cast(ir.Inst.Call).?, arch), + .unreach => return MCValue{ .unreach = {} }, + .constant => unreachable, // excluded from function bodies + .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?, arch), + .ptrtoint => return self.genPtrToInt(inst.cast(ir.Inst.PtrToInt).?), + .bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?), + .ret => return self.genRet(inst.cast(ir.Inst.Ret).?, arch), + .retvoid => return self.genRetVoid(inst.cast(ir.Inst.RetVoid).?, arch), + .cmp => return self.genCmp(inst.cast(ir.Inst.Cmp).?, arch), + .condbr => return self.genCondBr(inst.cast(ir.Inst.CondBr).?, arch), + .isnull => return self.genIsNull(inst.cast(ir.Inst.IsNull).?, arch), + .isnonnull => return self.genIsNonNull(inst.cast(ir.Inst.IsNonNull).?, arch), + } + } + + fn genAdd(self: *Function, inst: *ir.Inst.Add, comptime arch: std.Target.Cpu.Arch) !MCValue { + const lhs = try self.resolveInst(inst.args.lhs); + const rhs = try self.resolveInst(inst.args.rhs); + switch (arch) { + .i386, .x86_64 => { + // const lhs_reg = try self.instAsReg(lhs); + // const rhs_reg = try self.instAsReg(rhs); + // const result = try self.allocateReg(); + + // try self.code.append(??); + + // lhs_reg.release(); + // rhs_reg.release(); + return self.fail(inst.base.src, "TODO implement register allocation", .{}); + }, else => return self.fail(inst.base.src, "TODO implement add for {}", .{self.target.cpu.arch}), } } - fn genArg(self: *Function, src: usize) !MCValue { - switch (self.target.cpu.arch) { - else => return self.fail(src, "TODO implement function parameters for {}", .{self.target.cpu.arch}), - } - return .none; + fn genArg(self: *Function, inst: *ir.Inst.Arg) !MCValue { + return self.args[inst.args.index]; } - fn genBreakpoint(self: *Function, src: usize) !MCValue { - switch (self.target.cpu.arch) { + fn genBreakpoint(self: *Function, src: usize, comptime arch: std.Target.Cpu.Arch) !MCValue { + switch (arch) { .i386, .x86_64 => { try self.code.append(0xcc); // int3 }, @@ -216,8 +353,8 @@ const Function = struct { return .none; } - fn genCall(self: *Function, inst: *ir.Inst.Call) !MCValue { - switch (self.target.cpu.arch) { + fn genCall(self: *Function, inst: *ir.Inst.Call, comptime arch: std.Target.Cpu.Arch) !MCValue { + switch (arch) { .x86_64, .i386 => { if (inst.args.func.cast(ir.Inst.Constant)) |func_inst| { if (inst.args.args.len != 0) { @@ -251,11 +388,11 @@ const Function = struct { } } - fn ret(self: *Function, src: usize, mcv: MCValue) !MCValue { + fn ret(self: *Function, src: usize, comptime arch: std.Target.Cpu.Arch, mcv: MCValue) !MCValue { if (mcv != .none) { return self.fail(src, "TODO implement return with non-void operand", .{}); } - switch (self.target.cpu.arch) { + switch (arch) { .i386, .x86_64 => { try self.code.append(0xc3); // ret }, @@ -264,43 +401,43 @@ const Function = struct { return .unreach; } - fn genRet(self: *Function, inst: *ir.Inst.Ret) !MCValue { + fn genRet(self: *Function, inst: *ir.Inst.Ret, comptime arch: std.Target.Cpu.Arch) !MCValue { const operand = try self.resolveInst(inst.args.operand); - return self.ret(inst.base.src, operand); + return self.ret(inst.base.src, arch, operand); } - fn genRetVoid(self: *Function, inst: *ir.Inst.RetVoid) !MCValue { - return self.ret(inst.base.src, .none); + fn genRetVoid(self: *Function, inst: *ir.Inst.RetVoid, comptime arch: std.Target.Cpu.Arch) !MCValue { + return self.ret(inst.base.src, arch, .none); } - fn genCmp(self: *Function, inst: *ir.Inst.Cmp) !MCValue { - switch (self.target.cpu.arch) { + fn genCmp(self: *Function, inst: *ir.Inst.Cmp, comptime arch: std.Target.Cpu.Arch) !MCValue { + switch (arch) { else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}), } } - fn genCondBr(self: *Function, inst: *ir.Inst.CondBr) !MCValue { - switch (self.target.cpu.arch) { + fn genCondBr(self: *Function, inst: *ir.Inst.CondBr, comptime arch: std.Target.Cpu.Arch) !MCValue { + switch (arch) { else => return self.fail(inst.base.src, "TODO implement condbr for {}", .{self.target.cpu.arch}), } } - fn genIsNull(self: *Function, inst: *ir.Inst.IsNull) !MCValue { - switch (self.target.cpu.arch) { + fn genIsNull(self: *Function, inst: *ir.Inst.IsNull, comptime arch: std.Target.Cpu.Arch) !MCValue { + switch (arch) { else => return self.fail(inst.base.src, "TODO implement isnull for {}", .{self.target.cpu.arch}), } } - fn genIsNonNull(self: *Function, inst: *ir.Inst.IsNonNull) !MCValue { + fn genIsNonNull(self: *Function, inst: *ir.Inst.IsNonNull, comptime arch: std.Target.Cpu.Arch) !MCValue { // Here you can specialize this instruction if it makes sense to, otherwise the default // will call genIsNull and invert the result. - switch (self.target.cpu.arch) { + switch (arch) { else => return self.fail(inst.base.src, "TODO call genIsNull and invert the result ", .{}), } } - fn genRelativeFwdJump(self: *Function, src: usize, amount: u32) !void { - switch (self.target.cpu.arch) { + fn genRelativeFwdJump(self: *Function, src: usize, comptime arch: std.Target.Cpu.Arch, amount: u32) !void { + switch (arch) { .i386, .x86_64 => { // TODO x86 treats the operands as signed if (amount <= std.math.maxInt(u8)) { @@ -318,70 +455,13 @@ const Function = struct { } } - fn genBlock(self: *Function, inst: *ir.Inst.Block) !MCValue { - switch (self.target.cpu.arch) { + fn genBlock(self: *Function, inst: *ir.Inst.Block, comptime arch: std.Target.Cpu.Arch) !MCValue { + switch (arch) { else => return self.fail(inst.base.src, "TODO implement codegen Block for {}", .{self.target.cpu.arch}), } } - fn genAsm(self: *Function, inst: *ir.Inst.Assembly) !MCValue { - // TODO convert to inline function - switch (self.target.cpu.arch) { - .arm => return self.genAsmArch(.arm, inst), - .armeb => return self.genAsmArch(.armeb, inst), - .aarch64 => return self.genAsmArch(.aarch64, inst), - .aarch64_be => return self.genAsmArch(.aarch64_be, inst), - .aarch64_32 => return self.genAsmArch(.aarch64_32, inst), - .arc => return self.genAsmArch(.arc, inst), - .avr => return self.genAsmArch(.avr, inst), - .bpfel => return self.genAsmArch(.bpfel, inst), - .bpfeb => return self.genAsmArch(.bpfeb, inst), - .hexagon => return self.genAsmArch(.hexagon, inst), - .mips => return self.genAsmArch(.mips, inst), - .mipsel => return self.genAsmArch(.mipsel, inst), - .mips64 => return self.genAsmArch(.mips64, inst), - .mips64el => return self.genAsmArch(.mips64el, inst), - .msp430 => return self.genAsmArch(.msp430, inst), - .powerpc => return self.genAsmArch(.powerpc, inst), - .powerpc64 => return self.genAsmArch(.powerpc64, inst), - .powerpc64le => return self.genAsmArch(.powerpc64le, inst), - .r600 => return self.genAsmArch(.r600, inst), - .amdgcn => return self.genAsmArch(.amdgcn, inst), - .riscv32 => return self.genAsmArch(.riscv32, inst), - .riscv64 => return self.genAsmArch(.riscv64, inst), - .sparc => return self.genAsmArch(.sparc, inst), - .sparcv9 => return self.genAsmArch(.sparcv9, inst), - .sparcel => return self.genAsmArch(.sparcel, inst), - .s390x => return self.genAsmArch(.s390x, inst), - .tce => return self.genAsmArch(.tce, inst), - .tcele => return self.genAsmArch(.tcele, inst), - .thumb => return self.genAsmArch(.thumb, inst), - .thumbeb => return self.genAsmArch(.thumbeb, inst), - .i386 => return self.genAsmArch(.i386, inst), - .x86_64 => return self.genAsmArch(.x86_64, inst), - .xcore => return self.genAsmArch(.xcore, inst), - .nvptx => return self.genAsmArch(.nvptx, inst), - .nvptx64 => return self.genAsmArch(.nvptx64, inst), - .le32 => return self.genAsmArch(.le32, inst), - .le64 => return self.genAsmArch(.le64, inst), - .amdil => return self.genAsmArch(.amdil, inst), - .amdil64 => return self.genAsmArch(.amdil64, inst), - .hsail => return self.genAsmArch(.hsail, inst), - .hsail64 => return self.genAsmArch(.hsail64, inst), - .spir => return self.genAsmArch(.spir, inst), - .spir64 => return self.genAsmArch(.spir64, inst), - .kalimba => return self.genAsmArch(.kalimba, inst), - .shave => return self.genAsmArch(.shave, inst), - .lanai => return self.genAsmArch(.lanai, inst), - .wasm32 => return self.genAsmArch(.wasm32, inst), - .wasm64 => return self.genAsmArch(.wasm64, inst), - .renderscript32 => return self.genAsmArch(.renderscript32, inst), - .renderscript64 => return self.genAsmArch(.renderscript64, inst), - .ve => return self.genAsmArch(.ve, inst), - } - } - - fn genAsmArch(self: *Function, comptime arch: Target.Cpu.Arch, inst: *ir.Inst.Assembly) !MCValue { + fn genAsm(self: *Function, inst: *ir.Inst.Assembly, comptime arch: Target.Cpu.Arch) !MCValue { if (arch != .x86_64 and arch != .i386) { return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{}); } @@ -607,6 +687,9 @@ const Function = struct { } } }, + .stack_offset => |off| { + return self.fail(src, "TODO implement genSetReg for stack variables", .{}); + } }, else => return self.fail(src, "TODO implement genSetReg for more architectures", .{}), } diff --git a/src-self-hosted/codegen/x86_64.zig b/src-self-hosted/codegen/x86_64.zig index 0326f99b99..8bf131dbdd 100644 --- a/src-self-hosted/codegen/x86_64.zig +++ b/src-self-hosted/codegen/x86_64.zig @@ -1,20 +1,21 @@ +const Type = @import("../Type.zig"); + // zig fmt: off -/// Definitions of all of the x64 registers. The order is very, very important. +/// Definitions of all of the x64 registers. The order is semantically meaningful. /// The registers are defined such that IDs go in descending order of 64-bit, /// 32-bit, 16-bit, and then 8-bit, and each set contains exactly sixteen -/// registers. This results in some very, very useful properties: +/// registers. This results in some useful properties: /// /// Any 64-bit register can be turned into its 32-bit form by adding 16, and /// vice versa. This also works between 32-bit and 16-bit forms. With 8-bit, it -/// works for all except for sp, bp, si, and di, which don't *have* an 8-bit +/// works for all except for sp, bp, si, and di, which do *not* have an 8-bit /// form. /// /// If (register & 8) is set, the register is extended. /// /// The ID can be easily determined by figuring out what range the register is /// in, and then subtracting the base. -/// pub const Register = enum(u8) { // 0 through 15, 64-bit registers. 8-15 are extended. // id is just the int value. @@ -67,3 +68,24 @@ pub const Register = enum(u8) { }; // zig fmt: on + +/// After argument values have been computed, they are placed either in registers +/// or pushed on the stack. The way values are passed depends on the class. +pub const ParameterClass = enum { + /// Integral types that fit into one of the general purpose registers. + integer, + /// Types that fit into a vector register. + sse, + /// Types that fit into a vector register and can be passed + /// and returned in the upper bytes of it. + sse_up, + /// Types that will be returned via the x87FPU. + x87, + /// Types that will be returned via the x87FPU and can be passed and returned + /// in the upper bytes of it. + x87_up, + /// Types that will be returned via the x87FPU. + complex_x87, + /// Types that will be passed and returned in mem-ory via the stack. + memory, +}; diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index 2a02d0d89b..caacf4e7fc 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -535,6 +535,76 @@ pub const Type = extern union { }; } + /// Asserts the type has the ABI size already resolved. + pub fn abiSize(self: Type, target: Target) u64 { + return switch (self.tag()) { + .fn_noreturn_no_args => unreachable, // represents machine code; not a pointer + .fn_void_no_args => unreachable, // represents machine code; not a pointer + .fn_naked_noreturn_no_args => unreachable, // represents machine code; not a pointer + .fn_ccc_void_no_args => unreachable, // represents machine code; not a pointer + .function => unreachable, // represents machine code; not a pointer + .c_void => unreachable, + .void => unreachable, + .type => unreachable, + .comptime_int => unreachable, + .comptime_float => unreachable, + .noreturn => unreachable, + .@"null" => unreachable, + .@"undefined" => unreachable, + + .u8, + .i8, + .bool, + => return 1, + + .array_u8_sentinel_0 => @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", self.ptr_otherwise).len, + .array => { + const payload = @fieldParentPtr(Payload.Array, "base", self.ptr_otherwise); + const elem_size = std.math.max(payload.elem_type.abiAlignment(target), payload.elem_type.abiSize(target)); + return payload.len * elem_size; + }, + .i16, .u16 => return 2, + .i32, .u32 => return 4, + .i64, .u64 => return 8, + + .isize, + .usize, + .single_const_pointer_to_comptime_int, + .const_slice_u8, + .single_const_pointer, + => return @divExact(target.cpu.arch.ptrBitWidth(), 8), + + .c_short => return @divExact(CType.short.sizeInBits(target), 8), + .c_ushort => return @divExact(CType.ushort.sizeInBits(target), 8), + .c_int => return @divExact(CType.int.sizeInBits(target), 8), + .c_uint => return @divExact(CType.uint.sizeInBits(target), 8), + .c_long => return @divExact(CType.long.sizeInBits(target), 8), + .c_ulong => return @divExact(CType.ulong.sizeInBits(target), 8), + .c_longlong => return @divExact(CType.longlong.sizeInBits(target), 8), + .c_ulonglong => return @divExact(CType.ulonglong.sizeInBits(target), 8), + + .f16 => return 2, + .f32 => return 4, + .f64 => return 8, + .f128 => return 16, + .c_longdouble => return 16, + + .anyerror => return 2, // TODO revisit this when we have the concept of the error tag type + + + .int_signed, .int_unsigned => { + const bits: u16 = if (self.cast(Payload.IntSigned)) |pl| + pl.bits + else if (self.cast(Payload.IntUnsigned)) |pl| + pl.bits + else + unreachable; + + return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8); + }, + }; + } + pub fn isSinglePointer(self: Type) bool { return switch (self.tag()) { .u8, From 1eed0cf0f328372ddd8938ad3a95e37cb15ce6fb Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 28 Jun 2020 19:44:50 -0400 Subject: [PATCH 09/19] zig fmt and delete unused type --- src-self-hosted/codegen.zig | 11 +++++------ src-self-hosted/codegen/x86_64.zig | 23 +---------------------- 2 files changed, 6 insertions(+), 28 deletions(-) diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index ccd0da8ae0..73758bda87 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -54,7 +54,7 @@ pub fn generateSymbol( // Prepare the function parameters switch (bin_file.options.target.cpu.arch) { .x86_64 => { - const integer_registers = [_]Reg(.x86_64){.rdi, .rsi, .rdx, .rcx, .r8, .r9}; + const integer_registers = [_]Reg(.x86_64){ .rdi, .rsi, .rdx, .rcx, .r8, .r9 }; var next_int_reg: usize = 0; for (param_types) |param_type, src_i| { @@ -64,7 +64,7 @@ pub fn generateSymbol( try mc_args.append(.{ .stack_offset = next_stack_offset }); next_stack_offset += param_type.abiSize(bin_file.options.target); } else { - try mc_args.append(.{ .register = @enumToInt(integer_registers[next_int_reg])}); + try mc_args.append(.{ .register = @enumToInt(integer_registers[next_int_reg]) }); next_int_reg += 1; } }, @@ -75,10 +75,9 @@ pub fn generateSymbol( "TODO implement function parameters of type {}", .{@tagName(param_type.zigTypeTag())}, ), - } + }, } } - }, else => return Result{ .fail = try ErrorMsg.create( @@ -113,7 +112,7 @@ pub fn generateSymbol( function.gen() catch |err| switch (err) { error.CodegenFail => return Result{ .fail = function.err_msg.? }, - else => |e| return e, + else => |e| return e, }; if (function.err_msg) |em| { @@ -689,7 +688,7 @@ const Function = struct { }, .stack_offset => |off| { return self.fail(src, "TODO implement genSetReg for stack variables", .{}); - } + }, }, else => return self.fail(src, "TODO implement genSetReg for more architectures", .{}), } diff --git a/src-self-hosted/codegen/x86_64.zig b/src-self-hosted/codegen/x86_64.zig index 8bf131dbdd..ddcbd5320e 100644 --- a/src-self-hosted/codegen/x86_64.zig +++ b/src-self-hosted/codegen/x86_64.zig @@ -67,25 +67,4 @@ pub const Register = enum(u8) { } }; -// zig fmt: on - -/// After argument values have been computed, they are placed either in registers -/// or pushed on the stack. The way values are passed depends on the class. -pub const ParameterClass = enum { - /// Integral types that fit into one of the general purpose registers. - integer, - /// Types that fit into a vector register. - sse, - /// Types that fit into a vector register and can be passed - /// and returned in the upper bytes of it. - sse_up, - /// Types that will be returned via the x87FPU. - x87, - /// Types that will be returned via the x87FPU and can be passed and returned - /// in the upper bytes of it. - x87_up, - /// Types that will be returned via the x87FPU. - complex_x87, - /// Types that will be passed and returned in mem-ory via the stack. - memory, -}; +// zig fmt: on \ No newline at end of file From 35e8876c23e53c11115c78ff3984effa965a8cca Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Sun, 28 Jun 2020 23:42:42 -0600 Subject: [PATCH 10/19] Revert "arena_allocator: refactor and use full capacity" This reverts commit e120b07a524f1accd4975f5206018c61c4be9345. --- lib/std/heap/arena_allocator.zig | 48 +++++++++++++++++--------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/lib/std/heap/arena_allocator.zig b/lib/std/heap/arena_allocator.zig index 31fbad57f9..74ced774ed 100644 --- a/lib/std/heap/arena_allocator.zig +++ b/lib/std/heap/arena_allocator.zig @@ -15,7 +15,6 @@ pub const ArenaAllocator = struct { /// as a memory-saving optimization. pub const State = struct { buffer_list: std.SinglyLinkedList([]u8) = @as(std.SinglyLinkedList([]u8), .{}), - /// The first available index in the front buffer of `buffer_list` end_index: usize = 0, pub fn promote(self: State, child_allocator: *Allocator) ArenaAllocator { @@ -46,36 +45,39 @@ pub const ArenaAllocator = struct { } } - fn getBufNodeAddr(buf: []u8) usize { - return mem.alignBackward(@ptrToInt(buf.ptr) + buf.len - @sizeOf(BufNode), @alignOf(BufNode)); - } - - fn allocBuf(self: *ArenaAllocator, len: usize, ptr_align: u29) ![]u8 { - const alloc_len = len + @sizeOf(BufNode) + @alignOf(BufNode) - 1; - const buf = try self.child_allocator.callAllocFn(alloc_len, ptr_align, 1); - const buf_node = @intToPtr(*BufNode, getBufNodeAddr(buf)); - buf_node.* = .{ .data = buf, .next = null }; - assert(@ptrToInt(buf_node) - @ptrToInt(buf.ptr) >= len); + fn createNode(self: *ArenaAllocator, prev_len: usize, minimum_size: usize) !*BufNode { + const actual_min_size = minimum_size + (@sizeOf(BufNode) + 16); + const big_enough_len = prev_len + actual_min_size; + const len = big_enough_len + big_enough_len / 2; + const buf = try self.child_allocator.alignedAlloc(u8, @alignOf(BufNode), len); + const buf_node_slice = mem.bytesAsSlice(BufNode, buf[0..@sizeOf(BufNode)]); + const buf_node = &buf_node_slice[0]; + buf_node.* = BufNode{ + .data = buf, + .next = null, + }; self.state.buffer_list.prepend(buf_node); - self.state.end_index = len; - return buf[0..len]; + self.state.end_index = 0; + return buf_node; } fn alloc(allocator: *Allocator, n: usize, ptr_align: u29, len_align: u29) ![]u8 { const self = @fieldParentPtr(ArenaAllocator, "allocator", allocator); - if (self.state.buffer_list.first) |node_full_buf| { - assert(self.state.end_index > 0); - const cur_buf = node_full_buf.data[0.. - getBufNodeAddr(node_full_buf.data) - @ptrToInt(node_full_buf.data.ptr)]; + var cur_node = if (self.state.buffer_list.first) |first_node| first_node else try self.createNode(0, n + ptr_align); + while (true) { + const cur_buf = cur_node.data[@sizeOf(BufNode)..]; const addr = @ptrToInt(cur_buf.ptr) + self.state.end_index; - const aligned_index = self.state.end_index + (mem.alignForward(addr, ptr_align) - addr); - const aligned_end_index = aligned_index + n; - if (aligned_end_index <= cur_buf.len) { - self.state.end_index = aligned_end_index; - return cur_buf[aligned_index..aligned_end_index]; + const adjusted_addr = mem.alignForward(addr, ptr_align); + const adjusted_index = self.state.end_index + (adjusted_addr - addr); + const new_end_index = adjusted_index + n; + if (new_end_index > cur_buf.len) { + cur_node = try self.createNode(cur_buf.len, n + ptr_align); + continue; } + const result = cur_buf[adjusted_index..new_end_index]; + self.state.end_index = new_end_index; + return result; } - return try self.allocBuf(n, ptr_align); } }; From 67e97a1f0fe661b05234e24a58be15d9b48588f2 Mon Sep 17 00:00:00 2001 From: Jonathan Marler Date: Sun, 28 Jun 2020 23:44:48 -0600 Subject: [PATCH 11/19] ArenaAllocator: use full capacity --- lib/std/heap/arena_allocator.zig | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/std/heap/arena_allocator.zig b/lib/std/heap/arena_allocator.zig index 74ced774ed..a5d8aaea45 100644 --- a/lib/std/heap/arena_allocator.zig +++ b/lib/std/heap/arena_allocator.zig @@ -49,9 +49,8 @@ pub const ArenaAllocator = struct { const actual_min_size = minimum_size + (@sizeOf(BufNode) + 16); const big_enough_len = prev_len + actual_min_size; const len = big_enough_len + big_enough_len / 2; - const buf = try self.child_allocator.alignedAlloc(u8, @alignOf(BufNode), len); - const buf_node_slice = mem.bytesAsSlice(BufNode, buf[0..@sizeOf(BufNode)]); - const buf_node = &buf_node_slice[0]; + const buf = try self.child_allocator.callAllocFn(len, @alignOf(BufNode), 1); + const buf_node = @ptrCast(*BufNode, @alignCast(@alignOf(BufNode), buf.ptr)); buf_node.* = BufNode{ .data = buf, .next = null, From bf8bf528c6c131f61de4af24c3599486a59c5868 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 29 Jun 2020 17:10:01 +0200 Subject: [PATCH 12/19] Handle ENOTCAPABLE in WASI This commit adds `error.NotCapable` enum value and makes sure that every applicable WASI syscall that can return `ENOTCAPABLE` errno remaps it to `error.NotCapable. --- lib/std/fs.zig | 34 ++++++++---- lib/std/os.zig | 141 +++++++++++++++++++++++++++++++++++++------------ 2 files changed, 132 insertions(+), 43 deletions(-) diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 6cb7d478b2..08e77347da 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -264,7 +264,13 @@ pub const Dir = struct { pub const Kind = File.Kind; }; - const IteratorError = error{AccessDenied} || os.UnexpectedError; + const IteratorError = error{ + AccessDenied, + + /// WASI-only. This error occurs when the underlying `Dir` file descriptor does + /// not hold the required rights to call `fd_readdir` on it. + NotCapable, + } || os.UnexpectedError; pub const Iterator = switch (builtin.os.tag) { .macosx, .ios, .freebsd, .netbsd, .dragonfly => struct { @@ -535,14 +541,15 @@ pub const Dir = struct { w.EFAULT => unreachable, w.ENOTDIR => unreachable, w.EINVAL => unreachable, + w.ENOTCAPABLE => return error.NotCapable, else => |err| return os.unexpectedErrno(err), } if (bufused == 0) return null; self.index = 0; self.end_index = bufused; } - const entry = @ptrCast(*align(1) os.wasi.dirent_t, &self.buf[self.index]); - const entry_size = @sizeOf(os.wasi.dirent_t); + const entry = @ptrCast(*align(1) w.dirent_t, &self.buf[self.index]); + const entry_size = @sizeOf(w.dirent_t); const name_index = self.index + entry_size; const name = mem.span(self.buf[name_index .. name_index + entry.d_namlen]); @@ -556,12 +563,12 @@ pub const Dir = struct { } const entry_kind = switch (entry.d_type) { - wasi.FILETYPE_BLOCK_DEVICE => Entry.Kind.BlockDevice, - wasi.FILETYPE_CHARACTER_DEVICE => Entry.Kind.CharacterDevice, - wasi.FILETYPE_DIRECTORY => Entry.Kind.Directory, - wasi.FILETYPE_SYMBOLIC_LINK => Entry.Kind.SymLink, - wasi.FILETYPE_REGULAR_FILE => Entry.Kind.File, - wasi.FILETYPE_SOCKET_STREAM, wasi.FILETYPE_SOCKET_DGRAM => Entry.Kind.UnixDomainSocket, + w.FILETYPE_BLOCK_DEVICE => Entry.Kind.BlockDevice, + w.FILETYPE_CHARACTER_DEVICE => Entry.Kind.CharacterDevice, + w.FILETYPE_DIRECTORY => Entry.Kind.Directory, + w.FILETYPE_SYMBOLIC_LINK => Entry.Kind.SymLink, + w.FILETYPE_REGULAR_FILE => Entry.Kind.File, + w.FILETYPE_SOCKET_STREAM, wasi.FILETYPE_SOCKET_DGRAM => Entry.Kind.UnixDomainSocket, else => Entry.Kind.Unknown, }; return Entry{ @@ -621,6 +628,7 @@ pub const Dir = struct { InvalidUtf8, BadPathName, DeviceBusy, + NotCapable, } || os.UnexpectedError; pub fn close(self: *Dir) void { @@ -1105,7 +1113,7 @@ pub const Dir = struct { } } - pub const DeleteFileError = os.UnlinkError; + pub const DeleteFileError = os.UnlinkatError; /// Delete a file name and possibly the file it refers to, based on an open directory handle. /// Asserts that the path parameter has no null bytes. @@ -1147,6 +1155,7 @@ pub const Dir = struct { ReadOnlyFileSystem, InvalidUtf8, BadPathName, + NotCapable, Unexpected, }; @@ -1249,6 +1258,7 @@ pub const Dir = struct { /// On Windows, file paths cannot contain these characters: /// '/', '*', '?', '"', '<', '>', '|' BadPathName, + NotCapable, } || os.UnexpectedError; /// Whether `full_path` describes a symlink, file, or directory, this function @@ -1276,6 +1286,7 @@ pub const Dir = struct { error.FileBusy, error.BadPathName, error.Unexpected, + error.NotCapable, => |e| return e, } var dir = self.openDir(sub_path, .{ .iterate = true }) catch |err| switch (err) { @@ -1301,6 +1312,7 @@ pub const Dir = struct { error.InvalidUtf8, error.BadPathName, error.DeviceBusy, + error.NotCapable, => |e| return e, }; var cleanup_dir_parent: ?Dir = null; @@ -1342,6 +1354,7 @@ pub const Dir = struct { error.FileBusy, error.BadPathName, error.Unexpected, + error.NotCapable, => |e| return e, } @@ -1368,6 +1381,7 @@ pub const Dir = struct { error.InvalidUtf8, error.BadPathName, error.DeviceBusy, + error.NotCapable, => |e| return e, }; if (cleanup_dir_parent) |*d| d.close(); diff --git a/lib/std/os.zig b/lib/std/os.zig index 99d66db2bb..201cca9788 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -300,6 +300,10 @@ pub const ReadError = error{ /// This error occurs when no global event loop is configured, /// and reading from the file descriptor would block. WouldBlock, + + /// WASI-only. This error occurs when the file descriptor does + /// not hold the required rights to read from it. + NotCapable, } || UnexpectedError; /// Returns the number of bytes that were read, which can be less than @@ -335,6 +339,7 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize { wasi.ENOMEM => return error.SystemResources, wasi.ECONNRESET => return error.ConnectionResetByPeer, wasi.ETIMEDOUT => return error.ConnectionTimedOut, + wasi.ENOTCAPABLE => return error.NotCapable, else => |err| return unexpectedErrno(err), } } @@ -402,6 +407,7 @@ pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize { wasi.EISDIR => return error.IsDir, wasi.ENOBUFS => return error.SystemResources, wasi.ENOMEM => return error.SystemResources, + wasi.ENOTCAPABLE => return error.NotCapable, else => |err| return unexpectedErrno(err), } } @@ -466,6 +472,7 @@ pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize { wasi.ENXIO => return error.Unseekable, wasi.ESPIPE => return error.Unseekable, wasi.EOVERFLOW => return error.Unseekable, + wasi.ENOTCAPABLE => return error.NotCapable, else => |err| return unexpectedErrno(err), } } @@ -502,6 +509,10 @@ pub const TruncateError = error{ InputOutput, CannotTruncate, FileBusy, + + /// WASI-only. This error occurs when the file descriptor does + /// not hold the required rights to call `ftruncate` on it. + NotCapable, } || UnexpectedError; pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void { @@ -536,6 +547,7 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void { wasi.ETXTBSY => return error.FileBusy, wasi.EBADF => unreachable, // Handle not open for writing wasi.EINVAL => unreachable, // Handle not open for writing + wasi.ENOTCAPABLE => return error.NotCapable, else => |err| return unexpectedErrno(err), } } @@ -604,6 +616,7 @@ pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) PReadError!usize { wasi.ENXIO => return error.Unseekable, wasi.ESPIPE => return error.Unseekable, wasi.EOVERFLOW => return error.Unseekable, + wasi.ENOTCAPABLE => return error.NotCapable, else => |err| return unexpectedErrno(err), } } @@ -649,6 +662,10 @@ pub const WriteError = error{ /// This error occurs when no global event loop is configured, /// and reading from the file descriptor would block. WouldBlock, + + /// WASI-only. This error occurs when the file descriptor does + /// not hold the required rights to write to it. + NotCapable, } || UnexpectedError; /// Write to a file descriptor. @@ -697,6 +714,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize { wasi.ENOSPC => return error.NoSpaceLeft, wasi.EPERM => return error.AccessDenied, wasi.EPIPE => return error.BrokenPipe, + wasi.ENOTCAPABLE => return error.NotCapable, else => |err| return unexpectedErrno(err), } } @@ -774,6 +792,7 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize { wasi.ENOSPC => return error.NoSpaceLeft, wasi.EPERM => return error.AccessDenied, wasi.EPIPE => return error.BrokenPipe, + wasi.ENOTCAPABLE => return error.NotCapable, else => |err| return unexpectedErrno(err), } } @@ -856,6 +875,7 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize { wasi.ENXIO => return error.Unseekable, wasi.ESPIPE => return error.Unseekable, wasi.EOVERFLOW => return error.Unseekable, + wasi.ENOTCAPABLE => return error.NotCapable, else => |err| return unexpectedErrno(err), } } @@ -949,6 +969,7 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usiz wasi.ENXIO => return error.Unseekable, wasi.ESPIPE => return error.Unseekable, wasi.EOVERFLOW => return error.Unseekable, + wasi.ENOTCAPABLE => return error.NotCapable, else => |err| return unexpectedErrno(err), } } @@ -1020,6 +1041,10 @@ pub const OpenError = error{ /// The underlying filesystem does not support file locks FileLocksNotSupported, + + /// WASI-only. This error occurs when the file descriptor does + /// not hold the required rights to open a new resource relative to it. + NotCapable, } || UnexpectedError; /// Open and possibly create a file. Keeps trying if it gets interrupted. @@ -1113,6 +1138,7 @@ pub fn openatWasi(dir_fd: fd_t, file_path: []const u8, oflags: oflags_t, fdflags wasi.EPERM => return error.AccessDenied, wasi.EEXIST => return error.PathAlreadyExists, wasi.EBUSY => return error.DeviceBusy, + wasi.ENOTCAPABLE => return error.NotCapable, else => |err| return unexpectedErrno(err), } } @@ -1563,13 +1589,19 @@ pub fn symlinkZ(target_path: [*:0]const u8, sym_link_path: [*:0]const u8) SymLin } } +pub const SymlinkatError = error{ + /// WASI-only. This error occurs when the file descriptor does + /// not hold the required rights to create a new symbolic link relative to it. + NotCapable, +} || SymlinkError; + /// Similar to `symlink`, however, creates a symbolic link named `sym_link_path` which contains the string /// `target_path` **relative** to `newdirfd` directory handle. /// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent /// one; the latter case is known as a dangling link. /// If `sym_link_path` exists, it will not be overwritten. /// See also `symlinkatWasi`, `symlinkatZ` and `symlinkatW`. -pub fn symlinkat(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void { +pub fn symlinkat(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkatError!void { if (builtin.os.tag == .wasi) { return symlinkatWasi(target_path, newdirfd, sym_link_path); } @@ -1587,7 +1619,7 @@ pub const symlinkatC = @compileError("deprecated: renamed to symlinkatZ"); /// WASI-only. The same as `symlinkat` but targeting WASI. /// See also `symlinkat`. -pub fn symlinkatWasi(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void { +pub fn symlinkatWasi(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkatError!void { switch (wasi.path_symlink(target_path.ptr, target_path.len, newdirfd, sym_link_path.ptr, sym_link_path.len)) { wasi.ESUCCESS => {}, wasi.EFAULT => unreachable, @@ -1604,19 +1636,20 @@ pub fn symlinkatWasi(target_path: []const u8, newdirfd: fd_t, sym_link_path: []c wasi.ENOMEM => return error.SystemResources, wasi.ENOSPC => return error.NoSpaceLeft, wasi.EROFS => return error.ReadOnlyFileSystem, + wasi.ENOTCAPABLE => return error.NotCapable, else => |err| return unexpectedErrno(err), } } /// Windows-only. The same as `symlinkat` except the paths are null-terminated, WTF-16 encoded. /// See also `symlinkat`. -pub fn symlinkatW(target_path: [*:0]const u16, newdirfd: fd_t, sym_link_path: [*:0]const u16) SymlinkError!void { +pub fn symlinkatW(target_path: [*:0]const u16, newdirfd: fd_t, sym_link_path: [*:0]const u16) SymlinkatError!void { @compileError("TODO implement on Windows"); } /// The same as `symlinkat` except the parameters are null-terminated pointers. /// See also `symlinkat`. -pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*:0]const u8) SymLinkError!void { +pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*:0]const u8) SymLinkatError!void { if (builtin.os.tag == .windows) { const target_path_w = try windows.cStrToPrefixedFileW(target_path); const sym_link_path_w = try windows.cStrToPrefixedFileW(sym_link_path); @@ -1706,6 +1739,10 @@ pub fn unlinkZ(file_path: [*:0]const u8) UnlinkError!void { pub const UnlinkatError = UnlinkError || error{ /// When passing `AT_REMOVEDIR`, this error occurs when the named directory is not empty. DirNotEmpty, + + /// WASI-only. This error occurs when the file descriptor does + /// not hold the required rights to unlink a resource by path relative to it. + NotCapable, }; /// Delete a file name and possibly the file it refers to, based on an open directory handle. @@ -1747,6 +1784,7 @@ pub fn unlinkatWasi(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatErro wasi.ENOMEM => return error.SystemResources, wasi.EROFS => return error.ReadOnlyFileSystem, wasi.ENOTEMPTY => return error.DirNotEmpty, + wasi.ENOTCAPABLE => return error.NotCapable, wasi.EINVAL => unreachable, // invalid flags, or pathname has . as last component wasi.EBADF => unreachable, // always a race condition @@ -1925,13 +1963,19 @@ pub fn renameW(old_path: [*:0]const u16, new_path: [*:0]const u16) RenameError!v return windows.MoveFileExW(old_path, new_path, flags); } +pub const RenameatError = error{ + /// WASI-only. This error occurs when the file descriptor does + /// not hold the required rights to rename a resource by path relative to it. + NotCapable, +} || RenameError; + /// Change the name or location of a file based on an open directory handle. pub fn renameat( old_dir_fd: fd_t, old_path: []const u8, new_dir_fd: fd_t, new_path: []const u8, -) RenameError!void { +) RenameatError!void { if (builtin.os.tag == .windows) { const old_path_w = try windows.sliceToPrefixedFileW(old_path); const new_path_w = try windows.sliceToPrefixedFileW(new_path); @@ -1947,7 +1991,7 @@ pub fn renameat( /// WASI-only. Same as `renameat` expect targeting WASI. /// See also `renameat`. -pub fn renameatWasi(old_dir_fd: fd_t, old_path: []const u8, new_dir_fd: fd_t, new_path: []const u8) RenameError!void { +pub fn renameatWasi(old_dir_fd: fd_t, old_path: []const u8, new_dir_fd: fd_t, new_path: []const u8) RenameatError!void { switch (wasi.path_rename(old_dir_fd, old_path.ptr, old_path.len, new_dir_fd, new_path.ptr, new_path.len)) { wasi.ESUCCESS => return, wasi.EACCES => return error.AccessDenied, @@ -1968,6 +2012,7 @@ pub fn renameatWasi(old_dir_fd: fd_t, old_path: []const u8, new_dir_fd: fd_t, ne wasi.ENOTEMPTY => return error.PathAlreadyExists, wasi.EROFS => return error.ReadOnlyFileSystem, wasi.EXDEV => return error.RenameAcrossMountPoints, + wasi.ENOTCAPABLE => return error.NotCapable, else => |err| return unexpectedErrno(err), } } @@ -1978,7 +2023,7 @@ pub fn renameatZ( old_path: [*:0]const u8, new_dir_fd: fd_t, new_path: [*:0]const u8, -) RenameError!void { +) RenameatError!void { if (builtin.os.tag == .windows) { const old_path_w = try windows.cStrToPrefixedFileW(old_path); const new_path_w = try windows.cStrToPrefixedFileW(new_path); @@ -2017,7 +2062,7 @@ pub fn renameatW( new_dir_fd: fd_t, new_path_w: []const u16, ReplaceIfExists: windows.BOOLEAN, -) RenameError!void { +) RenameatError!void { const src_fd = windows.OpenFile(old_path_w, .{ .dir = old_dir_fd, .access_mask = windows.SYNCHRONIZE | windows.GENERIC_WRITE | windows.DELETE, @@ -2066,24 +2111,13 @@ pub fn renameatW( } } -pub const MakeDirError = error{ - AccessDenied, - DiskQuota, - PathAlreadyExists, - SymLinkLoop, - LinkQuotaExceeded, - NameTooLong, - FileNotFound, - SystemResources, - NoSpaceLeft, - NotDir, - ReadOnlyFileSystem, - InvalidUtf8, - BadPathName, - NoDevice, -} || UnexpectedError; +pub const MakeDirAtError = error{ + /// WASI-only. This error occurs when the file descriptor does + /// not hold the required rights to create a new directory relative to it. + NotCapable, +} || MakeDirError; -pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void { +pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirAtError!void { if (builtin.os.tag == .windows) { const sub_dir_path_w = try windows.sliceToPrefixedFileW(sub_dir_path); return mkdiratW(dir_fd, sub_dir_path_w.span().ptr, mode); @@ -2097,7 +2131,7 @@ pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!v pub const mkdiratC = @compileError("deprecated: renamed to mkdiratZ"); -pub fn mkdiratWasi(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void { +pub fn mkdiratWasi(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirAtError!void { switch (wasi.path_create_directory(dir_fd, sub_dir_path.ptr, sub_dir_path.len)) { wasi.ESUCCESS => return, wasi.EACCES => return error.AccessDenied, @@ -2114,11 +2148,12 @@ pub fn mkdiratWasi(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirErr wasi.ENOSPC => return error.NoSpaceLeft, wasi.ENOTDIR => return error.NotDir, wasi.EROFS => return error.ReadOnlyFileSystem, + wasi.ENOTCAPABLE => return error.NotCapable, else => |err| return unexpectedErrno(err), } } -pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: u32) MakeDirError!void { +pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: u32) MakeDirAtError!void { if (builtin.os.tag == .windows) { const sub_dir_path_w = try windows.cStrToPrefixedFileW(sub_dir_path); return mkdiratW(dir_fd, sub_dir_path_w.span().ptr, mode); @@ -2143,11 +2178,28 @@ pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: u32) MakeDirErr } } -pub fn mkdiratW(dir_fd: fd_t, sub_path_w: [*:0]const u16, mode: u32) MakeDirError!void { +pub fn mkdiratW(dir_fd: fd_t, sub_path_w: [*:0]const u16, mode: u32) MakeDirAtError!void { const sub_dir_handle = try windows.CreateDirectoryW(dir_fd, sub_path_w, null); windows.CloseHandle(sub_dir_handle); } +pub const MakeDirError = error{ + AccessDenied, + DiskQuota, + PathAlreadyExists, + SymLinkLoop, + LinkQuotaExceeded, + NameTooLong, + FileNotFound, + SystemResources, + NoSpaceLeft, + NotDir, + ReadOnlyFileSystem, + InvalidUtf8, + BadPathName, + NoDevice, +} || UnexpectedError; + /// Create a directory. /// `mode` is ignored on Windows. pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void { @@ -2364,10 +2416,16 @@ pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 } } +pub const ReadLinkAtError = error{ + /// WASI-only. This error occurs when the file descriptor does + /// not hold the required rights to read value of a symbolic link relative to it. + NotCapable, +} || ReadLinkError; + /// Similar to `readlink` except reads value of a symbolink link **relative** to `dirfd` directory handle. /// The return value is a slice of `out_buffer` from index 0. /// See also `readlinkatWasi`, `realinkatZ` and `realinkatW`. -pub fn readlinkat(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 { +pub fn readlinkat(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkAtError![]u8 { if (builtin.os.tag == .wasi) { return readlinkatWasi(dirfd, file_path, out_buffer); } @@ -2383,7 +2441,7 @@ pub const readlinkatC = @compileError("deprecated: renamed to readlinkatZ"); /// WASI-only. Same as `readlinkat` but targets WASI. /// See also `readlinkat`. -pub fn readlinkatWasi(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 { +pub fn readlinkatWasi(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkAtError![]u8 { var bufused: usize = undefined; switch (wasi.path_readlink(dirfd, file_path.ptr, file_path.len, out_buffer.ptr, out_buffer.len, &bufused)) { wasi.ESUCCESS => return out_buffer[0..bufused], @@ -2396,19 +2454,20 @@ pub fn readlinkatWasi(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) Read wasi.ENOENT => return error.FileNotFound, wasi.ENOMEM => return error.SystemResources, wasi.ENOTDIR => return error.NotDir, + wasi.ENOTCAPABLE => return error.NotCapable, else => |err| return unexpectedErrno(err), } } /// Windows-only. Same as `readlinkat` except `file_path` is null-terminated, WTF16 encoded. /// See also `readlinkat`. -pub fn readlinkatW(dirfd: fd_t, file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 { +pub fn readlinkatW(dirfd: fd_t, file_path: [*:0]const u16, out_buffer: []u8) ReadLinkAtError![]u8 { @compileError("TODO implement on Windows"); } /// Same as `readlinkat` except `file_path` is null-terminated. /// See also `readlinkat`. -pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 { +pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) ReadLinkAtError![]u8 { if (builtin.os.tag == .windows) { const file_path_w = try windows.cStrToPrefixedFileW(file_path); return readlinkatW(dirfd, file_path_w.span().ptr, out_buffer); @@ -3074,6 +3133,10 @@ pub fn waitpid(pid: i32, flags: u32) u32 { pub const FStatError = error{ SystemResources, AccessDenied, + + /// WASI-only. This error occurs when the file descriptor does + /// not hold the required rights to get its filestat information. + NotCapable, } || UnexpectedError; /// Return information about a file descriptor. @@ -3086,6 +3149,7 @@ pub fn fstat(fd: fd_t) FStatError!Stat { wasi.EBADF => unreachable, // Always a race condition. wasi.ENOMEM => return error.SystemResources, wasi.EACCES => return error.AccessDenied, + wasi.ENOTCAPABLE => return error.NotCapable, else => |err| return unexpectedErrno(err), } } @@ -3136,6 +3200,7 @@ pub fn fstatatWasi(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!S wasi.ENAMETOOLONG => return error.NameTooLong, wasi.ENOENT => return error.FileNotFound, wasi.ENOTDIR => return error.FileNotFound, + wasi.ENOTCAPABLE => return error.NotCapable, else => |err| return unexpectedErrno(err), } } @@ -3641,7 +3706,13 @@ pub fn gettimeofday(tv: ?*timeval, tz: ?*timezone) void { } } -pub const SeekError = error{Unseekable} || UnexpectedError; +pub const SeekError = error{ + Unseekable, + + /// WASI-only. This error occurs when the file descriptor does + /// not hold the required rights to seek on it. + NotCapable, +} || UnexpectedError; /// Repositions read/write file offset relative to the beginning. pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void { @@ -3669,6 +3740,7 @@ pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void { wasi.EOVERFLOW => return error.Unseekable, wasi.ESPIPE => return error.Unseekable, wasi.ENXIO => return error.Unseekable, + wasi.ENOTCAPABLE => return error.NotCapable, else => |err| return unexpectedErrno(err), } } @@ -3710,6 +3782,7 @@ pub fn lseek_CUR(fd: fd_t, offset: i64) SeekError!void { wasi.EOVERFLOW => return error.Unseekable, wasi.ESPIPE => return error.Unseekable, wasi.ENXIO => return error.Unseekable, + wasi.ENOTCAPABLE => return error.NotCapable, else => |err| return unexpectedErrno(err), } } @@ -3750,6 +3823,7 @@ pub fn lseek_END(fd: fd_t, offset: i64) SeekError!void { wasi.EOVERFLOW => return error.Unseekable, wasi.ESPIPE => return error.Unseekable, wasi.ENXIO => return error.Unseekable, + wasi.ENOTCAPABLE => return error.NotCapable, else => |err| return unexpectedErrno(err), } } @@ -3790,6 +3864,7 @@ pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 { wasi.EOVERFLOW => return error.Unseekable, wasi.ESPIPE => return error.Unseekable, wasi.ENXIO => return error.Unseekable, + wasi.ENOTCAPABLE => return error.NotCapable, else => |err| return unexpectedErrno(err), } } From b96882c57a20d3f9f19640dbb6b2483d7dea5dfe Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 29 Jun 2020 18:09:22 +0200 Subject: [PATCH 13/19] Fix compilation errors --- lib/std/child_process.zig | 1 + lib/std/elf.zig | 1 + lib/std/fs.zig | 2 ++ lib/std/os.zig | 12 ++++++------ src-self-hosted/stage2.zig | 3 +++ 5 files changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index 57c1dfb945..20293daecb 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -364,6 +364,7 @@ pub const ChildProcess = struct { error.FileTooBig => unreachable, error.DeviceBusy => unreachable, error.FileLocksNotSupported => unreachable, + error.NotCapable => unreachable, // until WASI comes up with multi-processing (if ever) else => |e| return e, } else diff --git a/lib/std/elf.zig b/lib/std/elf.zig index dd22a42304..54d4673f64 100644 --- a/lib/std/elf.zig +++ b/lib/std/elf.zig @@ -551,6 +551,7 @@ fn preadNoEof(file: std.fs.File, buf: []u8, offset: u64) !void { error.InputOutput => return error.FileSystem, error.Unexpected => return error.Unexpected, error.WouldBlock => return error.Unexpected, + error.NotCapable => unreachable, // NotCapable mainly pertains WASI target so it's a bug if we hit it here }; if (len == 0) return error.UnexpectedEndOfFile; i += len; diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 08e77347da..fe2e7338c8 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -1272,6 +1272,7 @@ pub const Dir = struct { if (self.deleteFile(sub_path)) { return; } else |err| switch (err) { + error.DirNotEmpty => unreachable, error.FileNotFound => return, error.IsDir => {}, error.AccessDenied => got_access_denied = true, @@ -1341,6 +1342,7 @@ pub const Dir = struct { // Impossible because we do not pass any path separators. error.NotDir => unreachable, + error.DirNotEmpty => unreachable, error.IsDir => {}, error.AccessDenied => got_access_denied = true, diff --git a/lib/std/os.zig b/lib/std/os.zig index 201cca9788..a7e29555d6 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -1589,11 +1589,11 @@ pub fn symlinkZ(target_path: [*:0]const u8, sym_link_path: [*:0]const u8) SymLin } } -pub const SymlinkatError = error{ +pub const SymLinkAtError = error{ /// WASI-only. This error occurs when the file descriptor does /// not hold the required rights to create a new symbolic link relative to it. NotCapable, -} || SymlinkError; +} || SymLinkError; /// Similar to `symlink`, however, creates a symbolic link named `sym_link_path` which contains the string /// `target_path` **relative** to `newdirfd` directory handle. @@ -1601,7 +1601,7 @@ pub const SymlinkatError = error{ /// one; the latter case is known as a dangling link. /// If `sym_link_path` exists, it will not be overwritten. /// See also `symlinkatWasi`, `symlinkatZ` and `symlinkatW`. -pub fn symlinkat(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkatError!void { +pub fn symlinkat(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkAtError!void { if (builtin.os.tag == .wasi) { return symlinkatWasi(target_path, newdirfd, sym_link_path); } @@ -1619,7 +1619,7 @@ pub const symlinkatC = @compileError("deprecated: renamed to symlinkatZ"); /// WASI-only. The same as `symlinkat` but targeting WASI. /// See also `symlinkat`. -pub fn symlinkatWasi(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkatError!void { +pub fn symlinkatWasi(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkAtError!void { switch (wasi.path_symlink(target_path.ptr, target_path.len, newdirfd, sym_link_path.ptr, sym_link_path.len)) { wasi.ESUCCESS => {}, wasi.EFAULT => unreachable, @@ -1643,13 +1643,13 @@ pub fn symlinkatWasi(target_path: []const u8, newdirfd: fd_t, sym_link_path: []c /// Windows-only. The same as `symlinkat` except the paths are null-terminated, WTF-16 encoded. /// See also `symlinkat`. -pub fn symlinkatW(target_path: [*:0]const u16, newdirfd: fd_t, sym_link_path: [*:0]const u16) SymlinkatError!void { +pub fn symlinkatW(target_path: [*:0]const u16, newdirfd: fd_t, sym_link_path: [*:0]const u16) SymLinkAtError!void { @compileError("TODO implement on Windows"); } /// The same as `symlinkat` except the parameters are null-terminated pointers. /// See also `symlinkat`. -pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*:0]const u8) SymLinkatError!void { +pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*:0]const u8) SymLinkAtError!void { if (builtin.os.tag == .windows) { const target_path_w = try windows.cStrToPrefixedFileW(target_path); const sym_link_path_w = try windows.cStrToPrefixedFileW(sym_link_path); diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index bd24ffb399..60076b9cdb 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -163,6 +163,7 @@ export fn stage2_render_ast(tree: *ast.Tree, output_file: *FILE) Error { error.OutOfMemory => return .OutOfMemory, error.Unexpected => return .Unexpected, error.InputOutput => return .FileSystem, + error.NotCapable => unreachable, // we should handle this when we're able to cross-compile Zig to WASI }; return .None; } @@ -606,6 +607,7 @@ export fn stage2_libc_parse(stage1_libc: *Stage2LibCInstallation, libc_file_z: [ error.NotDir => return .NotDir, error.DeviceBusy => return .DeviceBusy, error.FileLocksNotSupported => unreachable, + error.NotCapable => unreachable, // we should handle this when we're able to cross-compile Zig to WASI }; stage1_libc.initFromStage2(libc); return .None; @@ -649,6 +651,7 @@ export fn stage2_libc_render(stage1_libc: *Stage2LibCInstallation, output_file: error.AccessDenied => return .AccessDenied, error.Unexpected => return .Unexpected, error.InputOutput => return .FileSystem, + error.NotCapable => unreachable, // we should handle this when we're able to cross-compile Zig to WASI }; return .None; } From 5bc99dd7e8c037aa6c5f46d779fba99163d1da36 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Mon, 29 Jun 2020 18:28:28 +0200 Subject: [PATCH 14/19] Fix more compilation errors --- lib/std/os.zig | 1 + lib/std/zig/system.zig | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/lib/std/os.zig b/lib/std/os.zig index a7e29555d6..a1b3665845 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -4012,6 +4012,7 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP if (builtin.os.tag == .linux and !builtin.link_libc) { const fd = openZ(pathname, linux.O_PATH | linux.O_NONBLOCK | linux.O_CLOEXEC, 0) catch |err| switch (err) { error.FileLocksNotSupported => unreachable, + error.NotCapable => unreachable, // WASI only else => |e| return e, }; defer close(fd); diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 64c9401dbc..166dbf4c43 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -499,6 +499,7 @@ pub const NativeTargetInfo = struct { error.PipeBusy => unreachable, error.FileLocksNotSupported => unreachable, error.WouldBlock => unreachable, + error.NotCapable => unreachable, // we don't support WASI here (not yet at least) error.IsDir, error.NotDir, @@ -790,6 +791,7 @@ pub const NativeTargetInfo = struct { var it = mem.tokenize(rpath_list, ":"); while (it.next()) |rpath| { var dir = fs.cwd().openDir(rpath, .{}) catch |err| switch (err) { + error.NotCapable => unreachable, // we don't support WASI here (not yet at least) error.NameTooLong => unreachable, error.InvalidUtf8 => unreachable, error.BadPathName => unreachable, @@ -817,6 +819,7 @@ pub const NativeTargetInfo = struct { &link_buf, ) catch |err| switch (err) { error.NameTooLong => unreachable, + error.NotCapable => unreachable, // we don't support WASI here (not yet at least) error.AccessDenied, error.FileNotFound, @@ -851,6 +854,7 @@ pub const NativeTargetInfo = struct { const len = file.pread(buf[i .. buf.len - i], offset + i) catch |err| switch (err) { error.OperationAborted => unreachable, // Windows-only error.WouldBlock => unreachable, // Did not request blocking mode + error.NotCapable => unreachable, // WASI only, and we don't support it here yet error.SystemResources => return error.SystemResources, error.IsDir => return error.UnableToReadElfFile, error.BrokenPipe => return error.UnableToReadElfFile, From 8306826d53bf6dd0c5ea5ea27963374b31aa6dd1 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 30 Jun 2020 18:18:25 +0200 Subject: [PATCH 15/19] Map ENOTCAPABLE into error.AccessDenied instead of error.NotCapable This is direct result of review comments left by andrewrk and daurnimator. It makes sense to map `ENOTCAPABLE` into a more generic `error.AccessDenied`. --- lib/std/child_process.zig | 1 - lib/std/elf.zig | 2 +- lib/std/fs.zig | 21 +----- lib/std/os.zig | 151 ++++++++++++++++--------------------- lib/std/zig/system.zig | 5 +- src-self-hosted/stage2.zig | 3 - 6 files changed, 69 insertions(+), 114 deletions(-) diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index 20293daecb..57c1dfb945 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -364,7 +364,6 @@ pub const ChildProcess = struct { error.FileTooBig => unreachable, error.DeviceBusy => unreachable, error.FileLocksNotSupported => unreachable, - error.NotCapable => unreachable, // until WASI comes up with multi-processing (if ever) else => |e| return e, } else diff --git a/lib/std/elf.zig b/lib/std/elf.zig index 54d4673f64..b6609d8b31 100644 --- a/lib/std/elf.zig +++ b/lib/std/elf.zig @@ -551,7 +551,7 @@ fn preadNoEof(file: std.fs.File, buf: []u8, offset: u64) !void { error.InputOutput => return error.FileSystem, error.Unexpected => return error.Unexpected, error.WouldBlock => return error.Unexpected, - error.NotCapable => unreachable, // NotCapable mainly pertains WASI target so it's a bug if we hit it here + error.AccessDenied => return error.Unexpected, }; if (len == 0) return error.UnexpectedEndOfFile; i += len; diff --git a/lib/std/fs.zig b/lib/std/fs.zig index fe2e7338c8..caa414ac17 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -264,13 +264,7 @@ pub const Dir = struct { pub const Kind = File.Kind; }; - const IteratorError = error{ - AccessDenied, - - /// WASI-only. This error occurs when the underlying `Dir` file descriptor does - /// not hold the required rights to call `fd_readdir` on it. - NotCapable, - } || os.UnexpectedError; + const IteratorError = error{AccessDenied} || os.UnexpectedError; pub const Iterator = switch (builtin.os.tag) { .macosx, .ios, .freebsd, .netbsd, .dragonfly => struct { @@ -541,7 +535,7 @@ pub const Dir = struct { w.EFAULT => unreachable, w.ENOTDIR => unreachable, w.EINVAL => unreachable, - w.ENOTCAPABLE => return error.NotCapable, + w.ENOTCAPABLE => return error.AccessDenied, else => |err| return os.unexpectedErrno(err), } if (bufused == 0) return null; @@ -628,7 +622,6 @@ pub const Dir = struct { InvalidUtf8, BadPathName, DeviceBusy, - NotCapable, } || os.UnexpectedError; pub fn close(self: *Dir) void { @@ -1113,7 +1106,7 @@ pub const Dir = struct { } } - pub const DeleteFileError = os.UnlinkatError; + pub const DeleteFileError = os.UnlinkError; /// Delete a file name and possibly the file it refers to, based on an open directory handle. /// Asserts that the path parameter has no null bytes. @@ -1155,7 +1148,6 @@ pub const Dir = struct { ReadOnlyFileSystem, InvalidUtf8, BadPathName, - NotCapable, Unexpected, }; @@ -1258,7 +1250,6 @@ pub const Dir = struct { /// On Windows, file paths cannot contain these characters: /// '/', '*', '?', '"', '<', '>', '|' BadPathName, - NotCapable, } || os.UnexpectedError; /// Whether `full_path` describes a symlink, file, or directory, this function @@ -1272,7 +1263,6 @@ pub const Dir = struct { if (self.deleteFile(sub_path)) { return; } else |err| switch (err) { - error.DirNotEmpty => unreachable, error.FileNotFound => return, error.IsDir => {}, error.AccessDenied => got_access_denied = true, @@ -1287,7 +1277,6 @@ pub const Dir = struct { error.FileBusy, error.BadPathName, error.Unexpected, - error.NotCapable, => |e| return e, } var dir = self.openDir(sub_path, .{ .iterate = true }) catch |err| switch (err) { @@ -1313,7 +1302,6 @@ pub const Dir = struct { error.InvalidUtf8, error.BadPathName, error.DeviceBusy, - error.NotCapable, => |e| return e, }; var cleanup_dir_parent: ?Dir = null; @@ -1342,7 +1330,6 @@ pub const Dir = struct { // Impossible because we do not pass any path separators. error.NotDir => unreachable, - error.DirNotEmpty => unreachable, error.IsDir => {}, error.AccessDenied => got_access_denied = true, @@ -1356,7 +1343,6 @@ pub const Dir = struct { error.FileBusy, error.BadPathName, error.Unexpected, - error.NotCapable, => |e| return e, } @@ -1383,7 +1369,6 @@ pub const Dir = struct { error.InvalidUtf8, error.BadPathName, error.DeviceBusy, - error.NotCapable, => |e| return e, }; if (cleanup_dir_parent) |*d| d.close(); diff --git a/lib/std/os.zig b/lib/std/os.zig index a1b3665845..1e1049ae51 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -301,9 +301,9 @@ pub const ReadError = error{ /// and reading from the file descriptor would block. WouldBlock, - /// WASI-only. This error occurs when the file descriptor does + /// In WASI, this error occurs when the file descriptor does /// not hold the required rights to read from it. - NotCapable, + AccessDenied, } || UnexpectedError; /// Returns the number of bytes that were read, which can be less than @@ -339,7 +339,7 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize { wasi.ENOMEM => return error.SystemResources, wasi.ECONNRESET => return error.ConnectionResetByPeer, wasi.ETIMEDOUT => return error.ConnectionTimedOut, - wasi.ENOTCAPABLE => return error.NotCapable, + wasi.ENOTCAPABLE => return error.AccessDenied, else => |err| return unexpectedErrno(err), } } @@ -407,7 +407,7 @@ pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize { wasi.EISDIR => return error.IsDir, wasi.ENOBUFS => return error.SystemResources, wasi.ENOMEM => return error.SystemResources, - wasi.ENOTCAPABLE => return error.NotCapable, + wasi.ENOTCAPABLE => return error.AccessDenied, else => |err| return unexpectedErrno(err), } } @@ -472,7 +472,7 @@ pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize { wasi.ENXIO => return error.Unseekable, wasi.ESPIPE => return error.Unseekable, wasi.EOVERFLOW => return error.Unseekable, - wasi.ENOTCAPABLE => return error.NotCapable, + wasi.ENOTCAPABLE => return error.AccessDenied, else => |err| return unexpectedErrno(err), } } @@ -507,12 +507,11 @@ pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize { pub const TruncateError = error{ FileTooBig, InputOutput, - CannotTruncate, FileBusy, - /// WASI-only. This error occurs when the file descriptor does + /// In WASI, this error occurs when the file descriptor does /// not hold the required rights to call `ftruncate` on it. - NotCapable, + AccessDenied, } || UnexpectedError; pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void { @@ -533,7 +532,7 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void { switch (rc) { .SUCCESS => return, .INVALID_HANDLE => unreachable, // Handle not open for writing - .ACCESS_DENIED => return error.CannotTruncate, + .ACCESS_DENIED => return error.AccessDenied, else => return windows.unexpectedStatus(rc), } } @@ -543,11 +542,11 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void { wasi.EINTR => unreachable, wasi.EFBIG => return error.FileTooBig, wasi.EIO => return error.InputOutput, - wasi.EPERM => return error.CannotTruncate, + wasi.EPERM => return error.AccessDenied, wasi.ETXTBSY => return error.FileBusy, wasi.EBADF => unreachable, // Handle not open for writing wasi.EINVAL => unreachable, // Handle not open for writing - wasi.ENOTCAPABLE => return error.NotCapable, + wasi.ENOTCAPABLE => return error.AccessDenied, else => |err| return unexpectedErrno(err), } } @@ -566,7 +565,7 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void { EINTR => continue, EFBIG => return error.FileTooBig, EIO => return error.InputOutput, - EPERM => return error.CannotTruncate, + EPERM => return error.AccessDenied, ETXTBSY => return error.FileBusy, EBADF => unreachable, // Handle not open for writing EINVAL => unreachable, // Handle not open for writing @@ -616,7 +615,7 @@ pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) PReadError!usize { wasi.ENXIO => return error.Unseekable, wasi.ESPIPE => return error.Unseekable, wasi.EOVERFLOW => return error.Unseekable, - wasi.ENOTCAPABLE => return error.NotCapable, + wasi.ENOTCAPABLE => return error.AccessDenied, else => |err| return unexpectedErrno(err), } } @@ -654,6 +653,9 @@ pub const WriteError = error{ FileTooBig, InputOutput, NoSpaceLeft, + + /// In WASI, this error may occur when the file descriptor does + /// not hold the required rights to write to it. AccessDenied, BrokenPipe, SystemResources, @@ -662,10 +664,6 @@ pub const WriteError = error{ /// This error occurs when no global event loop is configured, /// and reading from the file descriptor would block. WouldBlock, - - /// WASI-only. This error occurs when the file descriptor does - /// not hold the required rights to write to it. - NotCapable, } || UnexpectedError; /// Write to a file descriptor. @@ -714,7 +712,7 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize { wasi.ENOSPC => return error.NoSpaceLeft, wasi.EPERM => return error.AccessDenied, wasi.EPIPE => return error.BrokenPipe, - wasi.ENOTCAPABLE => return error.NotCapable, + wasi.ENOTCAPABLE => return error.AccessDenied, else => |err| return unexpectedErrno(err), } } @@ -792,7 +790,7 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize { wasi.ENOSPC => return error.NoSpaceLeft, wasi.EPERM => return error.AccessDenied, wasi.EPIPE => return error.BrokenPipe, - wasi.ENOTCAPABLE => return error.NotCapable, + wasi.ENOTCAPABLE => return error.AccessDenied, else => |err| return unexpectedErrno(err), } } @@ -875,7 +873,7 @@ pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize { wasi.ENXIO => return error.Unseekable, wasi.ESPIPE => return error.Unseekable, wasi.EOVERFLOW => return error.Unseekable, - wasi.ENOTCAPABLE => return error.NotCapable, + wasi.ENOTCAPABLE => return error.AccessDenied, else => |err| return unexpectedErrno(err), } } @@ -969,7 +967,7 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usiz wasi.ENXIO => return error.Unseekable, wasi.ESPIPE => return error.Unseekable, wasi.EOVERFLOW => return error.Unseekable, - wasi.ENOTCAPABLE => return error.NotCapable, + wasi.ENOTCAPABLE => return error.AccessDenied, else => |err| return unexpectedErrno(err), } } @@ -1005,6 +1003,8 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usiz } pub const OpenError = error{ + /// In WASI, this error may occur when the file descriptor does + /// not hold the required rights to open a new resource relative to it. AccessDenied, SymLinkLoop, ProcessFdQuotaExceeded, @@ -1041,10 +1041,6 @@ pub const OpenError = error{ /// The underlying filesystem does not support file locks FileLocksNotSupported, - - /// WASI-only. This error occurs when the file descriptor does - /// not hold the required rights to open a new resource relative to it. - NotCapable, } || UnexpectedError; /// Open and possibly create a file. Keeps trying if it gets interrupted. @@ -1138,7 +1134,7 @@ pub fn openatWasi(dir_fd: fd_t, file_path: []const u8, oflags: oflags_t, fdflags wasi.EPERM => return error.AccessDenied, wasi.EEXIST => return error.PathAlreadyExists, wasi.EBUSY => return error.DeviceBusy, - wasi.ENOTCAPABLE => return error.NotCapable, + wasi.ENOTCAPABLE => return error.AccessDenied, else => |err| return unexpectedErrno(err), } } @@ -1525,6 +1521,8 @@ pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 { } pub const SymLinkError = error{ + /// In WASI, this error may occur when the file descriptor does + /// not hold the required rights to create a new symbolic link relative to it. AccessDenied, DiskQuota, PathAlreadyExists, @@ -1589,19 +1587,13 @@ pub fn symlinkZ(target_path: [*:0]const u8, sym_link_path: [*:0]const u8) SymLin } } -pub const SymLinkAtError = error{ - /// WASI-only. This error occurs when the file descriptor does - /// not hold the required rights to create a new symbolic link relative to it. - NotCapable, -} || SymLinkError; - /// Similar to `symlink`, however, creates a symbolic link named `sym_link_path` which contains the string /// `target_path` **relative** to `newdirfd` directory handle. /// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent /// one; the latter case is known as a dangling link. /// If `sym_link_path` exists, it will not be overwritten. /// See also `symlinkatWasi`, `symlinkatZ` and `symlinkatW`. -pub fn symlinkat(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkAtError!void { +pub fn symlinkat(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void { if (builtin.os.tag == .wasi) { return symlinkatWasi(target_path, newdirfd, sym_link_path); } @@ -1619,7 +1611,7 @@ pub const symlinkatC = @compileError("deprecated: renamed to symlinkatZ"); /// WASI-only. The same as `symlinkat` but targeting WASI. /// See also `symlinkat`. -pub fn symlinkatWasi(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkAtError!void { +pub fn symlinkatWasi(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void { switch (wasi.path_symlink(target_path.ptr, target_path.len, newdirfd, sym_link_path.ptr, sym_link_path.len)) { wasi.ESUCCESS => {}, wasi.EFAULT => unreachable, @@ -1636,20 +1628,20 @@ pub fn symlinkatWasi(target_path: []const u8, newdirfd: fd_t, sym_link_path: []c wasi.ENOMEM => return error.SystemResources, wasi.ENOSPC => return error.NoSpaceLeft, wasi.EROFS => return error.ReadOnlyFileSystem, - wasi.ENOTCAPABLE => return error.NotCapable, + wasi.ENOTCAPABLE => return error.AccessDenied, else => |err| return unexpectedErrno(err), } } /// Windows-only. The same as `symlinkat` except the paths are null-terminated, WTF-16 encoded. /// See also `symlinkat`. -pub fn symlinkatW(target_path: [*:0]const u16, newdirfd: fd_t, sym_link_path: [*:0]const u16) SymLinkAtError!void { +pub fn symlinkatW(target_path: [*:0]const u16, newdirfd: fd_t, sym_link_path: [*:0]const u16) SymLinkError!void { @compileError("TODO implement on Windows"); } /// The same as `symlinkat` except the parameters are null-terminated pointers. /// See also `symlinkat`. -pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*:0]const u8) SymLinkAtError!void { +pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*:0]const u8) SymLinkError!void { if (builtin.os.tag == .windows) { const target_path_w = try windows.cStrToPrefixedFileW(target_path); const sym_link_path_w = try windows.cStrToPrefixedFileW(sym_link_path); @@ -1677,6 +1669,9 @@ pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*: pub const UnlinkError = error{ FileNotFound, + + /// In WASI, this error may occur when the file descriptor does + /// not hold the required rights to unlink a resource by path relative to it. AccessDenied, FileBusy, FileSystem, @@ -1739,10 +1734,6 @@ pub fn unlinkZ(file_path: [*:0]const u8) UnlinkError!void { pub const UnlinkatError = UnlinkError || error{ /// When passing `AT_REMOVEDIR`, this error occurs when the named directory is not empty. DirNotEmpty, - - /// WASI-only. This error occurs when the file descriptor does - /// not hold the required rights to unlink a resource by path relative to it. - NotCapable, }; /// Delete a file name and possibly the file it refers to, based on an open directory handle. @@ -1784,7 +1775,7 @@ pub fn unlinkatWasi(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatErro wasi.ENOMEM => return error.SystemResources, wasi.EROFS => return error.ReadOnlyFileSystem, wasi.ENOTEMPTY => return error.DirNotEmpty, - wasi.ENOTCAPABLE => return error.NotCapable, + wasi.ENOTCAPABLE => return error.AccessDenied, wasi.EINVAL => unreachable, // invalid flags, or pathname has . as last component wasi.EBADF => unreachable, // always a race condition @@ -1887,6 +1878,8 @@ pub fn unlinkatW(dirfd: fd_t, sub_path_w: [*:0]const u16, flags: u32) UnlinkatEr } const RenameError = error{ + /// In WASI, this error may occur when the file descriptor does + /// not hold the required rights to rename a resource by path relative to it. AccessDenied, FileBusy, DiskQuota, @@ -1963,19 +1956,13 @@ pub fn renameW(old_path: [*:0]const u16, new_path: [*:0]const u16) RenameError!v return windows.MoveFileExW(old_path, new_path, flags); } -pub const RenameatError = error{ - /// WASI-only. This error occurs when the file descriptor does - /// not hold the required rights to rename a resource by path relative to it. - NotCapable, -} || RenameError; - /// Change the name or location of a file based on an open directory handle. pub fn renameat( old_dir_fd: fd_t, old_path: []const u8, new_dir_fd: fd_t, new_path: []const u8, -) RenameatError!void { +) RenameError!void { if (builtin.os.tag == .windows) { const old_path_w = try windows.sliceToPrefixedFileW(old_path); const new_path_w = try windows.sliceToPrefixedFileW(new_path); @@ -1991,7 +1978,7 @@ pub fn renameat( /// WASI-only. Same as `renameat` expect targeting WASI. /// See also `renameat`. -pub fn renameatWasi(old_dir_fd: fd_t, old_path: []const u8, new_dir_fd: fd_t, new_path: []const u8) RenameatError!void { +pub fn renameatWasi(old_dir_fd: fd_t, old_path: []const u8, new_dir_fd: fd_t, new_path: []const u8) RenameError!void { switch (wasi.path_rename(old_dir_fd, old_path.ptr, old_path.len, new_dir_fd, new_path.ptr, new_path.len)) { wasi.ESUCCESS => return, wasi.EACCES => return error.AccessDenied, @@ -2012,7 +1999,7 @@ pub fn renameatWasi(old_dir_fd: fd_t, old_path: []const u8, new_dir_fd: fd_t, ne wasi.ENOTEMPTY => return error.PathAlreadyExists, wasi.EROFS => return error.ReadOnlyFileSystem, wasi.EXDEV => return error.RenameAcrossMountPoints, - wasi.ENOTCAPABLE => return error.NotCapable, + wasi.ENOTCAPABLE => return error.AccessDenied, else => |err| return unexpectedErrno(err), } } @@ -2023,7 +2010,7 @@ pub fn renameatZ( old_path: [*:0]const u8, new_dir_fd: fd_t, new_path: [*:0]const u8, -) RenameatError!void { +) RenameError!void { if (builtin.os.tag == .windows) { const old_path_w = try windows.cStrToPrefixedFileW(old_path); const new_path_w = try windows.cStrToPrefixedFileW(new_path); @@ -2062,7 +2049,7 @@ pub fn renameatW( new_dir_fd: fd_t, new_path_w: []const u16, ReplaceIfExists: windows.BOOLEAN, -) RenameatError!void { +) RenameError!void { const src_fd = windows.OpenFile(old_path_w, .{ .dir = old_dir_fd, .access_mask = windows.SYNCHRONIZE | windows.GENERIC_WRITE | windows.DELETE, @@ -2111,13 +2098,7 @@ pub fn renameatW( } } -pub const MakeDirAtError = error{ - /// WASI-only. This error occurs when the file descriptor does - /// not hold the required rights to create a new directory relative to it. - NotCapable, -} || MakeDirError; - -pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirAtError!void { +pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void { if (builtin.os.tag == .windows) { const sub_dir_path_w = try windows.sliceToPrefixedFileW(sub_dir_path); return mkdiratW(dir_fd, sub_dir_path_w.span().ptr, mode); @@ -2131,7 +2112,7 @@ pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirAtError pub const mkdiratC = @compileError("deprecated: renamed to mkdiratZ"); -pub fn mkdiratWasi(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirAtError!void { +pub fn mkdiratWasi(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void { switch (wasi.path_create_directory(dir_fd, sub_dir_path.ptr, sub_dir_path.len)) { wasi.ESUCCESS => return, wasi.EACCES => return error.AccessDenied, @@ -2148,12 +2129,12 @@ pub fn mkdiratWasi(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirAtE wasi.ENOSPC => return error.NoSpaceLeft, wasi.ENOTDIR => return error.NotDir, wasi.EROFS => return error.ReadOnlyFileSystem, - wasi.ENOTCAPABLE => return error.NotCapable, + wasi.ENOTCAPABLE => return error.AccessDenied, else => |err| return unexpectedErrno(err), } } -pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: u32) MakeDirAtError!void { +pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: u32) MakeDirError!void { if (builtin.os.tag == .windows) { const sub_dir_path_w = try windows.cStrToPrefixedFileW(sub_dir_path); return mkdiratW(dir_fd, sub_dir_path_w.span().ptr, mode); @@ -2178,12 +2159,14 @@ pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: u32) MakeDirAtE } } -pub fn mkdiratW(dir_fd: fd_t, sub_path_w: [*:0]const u16, mode: u32) MakeDirAtError!void { +pub fn mkdiratW(dir_fd: fd_t, sub_path_w: [*:0]const u16, mode: u32) MakeDirError!void { const sub_dir_handle = try windows.CreateDirectoryW(dir_fd, sub_path_w, null); windows.CloseHandle(sub_dir_handle); } pub const MakeDirError = error{ + /// In WASI, this error may occur when the file descriptor does + /// not hold the required rights to create a new directory relative to it. AccessDenied, DiskQuota, PathAlreadyExists, @@ -2363,6 +2346,8 @@ pub fn fchdir(dirfd: fd_t) FchdirError!void { } pub const ReadLinkError = error{ + /// In WASI, this error may occur when the file descriptor does + /// not hold the required rights to read value of a symbolic link relative to it. AccessDenied, FileSystem, SymLinkLoop, @@ -2416,16 +2401,10 @@ pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 } } -pub const ReadLinkAtError = error{ - /// WASI-only. This error occurs when the file descriptor does - /// not hold the required rights to read value of a symbolic link relative to it. - NotCapable, -} || ReadLinkError; - /// Similar to `readlink` except reads value of a symbolink link **relative** to `dirfd` directory handle. /// The return value is a slice of `out_buffer` from index 0. /// See also `readlinkatWasi`, `realinkatZ` and `realinkatW`. -pub fn readlinkat(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkAtError![]u8 { +pub fn readlinkat(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 { if (builtin.os.tag == .wasi) { return readlinkatWasi(dirfd, file_path, out_buffer); } @@ -2441,7 +2420,7 @@ pub const readlinkatC = @compileError("deprecated: renamed to readlinkatZ"); /// WASI-only. Same as `readlinkat` but targets WASI. /// See also `readlinkat`. -pub fn readlinkatWasi(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkAtError![]u8 { +pub fn readlinkatWasi(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 { var bufused: usize = undefined; switch (wasi.path_readlink(dirfd, file_path.ptr, file_path.len, out_buffer.ptr, out_buffer.len, &bufused)) { wasi.ESUCCESS => return out_buffer[0..bufused], @@ -2454,20 +2433,20 @@ pub fn readlinkatWasi(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) Read wasi.ENOENT => return error.FileNotFound, wasi.ENOMEM => return error.SystemResources, wasi.ENOTDIR => return error.NotDir, - wasi.ENOTCAPABLE => return error.NotCapable, + wasi.ENOTCAPABLE => return error.AccessDenied, else => |err| return unexpectedErrno(err), } } /// Windows-only. Same as `readlinkat` except `file_path` is null-terminated, WTF16 encoded. /// See also `readlinkat`. -pub fn readlinkatW(dirfd: fd_t, file_path: [*:0]const u16, out_buffer: []u8) ReadLinkAtError![]u8 { +pub fn readlinkatW(dirfd: fd_t, file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 { @compileError("TODO implement on Windows"); } /// Same as `readlinkat` except `file_path` is null-terminated. /// See also `readlinkat`. -pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) ReadLinkAtError![]u8 { +pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 { if (builtin.os.tag == .windows) { const file_path_w = try windows.cStrToPrefixedFileW(file_path); return readlinkatW(dirfd, file_path_w.span().ptr, out_buffer); @@ -3132,11 +3111,10 @@ pub fn waitpid(pid: i32, flags: u32) u32 { pub const FStatError = error{ SystemResources, - AccessDenied, - /// WASI-only. This error occurs when the file descriptor does + /// In WASI, this error may occur when the file descriptor does /// not hold the required rights to get its filestat information. - NotCapable, + AccessDenied, } || UnexpectedError; /// Return information about a file descriptor. @@ -3149,7 +3127,7 @@ pub fn fstat(fd: fd_t) FStatError!Stat { wasi.EBADF => unreachable, // Always a race condition. wasi.ENOMEM => return error.SystemResources, wasi.EACCES => return error.AccessDenied, - wasi.ENOTCAPABLE => return error.NotCapable, + wasi.ENOTCAPABLE => return error.AccessDenied, else => |err| return unexpectedErrno(err), } } @@ -3200,7 +3178,7 @@ pub fn fstatatWasi(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!S wasi.ENAMETOOLONG => return error.NameTooLong, wasi.ENOENT => return error.FileNotFound, wasi.ENOTDIR => return error.FileNotFound, - wasi.ENOTCAPABLE => return error.NotCapable, + wasi.ENOTCAPABLE => return error.AccessDenied, else => |err| return unexpectedErrno(err), } } @@ -3709,9 +3687,9 @@ pub fn gettimeofday(tv: ?*timeval, tz: ?*timezone) void { pub const SeekError = error{ Unseekable, - /// WASI-only. This error occurs when the file descriptor does + /// In WASI, this error may occur when the file descriptor does /// not hold the required rights to seek on it. - NotCapable, + AccessDenied, } || UnexpectedError; /// Repositions read/write file offset relative to the beginning. @@ -3740,7 +3718,7 @@ pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void { wasi.EOVERFLOW => return error.Unseekable, wasi.ESPIPE => return error.Unseekable, wasi.ENXIO => return error.Unseekable, - wasi.ENOTCAPABLE => return error.NotCapable, + wasi.ENOTCAPABLE => return error.AccessDenied, else => |err| return unexpectedErrno(err), } } @@ -3782,7 +3760,7 @@ pub fn lseek_CUR(fd: fd_t, offset: i64) SeekError!void { wasi.EOVERFLOW => return error.Unseekable, wasi.ESPIPE => return error.Unseekable, wasi.ENXIO => return error.Unseekable, - wasi.ENOTCAPABLE => return error.NotCapable, + wasi.ENOTCAPABLE => return error.AccessDenied, else => |err| return unexpectedErrno(err), } } @@ -3823,7 +3801,7 @@ pub fn lseek_END(fd: fd_t, offset: i64) SeekError!void { wasi.EOVERFLOW => return error.Unseekable, wasi.ESPIPE => return error.Unseekable, wasi.ENXIO => return error.Unseekable, - wasi.ENOTCAPABLE => return error.NotCapable, + wasi.ENOTCAPABLE => return error.AccessDenied, else => |err| return unexpectedErrno(err), } } @@ -3864,7 +3842,7 @@ pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 { wasi.EOVERFLOW => return error.Unseekable, wasi.ESPIPE => return error.Unseekable, wasi.ENXIO => return error.Unseekable, - wasi.ENOTCAPABLE => return error.NotCapable, + wasi.ENOTCAPABLE => return error.AccessDenied, else => |err| return unexpectedErrno(err), } } @@ -4012,7 +3990,6 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP if (builtin.os.tag == .linux and !builtin.link_libc) { const fd = openZ(pathname, linux.O_PATH | linux.O_NONBLOCK | linux.O_CLOEXEC, 0) catch |err| switch (err) { error.FileLocksNotSupported => unreachable, - error.NotCapable => unreachable, // WASI only else => |e| return e, }; defer close(fd); diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 166dbf4c43..2b32e39624 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -499,7 +499,6 @@ pub const NativeTargetInfo = struct { error.PipeBusy => unreachable, error.FileLocksNotSupported => unreachable, error.WouldBlock => unreachable, - error.NotCapable => unreachable, // we don't support WASI here (not yet at least) error.IsDir, error.NotDir, @@ -791,7 +790,6 @@ pub const NativeTargetInfo = struct { var it = mem.tokenize(rpath_list, ":"); while (it.next()) |rpath| { var dir = fs.cwd().openDir(rpath, .{}) catch |err| switch (err) { - error.NotCapable => unreachable, // we don't support WASI here (not yet at least) error.NameTooLong => unreachable, error.InvalidUtf8 => unreachable, error.BadPathName => unreachable, @@ -819,7 +817,6 @@ pub const NativeTargetInfo = struct { &link_buf, ) catch |err| switch (err) { error.NameTooLong => unreachable, - error.NotCapable => unreachable, // we don't support WASI here (not yet at least) error.AccessDenied, error.FileNotFound, @@ -854,7 +851,6 @@ pub const NativeTargetInfo = struct { const len = file.pread(buf[i .. buf.len - i], offset + i) catch |err| switch (err) { error.OperationAborted => unreachable, // Windows-only error.WouldBlock => unreachable, // Did not request blocking mode - error.NotCapable => unreachable, // WASI only, and we don't support it here yet error.SystemResources => return error.SystemResources, error.IsDir => return error.UnableToReadElfFile, error.BrokenPipe => return error.UnableToReadElfFile, @@ -863,6 +859,7 @@ pub const NativeTargetInfo = struct { error.ConnectionTimedOut => return error.UnableToReadElfFile, error.Unexpected => return error.Unexpected, error.InputOutput => return error.FileSystem, + error.AccessDenied => return error.Unexpected, }; if (len == 0) return error.UnexpectedEndOfFile; i += len; diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig index 60076b9cdb..bd24ffb399 100644 --- a/src-self-hosted/stage2.zig +++ b/src-self-hosted/stage2.zig @@ -163,7 +163,6 @@ export fn stage2_render_ast(tree: *ast.Tree, output_file: *FILE) Error { error.OutOfMemory => return .OutOfMemory, error.Unexpected => return .Unexpected, error.InputOutput => return .FileSystem, - error.NotCapable => unreachable, // we should handle this when we're able to cross-compile Zig to WASI }; return .None; } @@ -607,7 +606,6 @@ export fn stage2_libc_parse(stage1_libc: *Stage2LibCInstallation, libc_file_z: [ error.NotDir => return .NotDir, error.DeviceBusy => return .DeviceBusy, error.FileLocksNotSupported => unreachable, - error.NotCapable => unreachable, // we should handle this when we're able to cross-compile Zig to WASI }; stage1_libc.initFromStage2(libc); return .None; @@ -651,7 +649,6 @@ export fn stage2_libc_render(stage1_libc: *Stage2LibCInstallation, output_file: error.AccessDenied => return .AccessDenied, error.Unexpected => return .Unexpected, error.InputOutput => return .FileSystem, - error.NotCapable => unreachable, // we should handle this when we're able to cross-compile Zig to WASI }; return .None; } From f69875d85cc80e37df4572772ec1488ba67de262 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 1 Jul 2020 21:56:30 +0000 Subject: [PATCH 16/19] build: -Dlib-files-only prevents self-hosted compiler from being built closes #5756 --- build.zig | 53 ++++++++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/build.zig b/build.zig index fd513de54f..3daef75a58 100644 --- a/build.zig +++ b/build.zig @@ -49,11 +49,6 @@ pub fn build(b: *Builder) !void { const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"}); - var exe = b.addExecutable("zig", "src-self-hosted/main.zig"); - exe.setBuildMode(mode); - test_step.dependOn(&exe.step); - b.default_step.dependOn(&exe.step); - const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false; const skip_release_small = b.option(bool, "skip-release-small", "Main test suite skips release-small builds") orelse skip_release; const skip_release_fast = b.option(bool, "skip-release-fast", "Main test suite skips release-fast builds") orelse skip_release; @@ -63,29 +58,37 @@ pub fn build(b: *Builder) !void { const only_install_lib_files = b.option(bool, "lib-files-only", "Only install library files") orelse false; const enable_llvm = b.option(bool, "enable-llvm", "Build self-hosted compiler with LLVM backend enabled") orelse false; - if (enable_llvm) { - var ctx = parseConfigH(b, config_h_text); - ctx.llvm = try findLLVM(b, ctx.llvm_config_exe); - try configureStage2(b, exe, ctx); - } if (!only_install_lib_files) { - exe.install(); - } - const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source"); - const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse false; - if (link_libc) exe.linkLibC(); + var exe = b.addExecutable("zig", "src-self-hosted/main.zig"); + exe.setBuildMode(mode); + test_step.dependOn(&exe.step); + b.default_step.dependOn(&exe.step); - exe.addBuildOption(bool, "enable_tracy", tracy != null); - if (tracy) |tracy_path| { - const client_cpp = fs.path.join( - b.allocator, - &[_][]const u8{ tracy_path, "TracyClient.cpp" }, - ) catch unreachable; - exe.addIncludeDir(tracy_path); - exe.addCSourceFile(client_cpp, &[_][]const u8{ "-DTRACY_ENABLE=1", "-fno-sanitize=undefined" }); - exe.linkSystemLibraryName("c++"); - exe.linkLibC(); + if (enable_llvm) { + var ctx = parseConfigH(b, config_h_text); + ctx.llvm = try findLLVM(b, ctx.llvm_config_exe); + + try configureStage2(b, exe, ctx); + } + if (!only_install_lib_files) { + exe.install(); + } + const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source"); + const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse false; + if (link_libc) exe.linkLibC(); + + exe.addBuildOption(bool, "enable_tracy", tracy != null); + if (tracy) |tracy_path| { + const client_cpp = fs.path.join( + b.allocator, + &[_][]const u8{ tracy_path, "TracyClient.cpp" }, + ) catch unreachable; + exe.addIncludeDir(tracy_path); + exe.addCSourceFile(client_cpp, &[_][]const u8{ "-DTRACY_ENABLE=1", "-fno-sanitize=undefined" }); + exe.linkSystemLibraryName("c++"); + exe.linkLibC(); + } } b.installDirectory(InstallDirectoryOptions{ From 7eed220924062aa68586278568df6731afb2a20e Mon Sep 17 00:00:00 2001 From: CodeMyst Date: Wed, 1 Jul 2020 21:44:18 +0200 Subject: [PATCH 17/19] in docs removed "path can be absolute" for imports --- doc/langref.html.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 95b7171c44..aef7b09798 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -7530,7 +7530,7 @@ test "@hasDecl" { source file than the one they are declared in.

- {#syntax#}path{#endsyntax#} can be a relative or absolute path, or it can be the name of a package. + {#syntax#}path{#endsyntax#} can be a relative path or it can be the name of a package. If it is a relative path, it is relative to the file that contains the {#syntax#}@import{#endsyntax#} function call.

From b8d5b3e6110023e17e1c191afa1d143f8b71fe4f Mon Sep 17 00:00:00 2001 From: Chris Watson Date: Tue, 30 Jun 2020 19:52:02 -0600 Subject: [PATCH 18/19] Add documentation for @src() builtin --- doc/langref.html.in | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/doc/langref.html.in b/doc/langref.html.in index aef7b09798..dfbb93decf 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -8030,7 +8030,29 @@ test "vector @splat" {

{#see_also|Vectors|@shuffle#} {#header_close#} + {#header_open|@src#} +
{#syntax#}@src() std.builtin.SourceLocation{#endsyntax#}
+

+ Returns a {#syntax#}SourceLocation{#endsyntax#} struct representing the function's name and location in the source code. This must be called in a function. +

+ {#code_begin|test#} +const std = @import("std"); +const expect = std.testing.expect; +test "@src" { + doTheTest(); +} + +fn doTheTest() void { + const src = @src(); + + expect(src.line == 9); + expect(src.column == 17); + expect(std.mem.endsWith(u8, src.fn_name, "doTheTest")); + expect(std.mem.endsWith(u8, src.file, "test.zig")); +} + {#code_end#} + {#header_close#} {#header_open|@sqrt#}
{#syntax#}@sqrt(value: var) @TypeOf(value){#endsyntax#}

From 30ae7f7573b7dcc3cd85f9e4cbab4e5608fdb3dd Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Wed, 1 Jul 2020 00:33:35 +0300 Subject: [PATCH 19/19] Corrected default value field initialization in std.zeroInit --- lib/std/mem.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 6bde83f782..08ecc5167f 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -720,8 +720,8 @@ pub fn zeroInit(comptime T: type, init: var) T { @field(value, field.name) = @field(init, field.name); }, } - } else if (field.default_value != null) { - @field(value, field.name) = field.default_value; + } else if (field.default_value) |default_value| { + @field(value, field.name) = default_value; } } @@ -748,7 +748,7 @@ test "zeroInit" { b: ?bool, c: I, e: [3]u8, - f: i64, + f: i64 = -1, }; const s = zeroInit(S, .{ @@ -762,7 +762,7 @@ test "zeroInit" { .d = 0, }, .e = [3]u8{ 0, 0, 0 }, - .f = 0, + .f = -1, }); }