From aa3e0ff454d06407b4ee347c1cd0c1e09444c52c Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Wed, 3 Feb 2021 22:15:18 +0100 Subject: [PATCH 01/20] Create type declarations for extern functions and write the 'import' section --- lib/std/wasm.zig | 14 ++++++++++ src/codegen/wasm.zig | 28 +++++++++++++------ src/link/Wasm.zig | 66 +++++++++++++++++++++++++++++++++++++++----- 3 files changed, 93 insertions(+), 15 deletions(-) diff --git a/lib/std/wasm.zig b/lib/std/wasm.zig index aa087c89c9..c59117d65b 100644 --- a/lib/std/wasm.zig +++ b/lib/std/wasm.zig @@ -253,6 +253,20 @@ pub fn section(val: Section) u8 { return @enumToInt(val); } +/// The kind of the type when importing or exporting to/from the host environment +/// https://webassembly.github.io/spec/core/syntax/modules.html +pub const ExternalKind = enum(u8) { + function, + table, + memory, + global, +}; + +/// Returns the integer value of a given `ExternalKind` +pub fn kind(val: ExternalKind) u8 { + return @enumToInt(val); +} + // types pub const element_type: u8 = 0x70; pub const function_type: u8 = 0x60; diff --git a/src/codegen/wasm.zig b/src/codegen/wasm.zig index abc411b551..5be26de7cb 100644 --- a/src/codegen/wasm.zig +++ b/src/codegen/wasm.zig @@ -163,13 +163,18 @@ pub const Context = struct { try self.genFunctype(); const writer = self.code.writer(); - // Reserve space to write the size after generating the code as well as space for locals count - try self.code.resize(10); - // Write instructions // TODO: check for and handle death of instructions const tv = self.decl.typed_value.most_recent.typed_value; - const mod_fn = tv.val.castTag(.function).?.data; + const mod_fn = blk: { + if (tv.val.castTag(.function)) |func| break :blk func.data; + if (tv.val.castTag(.extern_fn)) |ext_fn| return; // don't need codegen for extern functions + return self.fail(self.decl.src(), "TODO: Wasm codegen for decl type '{s}'", .{tv.ty.tag()}); + }; + + // Reserve space to write the size after generating the code as well as space for locals count + try self.code.resize(10); + try self.genBody(mod_fn.body); // finally, write our local types at the 'offset' position @@ -239,9 +244,16 @@ pub const Context = struct { fn genCall(self: *Context, inst: *Inst.Call) InnerError!WValue { const func_inst = inst.func.castTag(.constant).?; - const func = func_inst.val.castTag(.function).?.data; - const target = func.owner_decl; - const target_ty = target.typed_value.most_recent.typed_value.ty; + const func_val = inst.func.value().?; + + const target = blk: { + if (func_val.castTag(.function)) |func| { + break :blk func.data.owner_decl; + } else if (func_val.castTag(.extern_fn)) |ext_fn| { + break :blk ext_fn.data; + } + return self.fail(inst.base.src, "Expected a function, but instead found type '{s}'", .{func_val.tag()}); + }; for (inst.args) |arg| { const arg_val = self.resolveInst(arg); @@ -495,7 +507,7 @@ pub const Context = struct { } fn genBr(self: *Context, br: *Inst.Br) InnerError!WValue { - // of operand has codegen bits we should break with a value + // if operand has codegen bits we should break with a value if (br.operand.ty.hasCodeGenBits()) { const operand = self.resolveInst(br.operand); try self.emitWValue(operand); diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 547ab2a012..9660ff0a43 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -85,8 +85,6 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { const typed_value = decl.typed_value.most_recent.typed_value; if (typed_value.ty.zigTypeTag() != .Fn) return error.TODOImplementNonFnDeclsForWasm; - if (typed_value.val.tag() == .extern_fn) - return error.TODOImplementExternFnDeclsForWasm; if (decl.fn_link.wasm) |*fn_data| { fn_data.functype.items.len = 0; @@ -184,17 +182,63 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { ); } + // Import section + { + // TODO: implement non-functions imports + const header_offset = try reserveVecSectionHeader(file); + const writer = file.writer(); + var count: u32 = 0; + for (self.funcs.items) |decl, typeidx| { + if (decl.typed_value.most_recent.typed_value.val.tag() != .extern_fn) { + continue; + } + + // TODO: can we set/save the module name somewhere? + // For now, emit "env" like LLVM does + const module_name = "env"; + try leb.writeULEB128(writer, @intCast(u32, module_name.len)); + try writer.writeAll(module_name); + + // wasm requires the length of the import name and doesn't require a null-termination + const decl_len = mem.len(decl.name); + try leb.writeULEB128(writer, @intCast(u32, decl_len)); + try writer.writeAll(decl.name[0..decl_len]); + + // emit kind and the function type + try writer.writeByte(wasm.kind(.function)); + try leb.writeULEB128(writer, @intCast(u32, typeidx)); + + count += 1; + } + + try writeVecSectionHeader( + file, + header_offset, + .import, + @intCast(u32, (try file.getPos()) - header_offset - header_size), + count, + ); + } + // Function section { const header_offset = try reserveVecSectionHeader(file); const writer = file.writer(); - for (self.funcs.items) |_, typeidx| try leb.writeULEB128(writer, @intCast(u32, typeidx)); + var count: u32 = 0; + for (self.funcs.items) |decl, typeidx| { + // Extern functions only have a type, so skip the function signature section + if (decl.typed_value.most_recent.typed_value.val.tag() != .function) { + continue; + } + try leb.writeULEB128(writer, @intCast(u32, typeidx)); + count += 1; + } try writeVecSectionHeader( file, header_offset, .function, @intCast(u32, (try file.getPos()) - header_offset - header_size), - @intCast(u32, self.funcs.items.len), + count, ); } @@ -214,7 +258,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { // Type of the export try writer.writeByte(0x00); // Exported function index - try leb.writeULEB128(writer, self.getFuncidx(exprt.exported_decl).?); + try leb.writeULEB128(writer, self.getFuncidx(exprt.exported_decl).? + 1); }, else => return error.TODOImplementNonFnDeclsForWasm, } @@ -235,7 +279,13 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { { const header_offset = try reserveVecSectionHeader(file); const writer = file.writer(); + var count: u32 = 0; for (self.funcs.items) |decl| { + // Do not emit any code for extern functions + if (decl.typed_value.most_recent.typed_value.val.tag() != .function) { + std.debug.print("Skipping decl: {s}\n", .{decl.name}); + continue; + } const fn_data = &decl.fn_link.wasm.?; // Write the already generated code to the file, inserting @@ -247,18 +297,20 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { // Use a fixed width here to make calculating the code size // in codegen.wasm.gen() simpler. var buf: [5]u8 = undefined; - leb.writeUnsignedFixed(5, &buf, self.getFuncidx(idx_ref.decl).?); + std.debug.print("idx_ref: {s} - {d}\n", .{ idx_ref.decl.name, self.getFuncidx(idx_ref.decl).? }); + leb.writeUnsignedFixed(5, &buf, self.getFuncidx(idx_ref.decl).? - 1); try writer.writeAll(&buf); } try writer.writeAll(fn_data.code.items[current..]); + count += 1; } try writeVecSectionHeader( file, header_offset, .code, @intCast(u32, (try file.getPos()) - header_offset - header_size), - @intCast(u32, self.funcs.items.len), + count, ); } } From 36df6a008fb009fd8fd1d3362fc850cf97723dcd Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Thu, 4 Feb 2021 21:08:19 +0100 Subject: [PATCH 02/20] Ensure function indices are correct and fix a memory leak --- lib/std/wasm.zig | 2 +- src/codegen/wasm.zig | 2 +- src/link/Wasm.zig | 118 ++++++++++++++++++++++++++----------------- 3 files changed, 73 insertions(+), 49 deletions(-) diff --git a/lib/std/wasm.zig b/lib/std/wasm.zig index c59117d65b..a04378d283 100644 --- a/lib/std/wasm.zig +++ b/lib/std/wasm.zig @@ -263,7 +263,7 @@ pub const ExternalKind = enum(u8) { }; /// Returns the integer value of a given `ExternalKind` -pub fn kind(val: ExternalKind) u8 { +pub fn externalKind(val: ExternalKind) u8 { return @enumToInt(val); } diff --git a/src/codegen/wasm.zig b/src/codegen/wasm.zig index 5be26de7cb..34e0b2f9b5 100644 --- a/src/codegen/wasm.zig +++ b/src/codegen/wasm.zig @@ -161,7 +161,6 @@ pub const Context = struct { pub fn gen(self: *Context) InnerError!void { assert(self.code.items.len == 0); try self.genFunctype(); - const writer = self.code.writer(); // Write instructions // TODO: check for and handle death of instructions @@ -194,6 +193,7 @@ pub const Context = struct { } } + const writer = self.code.writer(); try writer.writeByte(wasm.opcode(.end)); // Fill in the size of the generated code to the reserved space at the diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 9660ff0a43..bd89ce3345 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -33,9 +33,18 @@ base: link.File, /// List of all function Decls to be written to the output file. The index of /// each Decl in this list at the time of writing the binary is used as the -/// function index. +/// function index. In the event where ext_funcs' size is not 0, the index of +/// each function is added on top of the ext_funcs' length. /// TODO: can/should we access some data structure in Module directly? funcs: std.ArrayListUnmanaged(*Module.Decl) = .{}, +/// List of all extern function Decls to be written to the `import` section of the +/// wasm binary. The positin in the list defines the function index +ext_funcs: std.ArrayListUnmanaged(*Module.Decl) = .{}, +/// When importing objects from the host environment, a name must be supplied. +/// LLVM uses "env" by default when none is given. This would be a good default for Zig +/// to support existing code. +/// TODO: Allow setting this through a flag? +host_name: []const u8 = "env", pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Wasm { assert(options.object_format == .wasm); @@ -76,7 +85,13 @@ pub fn deinit(self: *Wasm) void { decl.fn_link.wasm.?.code.deinit(self.base.allocator); decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator); } + for (self.ext_funcs.items) |decl| { + decl.fn_link.wasm.?.functype.deinit(self.base.allocator); + decl.fn_link.wasm.?.code.deinit(self.base.allocator); + decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator); + } self.funcs.deinit(self.base.allocator); + self.ext_funcs.deinit(self.base.allocator); } // Generate code for the Decl, storing it in memory to be later written to @@ -92,7 +107,12 @@ pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { fn_data.idx_refs.items.len = 0; } else { decl.fn_link.wasm = .{}; - try self.funcs.append(self.base.allocator, decl); + // dependent on function type, appends it to the correct list + switch (decl.typed_value.most_recent.typed_value.val.tag()) { + .function => try self.funcs.append(self.base.allocator, decl), + .extern_fn => try self.ext_funcs.append(self.base.allocator, decl), + else => return error.TODOImplementNonFnDeclsForWasm, + } } const fn_data = &decl.fn_link.wasm.?; @@ -141,7 +161,12 @@ pub fn updateDeclExports( pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void { // TODO: remove this assert when non-function Decls are implemented assert(decl.typed_value.most_recent.typed_value.ty.zigTypeTag() == .Fn); - _ = self.funcs.swapRemove(self.getFuncidx(decl).?); + const func_idx = self.getFuncidx(decl).?; + switch (decl.typed_value.most_recent.typed_value.val.tag()) { + .function => _ = self.funcs.swapRemove(func_idx), + .extern_fn => _ = self.ext_funcs.swapRemove(func_idx), + else => unreachable, + } decl.fn_link.wasm.?.functype.deinit(self.base.allocator); decl.fn_link.wasm.?.code.deinit(self.base.allocator); decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator); @@ -170,15 +195,18 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { // Type section { const header_offset = try reserveVecSectionHeader(file); - for (self.funcs.items) |decl| { - try file.writeAll(decl.fn_link.wasm.?.functype.items); - } + + // extern functions are defined in the wasm binary first through the `import` + // section, so define their func types first + for (self.ext_funcs.items) |decl| try file.writeAll(decl.fn_link.wasm.?.functype.items); + for (self.funcs.items) |decl| try file.writeAll(decl.fn_link.wasm.?.functype.items); + try writeVecSectionHeader( file, header_offset, .type, @intCast(u32, (try file.getPos()) - header_offset - header_size), - @intCast(u32, self.funcs.items.len), + @intCast(u32, self.ext_funcs.items.len + self.funcs.items.len), ); } @@ -187,28 +215,18 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { // TODO: implement non-functions imports const header_offset = try reserveVecSectionHeader(file); const writer = file.writer(); - var count: u32 = 0; - for (self.funcs.items) |decl, typeidx| { - if (decl.typed_value.most_recent.typed_value.val.tag() != .extern_fn) { - continue; - } + for (self.ext_funcs.items) |decl, typeidx| { + try leb.writeULEB128(writer, @intCast(u32, self.host_name.len)); + try writer.writeAll(self.host_name); - // TODO: can we set/save the module name somewhere? - // For now, emit "env" like LLVM does - const module_name = "env"; - try leb.writeULEB128(writer, @intCast(u32, module_name.len)); - try writer.writeAll(module_name); - - // wasm requires the length of the import name and doesn't require a null-termination + // wasm requires the length of the import name with no null-termination const decl_len = mem.len(decl.name); try leb.writeULEB128(writer, @intCast(u32, decl_len)); try writer.writeAll(decl.name[0..decl_len]); // emit kind and the function type - try writer.writeByte(wasm.kind(.function)); + try writer.writeByte(wasm.externalKind(.function)); try leb.writeULEB128(writer, @intCast(u32, typeidx)); - - count += 1; } try writeVecSectionHeader( @@ -216,7 +234,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { header_offset, .import, @intCast(u32, (try file.getPos()) - header_offset - header_size), - count, + @intCast(u32, self.ext_funcs.items.len), ); } @@ -224,21 +242,17 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { { const header_offset = try reserveVecSectionHeader(file); const writer = file.writer(); - var count: u32 = 0; - for (self.funcs.items) |decl, typeidx| { - // Extern functions only have a type, so skip the function signature section - if (decl.typed_value.most_recent.typed_value.val.tag() != .function) { - continue; - } - try leb.writeULEB128(writer, @intCast(u32, typeidx)); - count += 1; + for (self.funcs.items) |_, typeidx| { + const func_idx = @intCast(u32, self.getFuncIdxOffset() + typeidx); + try leb.writeULEB128(writer, func_idx); } + try writeVecSectionHeader( file, header_offset, .function, @intCast(u32, (try file.getPos()) - header_offset - header_size), - count, + @intCast(u32, self.funcs.items.len), ); } @@ -256,9 +270,9 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { switch (exprt.exported_decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) { .Fn => { // Type of the export - try writer.writeByte(0x00); + try writer.writeByte(wasm.externalKind(.function)); // Exported function index - try leb.writeULEB128(writer, self.getFuncidx(exprt.exported_decl).? + 1); + try leb.writeULEB128(writer, self.getFuncidx(exprt.exported_decl).?); }, else => return error.TODOImplementNonFnDeclsForWasm, } @@ -279,13 +293,7 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { { const header_offset = try reserveVecSectionHeader(file); const writer = file.writer(); - var count: u32 = 0; for (self.funcs.items) |decl| { - // Do not emit any code for extern functions - if (decl.typed_value.most_recent.typed_value.val.tag() != .function) { - std.debug.print("Skipping decl: {s}\n", .{decl.name}); - continue; - } const fn_data = &decl.fn_link.wasm.?; // Write the already generated code to the file, inserting @@ -297,20 +305,18 @@ pub fn flushModule(self: *Wasm, comp: *Compilation) !void { // Use a fixed width here to make calculating the code size // in codegen.wasm.gen() simpler. var buf: [5]u8 = undefined; - std.debug.print("idx_ref: {s} - {d}\n", .{ idx_ref.decl.name, self.getFuncidx(idx_ref.decl).? }); - leb.writeUnsignedFixed(5, &buf, self.getFuncidx(idx_ref.decl).? - 1); + leb.writeUnsignedFixed(5, &buf, self.getFuncidx(idx_ref.decl).?); try writer.writeAll(&buf); } try writer.writeAll(fn_data.code.items[current..]); - count += 1; } try writeVecSectionHeader( file, header_offset, .code, @intCast(u32, (try file.getPos()) - header_offset - header_size), - count, + @intCast(u32, self.funcs.items.len), ); } } @@ -575,13 +581,31 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { } /// Get the current index of a given Decl in the function list -/// TODO: we could maintain a hash map to potentially make this +/// This will correctly provide the index, regardless whether the function is extern or not +/// TODO: we could maintain a hash map to potentially make this simpler fn getFuncidx(self: Wasm, decl: *Module.Decl) ?u32 { - return for (self.funcs.items) |func, idx| { - if (func == decl) break @intCast(u32, idx); + var offset: u32 = 0; + const slice = switch (decl.typed_value.most_recent.typed_value.val.tag()) { + .function => blk: { + // when the target is a regular function, we have to calculate + // the offset of where the index starts + offset += self.getFuncIdxOffset(); + break :blk self.funcs.items; + }, + .extern_fn => self.ext_funcs.items, + else => return null, + }; + return for (slice) |func, idx| { + if (func == decl) break @intCast(u32, offset + idx); } else null; } +/// Based on the size of `ext_funcs` returns the +/// offset of the function indices +fn getFuncIdxOffset(self: Wasm) u32 { + return @intCast(u32, self.ext_funcs.items.len); +} + fn reserveVecSectionHeader(file: fs.File) !u64 { // section id + fixed leb contents size + fixed leb vector length const header_size = 1 + 5 + 5; From 9270aae071a4ee840193afe1162b24945cbd6d9e Mon Sep 17 00:00:00 2001 From: Tadeo Kondrak Date: Fri, 12 Feb 2021 13:40:44 -0700 Subject: [PATCH 03/20] stage2: fix zero-sized function parameters (#7998) --- src/codegen.zig | 25 +++++++++++++++---------- test/stage2/test.zig | 22 ++++++++++++++++++++++ 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/src/codegen.zig b/src/codegen.zig index 9771386403..d81ad1faf5 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -3705,17 +3705,22 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { for (param_types) |ty, i| { switch (ty.zigTypeTag()) { .Bool, .Int => { - const param_size = @intCast(u32, ty.abiSize(self.target.*)); - if (next_int_reg >= c_abi_int_param_regs.len) { - result.args[i] = .{ .stack_offset = next_stack_offset }; - next_stack_offset += param_size; + if (!ty.hasCodeGenBits()) { + assert(cc != .C); + result.args[i] = .{ .none = {} }; } else { - const aliased_reg = registerAlias( - c_abi_int_param_regs[next_int_reg], - param_size, - ); - result.args[i] = .{ .register = aliased_reg }; - next_int_reg += 1; + const param_size = @intCast(u32, ty.abiSize(self.target.*)); + if (next_int_reg >= c_abi_int_param_regs.len) { + result.args[i] = .{ .stack_offset = next_stack_offset }; + next_stack_offset += param_size; + } else { + const aliased_reg = registerAlias( + c_abi_int_param_regs[next_int_reg], + param_size, + ); + result.args[i] = .{ .register = aliased_reg }; + next_int_reg += 1; + } } }, else => return self.fail(src, "TODO implement function parameters of type {s}", .{@tagName(ty.zigTypeTag())}), diff --git a/test/stage2/test.zig b/test/stage2/test.zig index 486edeb864..b5de03524f 100644 --- a/test/stage2/test.zig +++ b/test/stage2/test.zig @@ -1393,4 +1393,26 @@ pub fn addCases(ctx: *TestContext) !void { "", ); } + + { + var case = ctx.exe("passing u0 to function", linux_x64); + case.addCompareOutput( + \\export fn _start() noreturn { + \\ doNothing(0); + \\ exit(); + \\} + \\fn doNothing(arg: u0) void {} + \\fn exit() noreturn { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (231), + \\ [arg1] "{rdi}" (0) + \\ : "rcx", "r11", "memory" + \\ ); + \\ unreachable; + \\} + , + "", + ); + } } From 68e7726478f89ca27127e68b79112c12ac7415f7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 16 Feb 2021 11:01:17 -0700 Subject: [PATCH 04/20] std.fs.net.Stream: add writev and writevAll I noticed that the write function does not properly use non-blocking I/O. This file needs to be reworked for evented I/O to properly take advantage of non-blocking writes to network sockets. --- lib/std/fs/file.zig | 2 ++ lib/std/net.zig | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig index 5138555723..baea3b4e7f 100644 --- a/lib/std/fs/file.zig +++ b/lib/std/fs/file.zig @@ -587,6 +587,7 @@ pub const File = struct { } /// See https://github.com/ziglang/zig/issues/7699 + /// See equivalent function: `std.net.Stream.writev`. pub fn writev(self: File, iovecs: []const os.iovec_const) WriteError!usize { if (is_windows) { // TODO improve this to use WriteFileScatter @@ -605,6 +606,7 @@ pub const File = struct { /// The `iovecs` parameter is mutable because this function needs to mutate the fields in /// order to handle partial writes from the underlying OS layer. /// See https://github.com/ziglang/zig/issues/7699 + /// See equivalent function: `std.net.Stream.writevAll`. pub fn writevAll(self: File, iovecs: []os.iovec_const) WriteError!void { if (iovecs.len == 0) return; diff --git a/lib/std/net.zig b/lib/std/net.zig index 28ae2b9857..2d62a27cd9 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -1621,6 +1621,9 @@ pub const Stream = struct { } } + /// TODO in evented I/O mode, this implementation incorrectly uses the event loop's + /// file system thread instead of non-blocking. It needs to be reworked to properly + /// use non-blocking I/O. pub fn write(self: Stream, buffer: []const u8) WriteError!usize { if (std.Target.current.os.tag == .windows) { return os.windows.WriteFile(self.handle, buffer, null, io.default_mode); @@ -1632,6 +1635,40 @@ pub const Stream = struct { return os.write(self.handle, buffer); } } + + /// See https://github.com/ziglang/zig/issues/7699 + /// See equivalent function: `std.fs.File.writev`. + pub fn writev(self: Stream, iovecs: []const os.iovec_const) WriteError!usize { + if (std.io.is_async) { + // TODO improve to actually take advantage of writev syscall, if available. + if (iovecs.len == 0) return 0; + const first_buffer = iovecs[0].iov_base[0..iovecs[0].iov_len]; + try self.write(first_buffer); + return first_buffer.len; + } else { + return os.writev(self.handle, iovecs); + } + } + + /// The `iovecs` parameter is mutable because this function needs to mutate the fields in + /// order to handle partial writes from the underlying OS layer. + /// See https://github.com/ziglang/zig/issues/7699 + /// See equivalent function: `std.fs.File.writevAll`. + pub fn writevAll(self: Stream, iovecs: []os.iovec_const) WriteError!void { + if (iovecs.len == 0) return; + + var i: usize = 0; + while (true) { + var amt = try self.writev(iovecs[i..]); + while (amt >= iovecs[i].iov_len) { + amt -= iovecs[i].iov_len; + i += 1; + if (i >= iovecs.len) return; + } + iovecs[i].iov_base += amt; + iovecs[i].iov_len -= amt; + } + } }; pub const StreamServer = struct { From 153cd4da0c723fe949bbf9eaf9d780500320bffc Mon Sep 17 00:00:00 2001 From: Michael Dusan Date: Fri, 19 Feb 2021 15:52:34 -0500 Subject: [PATCH 05/20] macos: fix cond to enable ZIG_SYSTEM_LINKER_HACK closes #8037 --- src/Compilation.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index c7bb260aa7..ae3385b2dc 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -645,7 +645,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { }; const darwin_options: DarwinOptions = if (build_options.have_llvm and comptime std.Target.current.isDarwin()) outer: { - const opts: DarwinOptions = if (use_lld and options.is_native_os and options.target.isDarwin()) inner: { + const opts: DarwinOptions = if (use_lld and std.builtin.os.tag == .macos and options.target.isDarwin()) inner: { // TODO Revisit this targeting versions lower than macOS 11 when LLVM 12 is out. // See https://github.com/ziglang/zig/issues/6996 const at_least_big_sur = options.target.os.getVersionRange().semver.min.major >= 11; From a5dcd07382aca0d429d31342de28f786c5f7fd20 Mon Sep 17 00:00:00 2001 From: rgreenblatt Date: Sat, 20 Feb 2021 18:02:10 -0500 Subject: [PATCH 06/20] fix unspecified fmt --- lib/std/build.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index 77ca854f15..65a53ef868 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -543,7 +543,7 @@ pub const Builder = struct { .Scalar => |s| { const n = std.fmt.parseInt(T, s, 10) catch |err| switch (err) { error.Overflow => { - warn("-D{s} value {} cannot fit into type {s}.\n\n", .{ name, s, @typeName(T) }); + warn("-D{s} value {s} cannot fit into type {s}.\n\n", .{ name, s, @typeName(T) }); self.markInvalidUserInput(); return null; }, From 340825a7afeb1909ab81c1e0258b81ff67323a2b Mon Sep 17 00:00:00 2001 From: data-man Date: Sat, 20 Feb 2021 21:10:08 +0500 Subject: [PATCH 07/20] Add epoll_pwait2 Linux syscall --- lib/std/os/bits/linux/arm-eabi.zig | 1 + lib/std/os/bits/linux/arm64.zig | 1 + lib/std/os/bits/linux/i386.zig | 1 + lib/std/os/bits/linux/mips.zig | 1 + lib/std/os/bits/linux/powerpc64.zig | 1 + lib/std/os/bits/linux/riscv64.zig | 1 + lib/std/os/bits/linux/sparc64.zig | 1 + lib/std/os/bits/linux/x86_64.zig | 1 + 8 files changed, 8 insertions(+) diff --git a/lib/std/os/bits/linux/arm-eabi.zig b/lib/std/os/bits/linux/arm-eabi.zig index 4d5accd133..5673cb952b 100644 --- a/lib/std/os/bits/linux/arm-eabi.zig +++ b/lib/std/os/bits/linux/arm-eabi.zig @@ -412,6 +412,7 @@ pub const SYS = extern enum(usize) { pidfd_getfd = 438, faccessat2 = 439, process_madvise = 440, + epoll_pwait2 = 441, breakpoint = 0x0f0001, cacheflush = 0x0f0002, diff --git a/lib/std/os/bits/linux/arm64.zig b/lib/std/os/bits/linux/arm64.zig index 7d08010fe8..a069b6adf1 100644 --- a/lib/std/os/bits/linux/arm64.zig +++ b/lib/std/os/bits/linux/arm64.zig @@ -313,6 +313,7 @@ pub const SYS = extern enum(usize) { pidfd_getfd = 438, faccessat2 = 439, process_madvise = 440, + epoll_pwait2 = 441, _, }; diff --git a/lib/std/os/bits/linux/i386.zig b/lib/std/os/bits/linux/i386.zig index 0e2f1c01aa..7ef34eb96b 100644 --- a/lib/std/os/bits/linux/i386.zig +++ b/lib/std/os/bits/linux/i386.zig @@ -448,6 +448,7 @@ pub const SYS = extern enum(usize) { pidfd_getfd = 438, faccessat2 = 439, process_madvise = 440, + epoll_pwait2 = 441, _, }; diff --git a/lib/std/os/bits/linux/mips.zig b/lib/std/os/bits/linux/mips.zig index c9621735ee..412a1f48be 100644 --- a/lib/std/os/bits/linux/mips.zig +++ b/lib/std/os/bits/linux/mips.zig @@ -430,6 +430,7 @@ pub const SYS = extern enum(usize) { pidfd_getfd = Linux + 438, faccessat2 = Linux + 439, process_madvise = Linux + 440, + epoll_pwait2 = Linux + 441, _, }; diff --git a/lib/std/os/bits/linux/powerpc64.zig b/lib/std/os/bits/linux/powerpc64.zig index cc100d7ec1..e0e9347aa1 100644 --- a/lib/std/os/bits/linux/powerpc64.zig +++ b/lib/std/os/bits/linux/powerpc64.zig @@ -409,6 +409,7 @@ pub const SYS = extern enum(usize) { pidfd_getfd = 438, faccessat2 = 439, process_madvise = 440, + epoll_pwait2 = 441, _, }; diff --git a/lib/std/os/bits/linux/riscv64.zig b/lib/std/os/bits/linux/riscv64.zig index 804d8dbf5e..0cbdea415c 100644 --- a/lib/std/os/bits/linux/riscv64.zig +++ b/lib/std/os/bits/linux/riscv64.zig @@ -310,6 +310,7 @@ pub const SYS = extern enum(usize) { pidfd_getfd = 438, faccessat2 = 439, process_madvise = 440, + epoll_pwait2 = 441, _, }; diff --git a/lib/std/os/bits/linux/sparc64.zig b/lib/std/os/bits/linux/sparc64.zig index e2e34c39e4..5644256a95 100644 --- a/lib/std/os/bits/linux/sparc64.zig +++ b/lib/std/os/bits/linux/sparc64.zig @@ -387,6 +387,7 @@ pub const SYS = extern enum(usize) { pidfd_getfd = 438, faccessat2 = 439, process_madvise = 440, + epoll_pwait2 = 441, _, }; diff --git a/lib/std/os/bits/linux/x86_64.zig b/lib/std/os/bits/linux/x86_64.zig index b16190c952..52fee679c5 100644 --- a/lib/std/os/bits/linux/x86_64.zig +++ b/lib/std/os/bits/linux/x86_64.zig @@ -375,6 +375,7 @@ pub const SYS = extern enum(usize) { pidfd_getfd = 438, faccessat2 = 439, process_madvise = 440, + epoll_pwait2 = 441, _, }; From f9be7471bc9a35fcb872a83564d41cdf561ca173 Mon Sep 17 00:00:00 2001 From: rgreenblatt Date: Sat, 13 Feb 2021 20:17:09 -0500 Subject: [PATCH 08/20] fix readable slice bug (and add tests) --- lib/std/fifo.zig | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/std/fifo.zig b/lib/std/fifo.zig index dfd932cb32..b21cb7fc1c 100644 --- a/lib/std/fifo.zig +++ b/lib/std/fifo.zig @@ -153,7 +153,7 @@ pub fn LinearFifo( var start = self.head + offset; if (start >= self.buf.len) { start -= self.buf.len; - return self.buf[start .. self.count - offset]; + return self.buf[start .. start + (self.count - offset)]; } else { const end = math.min(self.head + self.count, self.buf.len); return self.buf[start..end]; @@ -427,6 +427,8 @@ test "LinearFifo(u8, .Dynamic)" { fifo.writeAssumeCapacity("6 Date: Wed, 17 Feb 2021 20:28:12 -0700 Subject: [PATCH 09/20] remove z/Z format specifier deprecations The z/Z format specifiers were merged last October (4 months ago). They were then deprecated in January (just over a month ago). This PR removes them altogether. --- lib/std/fmt.zig | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index c1b24cc6da..86d89a7578 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -524,7 +524,7 @@ pub fn formatType( if (actual_fmt.len == 0) @compileError("cannot format array ref without a specifier (i.e. {s} or {*})"); if (info.child == u8) { - if (comptime mem.indexOfScalar(u8, "sxXeEzZ", actual_fmt[0]) != null) { + if (comptime mem.indexOfScalar(u8, "sxXeE", actual_fmt[0]) != null) { return formatText(value, actual_fmt, options, writer); } } @@ -542,7 +542,7 @@ pub fn formatType( return formatType(mem.span(value), actual_fmt, options, writer, max_depth); } if (ptr_info.child == u8) { - if (comptime mem.indexOfScalar(u8, "sxXeEzZ", actual_fmt[0]) != null) { + if (comptime mem.indexOfScalar(u8, "sxXeE", actual_fmt[0]) != null) { return formatText(mem.span(value), actual_fmt, options, writer); } } @@ -555,7 +555,7 @@ pub fn formatType( return writer.writeAll("{ ... }"); } if (ptr_info.child == u8) { - if (comptime mem.indexOfScalar(u8, "sxXeEzZ", actual_fmt[0]) != null) { + if (comptime mem.indexOfScalar(u8, "sxXeE", actual_fmt[0]) != null) { return formatText(value, actual_fmt, options, writer); } } @@ -576,7 +576,7 @@ pub fn formatType( return writer.writeAll("{ ... }"); } if (info.child == u8) { - if (comptime mem.indexOfScalar(u8, "sxXeEzZ", actual_fmt[0]) != null) { + if (comptime mem.indexOfScalar(u8, "sxXeE", actual_fmt[0]) != null) { return formatText(&value, actual_fmt, options, writer); } } @@ -658,8 +658,6 @@ pub fn formatIntValue( } else { @compileError("Cannot print integer that is larger than 8 bits as a ascii"); } - } else if (comptime std.mem.eql(u8, fmt, "Z")) { - @compileError("specifier 'Z' has been deprecated, wrap your argument in std.zig.fmtEscapes instead"); } else if (comptime std.mem.eql(u8, fmt, "u")) { if (@typeInfo(@TypeOf(int_value)).Int.bits <= 21) { return formatUnicodeCodepoint(@as(u21, int_value), options, writer); @@ -735,10 +733,6 @@ pub fn formatText( } } return; - } else if (comptime std.mem.eql(u8, fmt, "z")) { - @compileError("specifier 'z' has been deprecated, wrap your argument in std.zig.fmtId instead"); - } else if (comptime std.mem.eql(u8, fmt, "Z")) { - @compileError("specifier 'Z' has been deprecated, wrap your argument in std.zig.fmtEscapes instead"); } else { @compileError("Unsupported format string '" ++ fmt ++ "' for type '" ++ @typeName(@TypeOf(value)) ++ "'"); } From c70832bc411388c3e9749013d7cf99704ff95a36 Mon Sep 17 00:00:00 2001 From: Benjamin Graf Date: Sat, 13 Feb 2021 20:24:52 +0100 Subject: [PATCH 10/20] replace ArrayList.shrinkAndFree by ArrayList.shrinkRetainingCapacity --- lib/std/fs.zig | 2 +- lib/std/io/reader.zig | 2 +- lib/std/json.zig | 2 +- lib/std/math/big/int.zig | 2 +- lib/std/net.zig | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 17c0cb7b1d..53108ebe23 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -2186,7 +2186,7 @@ pub const Walker = struct { var top = &self.stack.items[self.stack.items.len - 1]; const dirname_len = top.dirname_len; if (try top.dir_it.next()) |base| { - self.name_buffer.shrinkAndFree(dirname_len); + self.name_buffer.shrinkRetainingCapacity(dirname_len); try self.name_buffer.append(path.sep); try self.name_buffer.appendSlice(base.name); if (base.kind == .Directory) { diff --git a/lib/std/io/reader.zig b/lib/std/io/reader.zig index e174051518..916e2155fa 100644 --- a/lib/std/io/reader.zig +++ b/lib/std/io/reader.zig @@ -111,7 +111,7 @@ pub fn Reader( delimiter: u8, max_size: usize, ) !void { - array_list.shrinkAndFree(0); + array_list.shrinkRetainingCapacity(0); while (true) { var byte: u8 = try self.readByte(); diff --git a/lib/std/json.zig b/lib/std/json.zig index ff258c7f3a..f9fc371049 100644 --- a/lib/std/json.zig +++ b/lib/std/json.zig @@ -2018,7 +2018,7 @@ pub const Parser = struct { pub fn reset(p: *Parser) void { p.state = .Simple; - p.stack.shrinkAndFree(0); + p.stack.shrinkRetainingCapacity(0); } pub fn parse(p: *Parser, input: []const u8) !ValueTree { diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index 81982eac51..d1d7b33508 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -607,7 +607,7 @@ pub const Mutable = struct { /// it will have the same length as it had when the function was called. pub fn gcd(rma: *Mutable, x: Const, y: Const, limbs_buffer: *std.ArrayList(Limb)) !void { const prev_len = limbs_buffer.items.len; - defer limbs_buffer.shrinkAndFree(prev_len); + defer limbs_buffer.shrinkRetainingCapacity(prev_len); const x_copy = if (rma.limbs.ptr == x.limbs.ptr) blk: { const start = limbs_buffer.items.len; try limbs_buffer.appendSlice(x.limbs); diff --git a/lib/std/net.zig b/lib/std/net.zig index 2d62a27cd9..636596c117 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -1205,13 +1205,13 @@ fn linuxLookupNameFromDnsSearch( var tok_it = mem.tokenize(search, " \t"); while (tok_it.next()) |tok| { - canon.shrinkAndFree(canon_name.len + 1); + canon.shrinkRetainingCapacity(canon_name.len + 1); try canon.appendSlice(tok); try linuxLookupNameFromDns(addrs, canon, canon.items, family, rc, port); if (addrs.items.len != 0) return; } - canon.shrinkAndFree(canon_name.len); + canon.shrinkRetainingCapacity(canon_name.len); return linuxLookupNameFromDns(addrs, canon, name, family, rc, port); } From 840331ee48e54b1ce4a3c8196f90eec73ce6deea Mon Sep 17 00:00:00 2001 From: Tau Date: Mon, 25 Jan 2021 07:23:03 +0100 Subject: [PATCH 11/20] Rebase link(at) properly --- lib/std/c.zig | 2 ++ lib/std/os.zig | 86 ++++++++++++++++++++++++++++++++++++++++++++ lib/std/os/linux.zig | 31 ++++++++++++++++ lib/std/os/test.zig | 69 +++++++++++++++++++++++++++++++++++ 4 files changed, 188 insertions(+) diff --git a/lib/std/c.zig b/lib/std/c.zig index b5ee8cd893..1688824dd9 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -100,6 +100,8 @@ pub extern "c" fn pwrite(fd: fd_t, buf: [*]const u8, nbyte: usize, offset: u64) pub extern "c" fn mmap(addr: ?*align(page_size) c_void, len: usize, prot: c_uint, flags: c_uint, fd: fd_t, offset: u64) *c_void; pub extern "c" fn munmap(addr: *align(page_size) c_void, len: usize) c_int; pub extern "c" fn mprotect(addr: *align(page_size) c_void, len: usize, prot: c_uint) c_int; +pub extern "c" fn link(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: c_int) c_int; +pub extern "c" fn linkat(oldfd: fd_t, oldpath: [*:0]const u8, newfd: fd_t, newpath: [*:0]const u8, flags: c_int) c_int; pub extern "c" fn unlink(path: [*:0]const u8) c_int; pub extern "c" fn unlinkat(dirfd: fd_t, path: [*:0]const u8, flags: c_uint) c_int; pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8; diff --git a/lib/std/os.zig b/lib/std/os.zig index 02eec2de71..c2431ff12c 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -1634,6 +1634,92 @@ pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*: } } +pub const LinkError = UnexpectedError || error{ + AccessDenied, + DiskQuota, + PathAlreadyExists, + FileSystem, + SymLinkLoop, + LinkQuotaExceeded, + NameTooLong, + FileNotFound, + SystemResources, + NoSpaceLeft, + ReadOnlyFileSystem, + NotSameFileSystem, +}; + +pub fn linkZ(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: i32) LinkError!void { + switch (errno(system.link(oldpath, newpath, flags))) { + 0 => return, + EACCES => return error.AccessDenied, + EDQUOT => return error.DiskQuota, + EEXIST => return error.PathAlreadyExists, + EFAULT => unreachable, + EIO => return error.FileSystem, + ELOOP => return error.SymLinkLoop, + EMLINK => return error.LinkQuotaExceeded, + ENAMETOOLONG => return error.NameTooLong, + ENOENT => return error.FileNotFound, + ENOMEM => return error.SystemResources, + ENOSPC => return error.NoSpaceLeft, + EPERM => return error.AccessDenied, + EROFS => return error.ReadOnlyFileSystem, + EXDEV => return error.NotSameFileSystem, + EINVAL => unreachable, + else => |err| return unexpectedErrno(err), + } +} + +pub fn link(oldpath: []const u8, newpath: []const u8, flags: i32) LinkError!void { + const old = try toPosixPath(oldpath); + const new = try toPosixPath(newpath); + return try linkZ(&old, &new, flags); +} + +pub const LinkatError = LinkError || error{NotDir}; + +pub fn linkatZ( + olddir: fd_t, + oldpath: [*:0]const u8, + newdir: fd_t, + newpath: [*:0]const u8, + flags: i32, +) LinkatError!void { + switch (errno(system.linkat(olddir, oldpath, newdir, newpath, flags))) { + 0 => return, + EACCES => return error.AccessDenied, + EDQUOT => return error.DiskQuota, + EEXIST => return error.PathAlreadyExists, + EFAULT => unreachable, + EIO => return error.FileSystem, + ELOOP => return error.SymLinkLoop, + EMLINK => return error.LinkQuotaExceeded, + ENAMETOOLONG => return error.NameTooLong, + ENOENT => return error.FileNotFound, + ENOMEM => return error.SystemResources, + ENOSPC => return error.NoSpaceLeft, + ENOTDIR => return error.NotDir, + EPERM => return error.AccessDenied, + EROFS => return error.ReadOnlyFileSystem, + EXDEV => return error.NotSameFileSystem, + EINVAL => unreachable, + else => |err| return unexpectedErrno(err), + } +} + +pub fn linkat( + olddir: fd_t, + oldpath: []const u8, + newdir: fd_t, + newpath: []const u8, + flags: i32, +) LinkatError!void { + const old = try toPosixPath(oldpath); + const new = try toPosixPath(newpath); + return try linkatZ(olddir, &old, newdir, &new, flags); +} + pub const UnlinkError = error{ FileNotFound, diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index b7534db191..035cdabe63 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -634,6 +634,37 @@ pub fn tgkill(tgid: pid_t, tid: pid_t, sig: i32) usize { return syscall2(.tgkill, @bitCast(usize, @as(isize, tgid)), @bitCast(usize, @as(isize, tid)), @bitCast(usize, @as(isize, sig))); } +pub fn link(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: i32) usize { + if (@hasField(SYS, "link")) { + return syscall3( + .link, + @ptrToInt(oldpath), + @ptrToInt(newpath), + @bitCast(usize, @as(isize, flags)), + ); + } else { + return syscall5( + .linkat, + @bitCast(usize, @as(isize, AT_FDCWD)), + @ptrToInt(oldpath), + @bitCast(usize, @as(isize, AT_FDCWD)), + @ptrToInt(newpath), + @bitCast(usize, @as(isize, flags)), + ); + } +} + +pub fn linkat(oldfd: fd_t, oldpath: [*:0]const u8, newfd: fd_t, newpath: [*:0]const u8, flags: i32) usize { + return syscall5( + .linkat, + @bitCast(usize, @as(isize, oldfd)), + @ptrToInt(oldpath), + @bitCast(usize, @as(isize, newfd)), + @ptrToInt(newpath), + @bitCast(usize, @as(isize, flags)), + ); +} + pub fn unlink(path: [*:0]const u8) usize { if (@hasField(SYS, "unlink")) { return syscall1(.unlink, @ptrToInt(path)); diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig index 0904a8585f..f08d4d58fa 100644 --- a/lib/std/os/test.zig +++ b/lib/std/os/test.zig @@ -189,6 +189,75 @@ fn testReadlink(target_path: []const u8, symlink_path: []const u8) !void { expect(mem.eql(u8, target_path, given)); } +test "link with relative paths" { + if (builtin.os.tag != .linux) return error.SkipZigTest; + var cwd = fs.cwd(); + + cwd.deleteFile("example.txt") catch {}; + cwd.deleteFile("new.txt") catch {}; + + try cwd.writeFile("example.txt", "example"); + try os.link("example.txt", "new.txt", 0); + + const efd = try cwd.openFile("example.txt", .{}); + defer efd.close(); + + const nfd = try cwd.openFile("new.txt", .{}); + defer nfd.close(); + + { + const estat = try os.fstat(efd.handle); + const nstat = try os.fstat(nfd.handle); + + testing.expectEqual(estat.ino, nstat.ino); + testing.expectEqual(@as(usize, 2), nstat.nlink); + } + + try os.unlink("new.txt"); + + { + const estat = try os.fstat(efd.handle); + testing.expectEqual(@as(usize, 1), estat.nlink); + } + + try cwd.deleteFile("example.txt"); +} + +test "linkat with different directories" { + if (builtin.os.tag != .linux) return error.SkipZigTest; + var cwd = fs.cwd(); + var tmp = tmpDir(.{}); + + cwd.deleteFile("example.txt") catch {}; + tmp.dir.deleteFile("new.txt") catch {}; + + try cwd.writeFile("example.txt", "example"); + try os.linkat(cwd.fd, "example.txt", tmp.dir.fd, "new.txt", 0); + + const efd = try cwd.openFile("example.txt", .{}); + defer efd.close(); + + const nfd = try tmp.dir.openFile("new.txt", .{}); + + { + defer nfd.close(); + const estat = try os.fstat(efd.handle); + const nstat = try os.fstat(nfd.handle); + + testing.expectEqual(estat.ino, nstat.ino); + testing.expectEqual(@as(usize, 2), nstat.nlink); + } + + try os.unlinkat(tmp.dir.fd, "new.txt", 0); + + { + const estat = try os.fstat(efd.handle); + testing.expectEqual(@as(usize, 1), estat.nlink); + } + + try cwd.deleteFile("example.txt"); +} + test "fstatat" { // enable when `fstat` and `fstatat` are implemented on Windows if (builtin.os.tag == .windows) return error.SkipZigTest; From 1bd434fd18e0cb769ca46849dac056a562ce7ce3 Mon Sep 17 00:00:00 2001 From: jacob gw Date: Mon, 1 Feb 2021 09:18:52 -0500 Subject: [PATCH 12/20] std.Progress: improve support for "dumb" terminals --- lib/std/Progress.zig | 18 +++++++++++++++++- src/Compilation.zig | 4 +++- src/stage1.zig | 4 +++- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig index 131aff2d2b..4afd191b93 100644 --- a/lib/std/Progress.zig +++ b/lib/std/Progress.zig @@ -25,6 +25,13 @@ terminal: ?std.fs.File = undefined, /// Whether the terminal supports ANSI escape codes. supports_ansi_escape_codes: bool = false, +/// If the terminal is "dumb", don't print output. +/// This can be useful if you don't want to print all +/// the stages of code generation if there are a lot. +/// You should not use it if the user should see output +/// for example showing the user what tests run. +dont_print_on_dumb: bool = false, + root: Node = undefined, /// Keeps track of how much time has passed since the beginning. @@ -141,6 +148,9 @@ pub fn start(self: *Progress, name: []const u8, estimated_total_items: usize) !* self.supports_ansi_escape_codes = true; } else if (std.builtin.os.tag == .windows and stderr.isTty()) { self.terminal = stderr; + } else if (std.builtin.os.tag != .windows) { + // we are in a "dumb" terminal like in acme or writing to a file + self.terminal = stderr; } self.root = Node{ .context = self, @@ -178,6 +188,8 @@ pub fn refresh(self: *Progress) void { } fn refreshWithHeldLock(self: *Progress) void { + const is_dumb = !self.supports_ansi_escape_codes and !(std.builtin.os.tag == .windows); + if (is_dumb and self.dont_print_on_dumb) return; const file = self.terminal orelse return; const prev_columns_written = self.columns_written; @@ -226,7 +238,11 @@ fn refreshWithHeldLock(self: *Progress) void { if (windows.kernel32.SetConsoleCursorPosition(file.handle, cursor_pos) != windows.TRUE) unreachable; - } else unreachable; + } else { + // we are in a "dumb" terminal like in acme or writing to a file + self.output_buffer[end] = '\n'; + end += 1; + } self.columns_written = 0; } diff --git a/src/Compilation.zig b/src/Compilation.zig index ae3385b2dc..180d49a196 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1538,7 +1538,9 @@ pub fn getCompileLogOutput(self: *Compilation) []const u8 { } pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemory }!void { - var progress: std.Progress = .{}; + // If the terminal is dumb, we dont want to show the user all the + // output. + var progress: std.Progress = .{ .dont_print_on_dumb = true }; var main_progress_node = try progress.start("", 0); defer main_progress_node.end(); if (self.color == .off) progress.terminal = null; diff --git a/src/stage1.zig b/src/stage1.zig index 8ab3b1d94d..1b7eadd1a8 100644 --- a/src/stage1.zig +++ b/src/stage1.zig @@ -278,7 +278,9 @@ export fn stage2_attach_segfault_handler() void { // ABI warning export fn stage2_progress_create() *std.Progress { const ptr = std.heap.c_allocator.create(std.Progress) catch @panic("out of memory"); - ptr.* = std.Progress{}; + // If the terminal is dumb, we dont want to show the user all the + // output. + ptr.* = std.Progress{ .dont_print_on_dumb = true }; return ptr; } From cc5e5cca83cca9a1de81f98a333e8a3fcd26df0c Mon Sep 17 00:00:00 2001 From: Bill Nagel Date: Mon, 11 Jan 2021 22:21:18 -0500 Subject: [PATCH 13/20] fix race condition in linuxWaitFd --- lib/std/event/loop.zig | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/std/event/loop.zig b/lib/std/event/loop.zig index 492b7c1758..912f99e961 100644 --- a/lib/std/event/loop.zig +++ b/lib/std/event/loop.zig @@ -440,13 +440,11 @@ pub const Loop = struct { .overlapped = ResumeNode.overlapped_init, }, }; - var need_to_delete = false; + var need_to_delete = true; defer if (need_to_delete) self.linuxRemoveFd(fd); suspend { - if (self.linuxAddFd(fd, &resume_node.base, flags)) |_| { - need_to_delete = true; - } else |err| switch (err) { + self.linuxAddFd(fd, &resume_node.base, flags) catch |err| switch (err) { error.FileDescriptorNotRegistered => unreachable, error.OperationCausesCircularLoop => unreachable, error.FileDescriptorIncompatibleWithEpoll => unreachable, @@ -456,6 +454,7 @@ pub const Loop = struct { error.UserResourceLimitReached, error.Unexpected, => { + need_to_delete = false; // Fall back to a blocking poll(). Ideally this codepath is never hit, since // epoll should be just fine. But this is better than incorrect behavior. var poll_flags: i16 = 0; @@ -479,7 +478,7 @@ pub const Loop = struct { }; resume @frame(); }, - } + }; } } From 4272f07f668979bc356f117cdf12986a95402b43 Mon Sep 17 00:00:00 2001 From: Asherah Connor <1915+kivikakk@users.noreply.github.com> Date: Sun, 21 Feb 2021 21:17:59 +1100 Subject: [PATCH 14/20] std.os.uefi.Guid fixes (#8032) * uefi: Guid.format compiles again Also use "writer" nomenclature in argument name. * uefi: add Guid.eql --- lib/std/os/uefi.zig | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/lib/std/os/uefi.zig b/lib/std/os/uefi.zig index 6cfa2d24e4..1942165999 100644 --- a/lib/std/os/uefi.zig +++ b/lib/std/os/uefi.zig @@ -3,6 +3,8 @@ // This file is part of [zig](https://ziglang.org/), which is MIT licensed. // The MIT license requires this copyright notice to be included in all copies // and substantial portions of the software. +const std = @import("../std.zig"); + /// A protocol is an interface identified by a GUID. pub const protocols = @import("uefi/protocols.zig"); @@ -33,10 +35,10 @@ pub const Guid = extern struct { self: @This(), comptime f: []const u8, options: std.fmt.FormatOptions, - out_stream: anytype, - ) Errors!void { + writer: anytype, + ) !void { if (f.len == 0) { - return std.fmt.format(out_stream, "{x:0>8}-{x:0>4}-{x:0>4}-{x:0>2}{x:0>2}-{x:0>12}", .{ + return std.fmt.format(writer, "{x:0>8}-{x:0>4}-{x:0>4}-{x:0>2}{x:0>2}-{x:0>12}", .{ self.time_low, self.time_mid, self.time_high_and_version, @@ -48,6 +50,15 @@ pub const Guid = extern struct { @compileError("Unknown format character: '" ++ f ++ "'"); } } + + pub fn eql(a: std.os.uefi.Guid, b: std.os.uefi.Guid) bool { + return a.time_low == b.time_low and + a.time_mid == b.time_mid and + a.time_high_and_version == b.time_high_and_version and + a.clock_seq_high_and_reserved == b.clock_seq_high_and_reserved and + a.clock_seq_low == b.clock_seq_low and + std.mem.eql(u8, &a.node, &b.node); + } }; /// An EFI Handle represents a collection of related interfaces. From 057bf1afc9933e32bd35842d2464dab2164f06fb Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Thu, 18 Feb 2021 20:28:59 +0100 Subject: [PATCH 15/20] std: Add more error checking in hexToBytes Prevent the function from turning into an endless loop that may or may not perform OOB accesses. --- lib/std/crypto/25519/ed25519.zig | 10 +++++----- lib/std/crypto/25519/ristretto255.zig | 2 +- lib/std/crypto/25519/x25519.zig | 4 ++-- lib/std/crypto/aes.zig | 8 ++++---- lib/std/crypto/blake3.zig | 2 +- lib/std/crypto/gimli.zig | 22 +++++++++++----------- lib/std/fmt.zig | 25 ++++++++++++++++++------- src/Cache.zig | 2 +- 8 files changed, 43 insertions(+), 32 deletions(-) diff --git a/lib/std/crypto/25519/ed25519.zig b/lib/std/crypto/25519/ed25519.zig index 420eb33a30..5c7ec0cdac 100644 --- a/lib/std/crypto/25519/ed25519.zig +++ b/lib/std/crypto/25519/ed25519.zig @@ -207,7 +207,7 @@ pub const Ed25519 = struct { test "ed25519 key pair creation" { var seed: [32]u8 = undefined; - try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166"); + _ = try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166"); const key_pair = try Ed25519.KeyPair.create(seed); var buf: [256]u8 = undefined; std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{key_pair.secret_key}), "8052030376D47112BE7F73ED7A019293DD12AD910B654455798B4667D73DE1662D6F7455D97B4A3A10D7293909D1A4F2058CB9A370E43FA8154BB280DB839083"); @@ -216,7 +216,7 @@ test "ed25519 key pair creation" { test "ed25519 signature" { var seed: [32]u8 = undefined; - try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166"); + _ = try fmt.hexToBytes(seed[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166"); const key_pair = try Ed25519.KeyPair.create(seed); const sig = try Ed25519.sign("test", key_pair, null); @@ -339,11 +339,11 @@ test "ed25519 test vectors" { }; for (entries) |entry, i| { var msg: [entry.msg_hex.len / 2]u8 = undefined; - try fmt.hexToBytes(&msg, entry.msg_hex); + _ = try fmt.hexToBytes(&msg, entry.msg_hex); var public_key: [32]u8 = undefined; - try fmt.hexToBytes(&public_key, entry.public_key_hex); + _ = try fmt.hexToBytes(&public_key, entry.public_key_hex); var sig: [64]u8 = undefined; - try fmt.hexToBytes(&sig, entry.sig_hex); + _ = try fmt.hexToBytes(&sig, entry.sig_hex); if (entry.expected) |error_type| { std.testing.expectError(error_type, Ed25519.verify(sig, &msg, public_key)); } else { diff --git a/lib/std/crypto/25519/ristretto255.zig b/lib/std/crypto/25519/ristretto255.zig index 68fb938103..df85422f65 100644 --- a/lib/std/crypto/25519/ristretto255.zig +++ b/lib/std/crypto/25519/ristretto255.zig @@ -173,7 +173,7 @@ test "ristretto255" { std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{p.toBytes()}), "E2F2AE0A6ABC4E71A884A961C500515F58E30B6AA582DD8DB6A65945E08D2D76"); var r: [Ristretto255.encoded_length]u8 = undefined; - try fmt.hexToBytes(r[0..], "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919"); + _ = try fmt.hexToBytes(r[0..], "6a493210f7499cd17fecb510ae0cea23a110e8d5b901f8acadd3095c73a3b919"); var q = try Ristretto255.fromBytes(r); q = q.dbl().add(p); std.testing.expectEqualStrings(try std.fmt.bufPrint(&buf, "{X}", .{q.toBytes()}), "E882B131016B52C1D3337080187CF768423EFCCBB517BB495AB812C4160FF44E"); diff --git a/lib/std/crypto/25519/x25519.zig b/lib/std/crypto/25519/x25519.zig index 530637a451..5d0479bd4d 100644 --- a/lib/std/crypto/25519/x25519.zig +++ b/lib/std/crypto/25519/x25519.zig @@ -85,8 +85,8 @@ const htest = @import("../test.zig"); test "x25519 public key calculation from secret key" { var sk: [32]u8 = undefined; var pk_expected: [32]u8 = undefined; - try fmt.hexToBytes(sk[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166"); - try fmt.hexToBytes(pk_expected[0..], "f1814f0e8ff1043d8a44d25babff3cedcae6c22c3edaa48f857ae70de2baae50"); + _ = try fmt.hexToBytes(sk[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166"); + _ = try fmt.hexToBytes(pk_expected[0..], "f1814f0e8ff1043d8a44d25babff3cedcae6c22c3edaa48f857ae70de2baae50"); const pk_calculated = try X25519.recoverPublicKey(sk); std.testing.expectEqual(pk_calculated, pk_expected); } diff --git a/lib/std/crypto/aes.zig b/lib/std/crypto/aes.zig index d862bcf3fc..2a81492c8a 100644 --- a/lib/std/crypto/aes.zig +++ b/lib/std/crypto/aes.zig @@ -122,11 +122,11 @@ test "expand 128-bit key" { var exp: [16]u8 = undefined; for (enc.key_schedule.round_keys) |round_key, i| { - try std.fmt.hexToBytes(&exp, exp_enc[i]); + _ = try std.fmt.hexToBytes(&exp, exp_enc[i]); testing.expectEqualSlices(u8, &exp, &round_key.toBytes()); } for (enc.key_schedule.round_keys) |round_key, i| { - try std.fmt.hexToBytes(&exp, exp_dec[i]); + _ = try std.fmt.hexToBytes(&exp, exp_dec[i]); testing.expectEqualSlices(u8, &exp, &round_key.toBytes()); } } @@ -144,11 +144,11 @@ test "expand 256-bit key" { var exp: [16]u8 = undefined; for (enc.key_schedule.round_keys) |round_key, i| { - try std.fmt.hexToBytes(&exp, exp_enc[i]); + _ = try std.fmt.hexToBytes(&exp, exp_enc[i]); testing.expectEqualSlices(u8, &exp, &round_key.toBytes()); } for (dec.key_schedule.round_keys) |round_key, i| { - try std.fmt.hexToBytes(&exp, exp_dec[i]); + _ = try std.fmt.hexToBytes(&exp, exp_dec[i]); testing.expectEqualSlices(u8, &exp, &round_key.toBytes()); } } diff --git a/lib/std/crypto/blake3.zig b/lib/std/crypto/blake3.zig index 7a65487135..a10c50b074 100644 --- a/lib/std/crypto/blake3.zig +++ b/lib/std/crypto/blake3.zig @@ -663,7 +663,7 @@ fn testBlake3(hasher: *Blake3, input_len: usize, expected_hex: [262]u8) void { // Compare to expected value var expected_bytes: [expected_hex.len / 2]u8 = undefined; - fmt.hexToBytes(expected_bytes[0..], expected_hex[0..]) catch unreachable; + _ = fmt.hexToBytes(expected_bytes[0..], expected_hex[0..]) catch unreachable; testing.expectEqual(actual_bytes, expected_bytes); // Restore initial state diff --git a/lib/std/crypto/gimli.zig b/lib/std/crypto/gimli.zig index 4809b9b6f7..1c1d6c79db 100644 --- a/lib/std/crypto/gimli.zig +++ b/lib/std/crypto/gimli.zig @@ -270,7 +270,7 @@ pub fn hash(out: []u8, in: []const u8, options: Hash.Options) void { test "hash" { // a test vector (30) from NIST KAT submission. var msg: [58 / 2]u8 = undefined; - try std.fmt.hexToBytes(&msg, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C"); + _ = try std.fmt.hexToBytes(&msg, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C"); var md: [32]u8 = undefined; hash(&md, &msg, .{}); htest.assertEqual("1C9A03DC6A5DDC5444CFC6F4B154CFF5CF081633B2CEA4D7D0AE7CCFED5AAA44", &md); @@ -278,7 +278,7 @@ test "hash" { test "hash test vector 17" { var msg: [32 / 2]u8 = undefined; - try std.fmt.hexToBytes(&msg, "000102030405060708090A0B0C0D0E0F"); + _ = try std.fmt.hexToBytes(&msg, "000102030405060708090A0B0C0D0E0F"); var md: [32]u8 = undefined; hash(&md, &msg, .{}); htest.assertEqual("404C130AF1B9023A7908200919F690FFBB756D5176E056FFDE320016A37C7282", &md); @@ -286,7 +286,7 @@ test "hash test vector 17" { test "hash test vector 33" { var msg: [32]u8 = undefined; - try std.fmt.hexToBytes(&msg, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"); + _ = try std.fmt.hexToBytes(&msg, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"); var md: [32]u8 = undefined; hash(&md, &msg, .{}); htest.assertEqual("A8F4FA28708BDA7EFB4C1914CA4AFA9E475B82D588D36504F87DBB0ED9AB3C4B", &md); @@ -436,9 +436,9 @@ pub const Aead = struct { test "cipher" { var key: [32]u8 = undefined; - try std.fmt.hexToBytes(&key, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"); + _ = try std.fmt.hexToBytes(&key, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"); var nonce: [16]u8 = undefined; - try std.fmt.hexToBytes(&nonce, "000102030405060708090A0B0C0D0E0F"); + _ = try std.fmt.hexToBytes(&nonce, "000102030405060708090A0B0C0D0E0F"); { // test vector (1) from NIST KAT submission. const ad: [0]u8 = undefined; const pt: [0]u8 = undefined; @@ -456,7 +456,7 @@ test "cipher" { { // test vector (34) from NIST KAT submission. const ad: [0]u8 = undefined; var pt: [2 / 2]u8 = undefined; - try std.fmt.hexToBytes(&pt, "00"); + _ = try std.fmt.hexToBytes(&pt, "00"); var ct: [pt.len]u8 = undefined; var tag: [16]u8 = undefined; @@ -470,9 +470,9 @@ test "cipher" { } { // test vector (106) from NIST KAT submission. var ad: [12 / 2]u8 = undefined; - try std.fmt.hexToBytes(&ad, "000102030405"); + _ = try std.fmt.hexToBytes(&ad, "000102030405"); var pt: [6 / 2]u8 = undefined; - try std.fmt.hexToBytes(&pt, "000102"); + _ = try std.fmt.hexToBytes(&pt, "000102"); var ct: [pt.len]u8 = undefined; var tag: [16]u8 = undefined; @@ -486,9 +486,9 @@ test "cipher" { } { // test vector (790) from NIST KAT submission. var ad: [60 / 2]u8 = undefined; - try std.fmt.hexToBytes(&ad, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D"); + _ = try std.fmt.hexToBytes(&ad, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D"); var pt: [46 / 2]u8 = undefined; - try std.fmt.hexToBytes(&pt, "000102030405060708090A0B0C0D0E0F10111213141516"); + _ = try std.fmt.hexToBytes(&pt, "000102030405060708090A0B0C0D0E0F10111213141516"); var ct: [pt.len]u8 = undefined; var tag: [16]u8 = undefined; @@ -503,7 +503,7 @@ test "cipher" { { // test vector (1057) from NIST KAT submission. const ad: [0]u8 = undefined; var pt: [64 / 2]u8 = undefined; - try std.fmt.hexToBytes(&pt, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"); + _ = try std.fmt.hexToBytes(&pt, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"); var ct: [pt.len]u8 = undefined; var tag: [16]u8 = undefined; diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 86d89a7578..e5ce457091 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -1982,23 +1982,34 @@ test "bytes.hex" { pub const trim = @compileError("deprecated; use std.mem.trim with std.ascii.spaces instead"); pub const isWhiteSpace = @compileError("deprecated; use std.ascii.isSpace instead"); -pub fn hexToBytes(out: []u8, input: []const u8) !void { - if (out.len * 2 < input.len) +/// Decodes the sequence of bytes represented by the specified string of +/// hexadecimal characters. +/// Returns a slice of the output buffer containing the decoded bytes. +pub fn hexToBytes(out: []u8, input: []const u8) ![]u8 { + // Expect 0 or n pairs of hexadecimal digits. + if (input.len & 1 != 0) return error.InvalidLength; + if (out.len * 2 < input.len) + return error.NoSpaceLeft; var in_i: usize = 0; - while (in_i != input.len) : (in_i += 2) { + while (in_i < input.len) : (in_i += 2) { const hi = try charToDigit(input[in_i], 16); const lo = try charToDigit(input[in_i + 1], 16); out[in_i / 2] = (hi << 4) | lo; } + + return out[0 .. in_i / 2]; } test "hexToBytes" { - const test_hex_str = "909A312BB12ED1F819B3521AC4C1E896F2160507FFC1C8381E3B07BB16BD1706"; - var pb: [32]u8 = undefined; - try hexToBytes(pb[0..], test_hex_str); - try expectFmt(test_hex_str, "{X}", .{pb}); + var buf: [32]u8 = undefined; + try expectFmt("90" ** 32, "{X}", .{try hexToBytes(&buf, "90" ** 32)}); + try expectFmt("ABCD", "{X}", .{try hexToBytes(&buf, "ABCD")}); + try expectFmt("", "{X}", .{try hexToBytes(&buf, "")}); + std.testing.expectError(error.InvalidCharacter, hexToBytes(&buf, "012Z")); + std.testing.expectError(error.InvalidLength, hexToBytes(&buf, "AAA")); + std.testing.expectError(error.NoSpaceLeft, hexToBytes(buf[0..1], "ABAB")); } test "formatIntValue with comptime_int" { diff --git a/src/Cache.zig b/src/Cache.zig index f5ffb34dbe..57ff9227fa 100644 --- a/src/Cache.zig +++ b/src/Cache.zig @@ -317,7 +317,7 @@ pub const Manifest = struct { cache_hash_file.stat.size = fmt.parseInt(u64, size, 10) catch return error.InvalidFormat; cache_hash_file.stat.inode = fmt.parseInt(fs.File.INode, inode, 10) catch return error.InvalidFormat; cache_hash_file.stat.mtime = fmt.parseInt(i64, mtime_nsec_str, 10) catch return error.InvalidFormat; - std.fmt.hexToBytes(&cache_hash_file.bin_digest, digest_str) catch return error.InvalidFormat; + _ = std.fmt.hexToBytes(&cache_hash_file.bin_digest, digest_str) catch return error.InvalidFormat; if (file_path.len == 0) { return error.InvalidFormat; From 9712e892656ace8ee26c2b2decfde0f2d116ec54 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Fri, 5 Feb 2021 21:05:14 +0100 Subject: [PATCH 16/20] stage2 codegen: Add Type argument to genSetReg --- src/codegen.zig | 94 ++++++++++++++++++++++++------------------------- 1 file changed, 46 insertions(+), 48 deletions(-) diff --git a/src/codegen.zig b/src/codegen.zig index d81ad1faf5..ea08b80092 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -939,7 +939,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { /// Copies a value to a register without tracking the register. The register is not considered /// allocated. A second call to `copyToTmpRegister` may return the same register. /// This can have a side effect of spilling instructions to the stack to free up a register. - fn copyToTmpRegister(self: *Self, src: usize, mcv: MCValue) !Register { + fn copyToTmpRegister(self: *Self, src: usize, ty: Type, mcv: MCValue) !Register { const reg = self.findUnusedReg() orelse b: { // We'll take over the first register. Move the instruction that was previously // there to a stack allocation. @@ -956,7 +956,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { break :b reg; }; - try self.genSetReg(src, reg, mcv); + try self.genSetReg(src, ty, reg, mcv); return reg; } @@ -983,7 +983,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { break :b reg; }; - try self.genSetReg(reg_owner.src, reg, mcv); + try self.genSetReg(reg_owner.src, reg_owner.ty, reg, mcv); return MCValue{ .register = reg }; } @@ -1351,13 +1351,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { // Load immediate into register if it doesn't fit // as an operand break :blk Instruction.Operand.fromU32(@intCast(u32, imm)) orelse - Instruction.Operand.reg(try self.copyToTmpRegister(src, op2), Instruction.Operand.Shift.none); + Instruction.Operand.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), op2), Instruction.Operand.Shift.none); }, .register => |reg| Instruction.Operand.reg(reg, Instruction.Operand.Shift.none), .stack_offset, .embedded_in_code, .memory, - => Instruction.Operand.reg(try self.copyToTmpRegister(src, op2), Instruction.Operand.Shift.none), + => Instruction.Operand.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), op2), Instruction.Operand.Shift.none), }; switch (op) { @@ -1443,7 +1443,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { switch (src_mcv) { .immediate => |imm| { if (imm > math.maxInt(u31)) { - src_mcv = MCValue{ .register = try self.copyToTmpRegister(src_inst.src, src_mcv) }; + src_mcv = MCValue{ .register = try self.copyToTmpRegister(src_inst.src, Type.initTag(.u64), src_mcv) }; } }, else => {}, @@ -1474,7 +1474,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .register => |dst_reg| { switch (src_mcv) { .none => unreachable, - .undef => try self.genSetReg(src, dst_reg, .undef), + .undef => try self.genSetReg(src, dst_ty, dst_reg, .undef), .dead, .unreach => unreachable, .ptr_stack_offset => unreachable, .ptr_embedded_in_code => unreachable, @@ -1684,7 +1684,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { switch (mc_arg) { .none => continue, .register => |reg| { - try self.genSetReg(arg.src, reg, arg_mcv); + try self.genSetReg(arg.src, arg.ty, reg, arg_mcv); // TODO interact with the register allocator to mark the instruction as moved. }, .stack_offset => { @@ -1753,7 +1753,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { else unreachable; - try self.genSetReg(inst.base.src, .ra, .{ .memory = got_addr }); + try self.genSetReg(inst.base.src, Type.initTag(.usize), .ra, .{ .memory = got_addr }); mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.ra, 0, .ra).toU32()); } else if (func_value.castTag(.extern_fn)) |_| { return self.fail(inst.base.src, "TODO implement calling extern functions", .{}); @@ -1826,7 +1826,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .compare_flags_signed => unreachable, .compare_flags_unsigned => unreachable, .register => |reg| { - try self.genSetReg(arg.src, reg, arg_mcv); + try self.genSetReg(arg.src, arg.ty, reg, arg_mcv); // TODO interact with the register allocator to mark the instruction as moved. }, .stack_offset => { @@ -1854,7 +1854,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { else unreachable; - try self.genSetReg(inst.base.src, .lr, .{ .memory = got_addr }); + try self.genSetReg(inst.base.src, Type.initTag(.usize), .lr, .{ .memory = got_addr }); // TODO: add Instruction.supportedOn // function for ARM @@ -1889,7 +1889,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .compare_flags_signed => unreachable, .compare_flags_unsigned => unreachable, .register => |reg| { - try self.genSetReg(arg.src, reg, arg_mcv); + try self.genSetReg(arg.src, arg.ty, reg, arg_mcv); // TODO interact with the register allocator to mark the instruction as moved. }, .stack_offset => { @@ -1917,7 +1917,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { else unreachable; - try self.genSetReg(inst.base.src, .x30, .{ .memory = got_addr }); + try self.genSetReg(inst.base.src, Type.initTag(.usize), .x30, .{ .memory = got_addr }); writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32()); } else if (func_value.castTag(.extern_fn)) |_| { @@ -1940,7 +1940,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { switch (mc_arg) { .none => continue, .register => |reg| { - try self.genSetReg(arg.src, reg, arg_mcv); + try self.genSetReg(arg.src, arg.ty, reg, arg_mcv); // TODO interact with the register allocator to mark the instruction as moved. }, .stack_offset => { @@ -1973,12 +1973,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { const got_addr = got.addr + func.owner_decl.link.macho.offset_table_index * @sizeOf(u64); switch (arch) { .x86_64 => { - try self.genSetReg(inst.base.src, .rax, .{ .memory = got_addr }); + try self.genSetReg(inst.base.src, Type.initTag(.u32), .rax, .{ .memory = got_addr }); // callq *%rax self.code.appendSliceAssumeCapacity(&[2]u8{ 0xff, 0xd0 }); }, .aarch64 => { - try self.genSetReg(inst.base.src, .x30, .{ .memory = got_addr }); + try self.genSetReg(inst.base.src, Type.initTag(.u32), .x30, .{ .memory = got_addr }); // blr x30 writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32()); }, @@ -2579,7 +2579,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { const reg = parseRegName(reg_name) orelse return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); const arg = try self.resolveInst(inst.args[i]); - try self.genSetReg(inst.base.src, reg, arg); + try self.genSetReg(inst.base.src, inst.args[i].ty, reg, arg); } if (mem.eql(u8, inst.asm_source, "svc #0")) { @@ -2609,7 +2609,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { const reg = parseRegName(reg_name) orelse return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); const arg = try self.resolveInst(inst.args[i]); - try self.genSetReg(inst.base.src, reg, arg); + try self.genSetReg(inst.base.src, inst.args[i].ty, reg, arg); } if (mem.eql(u8, inst.asm_source, "svc #0")) { @@ -2641,7 +2641,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { const reg = parseRegName(reg_name) orelse return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); const arg = try self.resolveInst(inst.args[i]); - try self.genSetReg(inst.base.src, reg, arg); + try self.genSetReg(inst.base.src, inst.args[i].ty, reg, arg); } if (mem.eql(u8, inst.asm_source, "ecall")) { @@ -2671,7 +2671,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { const reg = parseRegName(reg_name) orelse return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name}); const arg = try self.resolveInst(inst.args[i]); - try self.genSetReg(inst.base.src, reg, arg); + try self.genSetReg(inst.base.src, inst.args[i].ty, reg, arg); } if (mem.eql(u8, inst.asm_source, "syscall")) { @@ -2733,7 +2733,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { fn setRegOrMem(self: *Self, src: usize, ty: Type, loc: MCValue, val: MCValue) !void { switch (loc) { .none => return, - .register => |reg| return self.genSetReg(src, reg, val), + .register => |reg| return self.genSetReg(src, ty, reg, val), .stack_offset => |off| return self.genSetStack(src, ty, off, val), .memory => { return self.fail(src, "TODO implement setRegOrMem for memory", .{}); @@ -2768,7 +2768,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { return self.fail(src, "TODO implement set stack variable with compare flags value (signed)", .{}); }, .immediate => { - const reg = try self.copyToTmpRegister(src, mcv); + const reg = try self.copyToTmpRegister(src, ty, mcv); return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg }); }, .embedded_in_code => |code_offset| { @@ -2782,7 +2782,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { 1, 4 => { const offset = if (math.cast(u12, adj_off)) |imm| blk: { break :blk Instruction.Offset.imm(imm); - } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(src, MCValue{ .immediate = adj_off }), 0); + } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0); const str = switch (abi_size) { 1 => Instruction.strb, 4 => Instruction.str, @@ -2797,7 +2797,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { 2 => { const offset = if (adj_off <= math.maxInt(u8)) blk: { break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off)); - } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(src, MCValue{ .immediate = adj_off })); + } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), MCValue{ .immediate = adj_off })); writeInt(u32, try self.code.addManyAsArray(4), Instruction.strh(.al, reg, .fp, .{ .offset = offset, @@ -2814,7 +2814,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { if (stack_offset == off) return; // Copy stack variable to itself; nothing to do. - const reg = try self.copyToTmpRegister(src, mcv); + const reg = try self.copyToTmpRegister(src, ty, mcv); return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg }); }, }, @@ -2903,7 +2903,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { if (stack_offset == off) return; // Copy stack variable to itself; nothing to do. - const reg = try self.copyToTmpRegister(src, mcv); + const reg = try self.copyToTmpRegister(src, ty, mcv); return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg }); }, }, @@ -2931,7 +2931,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { return self.fail(src, "TODO implement set stack variable with compare flags value (signed)", .{}); }, .immediate => { - const reg = try self.copyToTmpRegister(src, mcv); + const reg = try self.copyToTmpRegister(src, ty, mcv); return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg }); }, .embedded_in_code => |code_offset| { @@ -2946,7 +2946,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { const offset = if (math.cast(i9, adj_off)) |imm| Instruction.LoadStoreOffset.imm_post_index(-imm) else |_| - Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(src, MCValue{ .immediate = adj_off })); + Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(src, Type.initTag(.u64), MCValue{ .immediate = adj_off })); const rn: Register = switch (arch) { .aarch64, .aarch64_be => .x29, .aarch64_32 => .w29, @@ -2967,7 +2967,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { if (stack_offset == off) return; // Copy stack variable to itself; nothing to do. - const reg = try self.copyToTmpRegister(src, mcv); + const reg = try self.copyToTmpRegister(src, ty, mcv); return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg }); }, }, @@ -2975,7 +2975,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { } } - fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) InnerError!void { + fn genSetReg(self: *Self, src: usize, ty: Type, reg: Register, mcv: MCValue) InnerError!void { switch (arch) { .arm, .armeb => switch (mcv) { .dead => unreachable, @@ -2986,7 +2986,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { if (!self.wantSafety()) return; // The already existing value will do just fine. // Write the debug undefined value. - return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaa }); + return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaa }); }, .compare_flags_unsigned, .compare_flags_signed, @@ -3051,21 +3051,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .memory => |addr| { // The value is in memory at a hard-coded address. // If the type is a pointer, it means the pointer address is at this memory location. - try self.genSetReg(src, reg, .{ .immediate = addr }); + try self.genSetReg(src, ty, reg, .{ .immediate = addr }); writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, reg, .{ .offset = Instruction.Offset.none }).toU32()); }, .stack_offset => |unadjusted_off| { // TODO: maybe addressing from sp instead of fp - // TODO: supply type information to genSetReg as we do to genSetStack - // const abi_size = ty.abiSize(self.target.*); - const abi_size = 4; + const abi_size = ty.abiSize(self.target.*); const adj_off = unadjusted_off + abi_size; switch (abi_size) { 1, 4 => { const offset = if (adj_off <= math.maxInt(u12)) blk: { break :blk Instruction.Offset.imm(@intCast(u12, adj_off)); - } else Instruction.Offset.reg(try self.copyToTmpRegister(src, MCValue{ .immediate = adj_off }), 0); + } else Instruction.Offset.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0); const ldr = switch (abi_size) { 1 => Instruction.ldrb, 4 => Instruction.ldr, @@ -3080,7 +3078,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { 2 => { const offset = if (adj_off <= math.maxInt(u8)) blk: { break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off)); - } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(src, MCValue{ .immediate = adj_off })); + } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), MCValue{ .immediate = adj_off })); writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldrh(.al, reg, .fp, .{ .offset = offset, @@ -3102,8 +3100,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { return; // The already existing value will do just fine. // Write the debug undefined value. switch (reg.size()) { - 32 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaa }), - 64 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }), + 32 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaa }), + 64 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }), else => unreachable, // unexpected register size } }, @@ -3216,7 +3214,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { } else { // The value is in memory at a hard-coded address. // If the type is a pointer, it means the pointer address is at this memory location. - try self.genSetReg(src, reg, .{ .immediate = addr }); + try self.genSetReg(src, Type.initTag(.usize), reg, .{ .immediate = addr }); mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{ .register = .{ .rn = reg } }).toU32()); } }, @@ -3231,7 +3229,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { if (!self.wantSafety()) return; // The already existing value will do just fine. // Write the debug undefined value. - return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }); + return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }); }, .immediate => |unsigned_x| { const x = @bitCast(i64, unsigned_x); @@ -3256,7 +3254,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .memory => |addr| { // The value is in memory at a hard-coded address. // If the type is a pointer, it means the pointer address is at this memory location. - try self.genSetReg(src, reg, .{ .immediate = addr }); + try self.genSetReg(src, ty, reg, .{ .immediate = addr }); mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ld(reg, 0, reg).toU32()); // LOAD imm=[i12 offset = 0], rs1 = @@ -3275,10 +3273,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { return; // The already existing value will do just fine. // Write the debug undefined value. switch (reg.size()) { - 8 => return self.genSetReg(src, reg, .{ .immediate = 0xaa }), - 16 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaa }), - 32 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaa }), - 64 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }), + 8 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaa }), + 16 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaa }), + 32 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaa }), + 64 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }), else => unreachable, } }, @@ -3492,7 +3490,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { assert(id3 != 4 and id3 != 5); // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue. - try self.genSetReg(src, reg, MCValue{ .immediate = x }); + try self.genSetReg(src, ty, reg, MCValue{ .immediate = x }); // Now, the register contains the address of the value to load into it // Currently, we're only allowing 64-bit registers, so we need the `REX.W 8B /r` variant. @@ -3591,7 +3589,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { // This immediate is unsigned. const U = std.meta.Int(.unsigned, ti.bits - @boolToInt(ti.signedness == .signed)); if (imm >= math.maxInt(U)) { - return MCValue{ .register = try self.copyToTmpRegister(inst.src, mcv) }; + return MCValue{ .register = try self.copyToTmpRegister(inst.src, Type.initTag(.usize), mcv) }; } }, else => {}, From 36178caf3e34859fc715bb281f2692135a337154 Mon Sep 17 00:00:00 2001 From: Ryan Greenblatt Date: Sun, 21 Feb 2021 05:26:46 -0500 Subject: [PATCH 17/20] Added support for passing write file args as build options (#7909) * Added support for passing write file args as build options * Fix missing fmtEscapes and unused format * Actually fixed now, must be formatted * remove addPathBuildOption --- lib/std/build.zig | 49 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index 65a53ef868..f52b863d3f 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1308,6 +1308,12 @@ const BuildOptionArtifactArg = struct { artifact: *LibExeObjStep, }; +const BuildOptionWriteFileArg = struct { + name: []const u8, + write_file: *WriteFileStep, + basename: []const u8, +}; + pub const LibExeObjStep = struct { step: Step, builder: *Builder, @@ -1355,6 +1361,7 @@ pub const LibExeObjStep = struct { packages: ArrayList(Pkg), build_options_contents: std.ArrayList(u8), build_options_artifact_args: std.ArrayList(BuildOptionArtifactArg), + build_options_write_file_args: std.ArrayList(BuildOptionWriteFileArg), object_src: []const u8, @@ -1515,6 +1522,7 @@ pub const LibExeObjStep = struct { .object_src = undefined, .build_options_contents = std.ArrayList(u8).init(builder.allocator), .build_options_artifact_args = std.ArrayList(BuildOptionArtifactArg).init(builder.allocator), + .build_options_write_file_args = std.ArrayList(BuildOptionWriteFileArg).init(builder.allocator), .c_std = Builder.CStd.C99, .override_lib_dir = null, .main_pkg_path = null, @@ -2008,6 +2016,23 @@ pub const LibExeObjStep = struct { self.step.dependOn(&artifact.step); } + /// The value is the path in the cache dir. + /// Adds a dependency automatically. + /// basename refers to the basename of the WriteFileStep + pub fn addBuildOptionWriteFile( + self: *LibExeObjStep, + name: []const u8, + write_file: *WriteFileStep, + basename: []const u8, + ) void { + self.build_options_write_file_args.append(.{ + .name = name, + .write_file = write_file, + .basename = basename, + }) catch unreachable; + self.step.dependOn(&write_file.step); + } + pub fn addSystemIncludeDir(self: *LibExeObjStep, path: []const u8) void { self.include_dirs.append(IncludeDir{ .RawPathSystem = self.builder.dupe(path) }) catch unreachable; } @@ -2228,11 +2253,27 @@ pub const LibExeObjStep = struct { } } - if (self.build_options_contents.items.len > 0 or self.build_options_artifact_args.items.len > 0) { - // Render build artifact options at the last minute, now that the path is known. + if (self.build_options_contents.items.len > 0 or + self.build_options_artifact_args.items.len > 0 or + self.build_options_write_file_args.items.len > 0) + { + // Render build artifact and write file options at the last minute, now that the path is known. + // + // Note that pathFromRoot uses resolve path, so this will have + // correct behavior even if getOutputPath is already absolute. for (self.build_options_artifact_args.items) |item| { - const out = self.build_options_contents.writer(); - out.print("pub const {s}: []const u8 = \"{}\";\n", .{ item.name, std.zig.fmtEscapes(item.artifact.getOutputPath()) }) catch unreachable; + self.addBuildOption( + []const u8, + item.name, + self.builder.pathFromRoot(item.artifact.getOutputPath()), + ); + } + for (self.build_options_write_file_args.items) |item| { + self.addBuildOption( + []const u8, + item.name, + self.builder.pathFromRoot(item.write_file.getOutputPath(item.basename)), + ); } const build_options_file = try fs.path.join( From 0aef1faa820e29a9eceb51a2048977d7be938f2a Mon Sep 17 00:00:00 2001 From: ducdetronquito Date: Sun, 21 Feb 2021 11:55:00 +0100 Subject: [PATCH 18/20] std.fifo.LinearFifo - Expose reader and writer type. --- lib/std/fifo.zig | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/std/fifo.zig b/lib/std/fifo.zig index b21cb7fc1c..b0771bba16 100644 --- a/lib/std/fifo.zig +++ b/lib/std/fifo.zig @@ -44,6 +44,8 @@ pub fn LinearFifo( count: usize, const Self = @This(); + pub const Reader = std.io.Reader(*Self, error{}, readFn); + pub const Writer = std.io.Writer(*Self, error{OutOfMemory}, appendWrite); // Type of Self argument for slice operations. // If buffer is inline (Static) then we need to ensure we haven't @@ -228,7 +230,7 @@ pub fn LinearFifo( return self.read(dest); } - pub fn reader(self: *Self) std.io.Reader(*Self, error{}, readFn) { + pub fn reader(self: *Self) Reader { return .{ .context = self }; } @@ -318,7 +320,7 @@ pub fn LinearFifo( return bytes.len; } - pub fn writer(self: *Self) std.io.Writer(*Self, error{OutOfMemory}, appendWrite) { + pub fn writer(self: *Self) Writer { return .{ .context = self }; } From d9e46dceeca3f66b87e6b2e36415417495d2d2a0 Mon Sep 17 00:00:00 2001 From: johnLate Date: Mon, 22 Feb 2021 19:29:00 +0100 Subject: [PATCH 19/20] std.Thread.Semaphore: Fix wrong variable name Fixes ziglang#8052 --- lib/std/Thread/Semaphore.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/Thread/Semaphore.zig b/lib/std/Thread/Semaphore.zig index 77a278b355..a899cd9b6f 100644 --- a/lib/std/Thread/Semaphore.zig +++ b/lib/std/Thread/Semaphore.zig @@ -13,7 +13,7 @@ cond: Condition = .{}, //! It is OK to initialize this field to any value. permits: usize = 0, -const RwLock = @This(); +const Semaphore = @This(); const std = @import("../std.zig"); const Mutex = std.Thread.Mutex; const Condition = std.Thread.Condition; From 8b9434871ea437840d25f073b945466359f402f9 Mon Sep 17 00:00:00 2001 From: Josh Wolfe Date: Wed, 24 Feb 2021 08:26:13 -0500 Subject: [PATCH 20/20] Avoid concept of a "Unicode character" in documentation and error messages (#8059) --- doc/langref.html.in | 30 ++++++++++++++++++++++-------- lib/std/zig/parser_test.zig | 2 +- lib/std/zig/tokenizer.zig | 6 +++--- src/stage1/tokenizer.cpp | 4 ++-- 4 files changed, 28 insertions(+), 14 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index f43abfe1e6..e49609fdbf 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -310,7 +310,7 @@ pub fn main() !void {

The two arguments passed to the stdout.print() function, "Hello, {s}!\n" and .{"world"}, are evaluated at {#link|compile-time|comptime#}. The code sample is - purposely written to show how to perform {#link|string|String Literals and Character Literals#} + purposely written to show how to perform {#link|string|String Literals and Unicode Code Point Literals#} substitution in the print function. The curly-braces inside of the first argument are substituted with the compile-time known value inside of the second argument (known as an {#link|anonymous struct literal|Anonymous Struct Literals#}). The \n @@ -682,18 +682,31 @@ pub fn main() void { {#see_also|Optionals|undefined#} {#header_close#} - {#header_open|String Literals and Character Literals#} + {#header_open|String Literals and Unicode Code Point Literals#}

- String literals are single-item constant {#link|Pointers#} to null-terminated UTF-8 encoded byte arrays. + String literals are single-item constant {#link|Pointers#} to null-terminated byte arrays. The type of string literals encodes both the length, and the fact that they are null-terminated, and thus they can be {#link|coerced|Type Coercion#} to both {#link|Slices#} and {#link|Null-Terminated Pointers|Sentinel-Terminated Pointers#}. Dereferencing string literals converts them to {#link|Arrays#}.

- Character literals have type {#syntax#}comptime_int{#endsyntax#}, the same as + The encoding of a string in Zig is de-facto assumed to be UTF-8. + Because Zig source code is {#link|UTF-8 encoded|Source Encoding#}, any non-ASCII bytes appearing within a string literal + in source code carry their UTF-8 meaning into the content of the string in the Zig program; + the bytes are not modified by the compiler. + However, it is possible to embbed non-UTF-8 bytes into a string literal using \xNN notation. +

+

+ Unicode code point literals have type {#syntax#}comptime_int{#endsyntax#}, the same as {#link|Integer Literals#}. All {#link|Escape Sequences#} are valid in both string literals - and character literals. + and Unicode code point literals. +

+

+ In many other programming languages, a Unicode code point literal is called a "character literal". + However, there is no precise technical definition of a "character" + in recent versions of the Unicode specification (as of Unicode 13.0). + In Zig, a Unicode code point literal corresponds to the Unicode definition of a code point.

{#code_begin|test#} const expect = @import("std").testing.expect; @@ -709,6 +722,7 @@ test "string literals" { expect('\u{1f4a9}' == 128169); expect('💯' == 128175); expect(mem.eql(u8, "hello", "h\x65llo")); + expect("\xff"[0] == 0xff); // non-UTF-8 strings are possible with \xNN notation. } {#code_end#} {#see_also|Arrays|Zig Test|Source Encoding#} @@ -749,11 +763,11 @@ test "string literals" { \xNN - hexadecimal 8-bit character code (2 digits) + hexadecimal 8-bit byte value (2 digits) \u{NNNNNN} - hexadecimal Unicode character code UTF-8 encoded (1 or more digits) + hexadecimal Unicode code point UTF-8 encoded (1 or more digits) @@ -7414,7 +7428,7 @@ test "main" { This function returns a compile time constant pointer to null-terminated, fixed-size array with length equal to the byte count of the file given by {#syntax#}path{#endsyntax#}. The contents of the array are the contents of the file. - This is equivalent to a {#link|string literal|String Literals and Character Literals#} + This is equivalent to a {#link|string literal|String Literals and Unicode Code Point Literals#} with the file contents.

diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index 822e9006c4..505e900c64 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -680,7 +680,7 @@ test "zig fmt: enum literal inside array literal" { ); } -test "zig fmt: character literal larger than u8" { +test "zig fmt: Unicode code point literal larger than u8" { try testCanonical( \\const x = '\u{01f4a9}'; \\ diff --git a/lib/std/zig/tokenizer.zig b/lib/std/zig/tokenizer.zig index dcbf717638..083f942db6 100644 --- a/lib/std/zig/tokenizer.zig +++ b/lib/std/zig/tokenizer.zig @@ -1513,7 +1513,7 @@ test "tokenizer - unknown length pointer and then c pointer" { }); } -test "tokenizer - char literal with hex escape" { +test "tokenizer - code point literal with hex escape" { testTokenize( \\'\x1b' , &[_]Token.Id{.CharLiteral}); @@ -1522,7 +1522,7 @@ test "tokenizer - char literal with hex escape" { , &[_]Token.Id{ .Invalid, .Invalid }); } -test "tokenizer - char literal with unicode escapes" { +test "tokenizer - code point literal with unicode escapes" { // Valid unicode escapes testTokenize( \\'\u{3}' @@ -1572,7 +1572,7 @@ test "tokenizer - char literal with unicode escapes" { , &[_]Token.Id{ .Invalid, .IntegerLiteral, .Invalid }); } -test "tokenizer - char literal with unicode code point" { +test "tokenizer - code point literal with unicode code point" { testTokenize( \\'💩' , &[_]Token.Id{.CharLiteral}); diff --git a/src/stage1/tokenizer.cpp b/src/stage1/tokenizer.cpp index 1d25bca17b..623169a313 100644 --- a/src/stage1/tokenizer.cpp +++ b/src/stage1/tokenizer.cpp @@ -1447,7 +1447,7 @@ void tokenize(Buf *buf, Tokenization *out) { tokenize_error(&t, "unterminated string"); break; } else if (t.cur_tok->id == TokenIdCharLiteral) { - tokenize_error(&t, "unterminated character literal"); + tokenize_error(&t, "unterminated Unicode code point literal"); break; } else { zig_unreachable(); @@ -1456,7 +1456,7 @@ void tokenize(Buf *buf, Tokenization *out) { case TokenizeStateCharLiteral: case TokenizeStateCharLiteralEnd: case TokenizeStateCharLiteralUnicode: - tokenize_error(&t, "unterminated character literal"); + tokenize_error(&t, "unterminated Unicode code point literal"); break; case TokenizeStateSymbol: case TokenizeStateZero: