diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2021-03-12 18:05:27 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2021-03-12 18:05:27 -0700 |
| commit | 1f34c03ac14ac352ec03267ca8592dadfbd5e4bc (patch) | |
| tree | ebcb851922636b7dd2b17acb72187836c86180ec /lib/std | |
| parent | 868253a9c94d9907fae81e5e3108c7d10a85f5c3 (diff) | |
| parent | 8ebb18d9da0bfbe6a974636fd36e3391d1de253b (diff) | |
| download | zig-1f34c03ac14ac352ec03267ca8592dadfbd5e4bc.tar.gz zig-1f34c03ac14ac352ec03267ca8592dadfbd5e4bc.zip | |
Merge remote-tracking branch 'origin/master' into llvm12
Diffstat (limited to 'lib/std')
| -rw-r--r-- | lib/std/Progress.zig | 130 | ||||
| -rw-r--r-- | lib/std/Thread.zig | 2 | ||||
| -rw-r--r-- | lib/std/c.zig | 4 | ||||
| -rw-r--r-- | lib/std/elf.zig | 52 | ||||
| -rw-r--r-- | lib/std/fmt.zig | 161 | ||||
| -rw-r--r-- | lib/std/fmt/parse_float.zig | 4 | ||||
| -rw-r--r-- | lib/std/hash_map.zig | 45 | ||||
| -rw-r--r-- | lib/std/io/c_writer.zig | 2 | ||||
| -rw-r--r-- | lib/std/mem.zig | 10 | ||||
| -rw-r--r-- | lib/std/meta.zig | 52 | ||||
| -rw-r--r-- | lib/std/multi_array_list.zig | 109 | ||||
| -rw-r--r-- | lib/std/os.zig | 34 | ||||
| -rw-r--r-- | lib/std/os/linux/io_uring.zig | 8 | ||||
| -rw-r--r-- | lib/std/special/init-exe/build.zig | 4 | ||||
| -rw-r--r-- | lib/std/special/init-lib/build.zig | 7 | ||||
| -rw-r--r-- | lib/std/zig/ast.zig | 19 | ||||
| -rw-r--r-- | lib/std/zig/parse.zig | 82 | ||||
| -rw-r--r-- | lib/std/zig/parser_test.zig | 57 | ||||
| -rw-r--r-- | lib/std/zig/render.zig | 29 |
19 files changed, 507 insertions, 304 deletions
diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig index 4afd191b93..c912b60f57 100644 --- a/lib/std/Progress.zig +++ b/lib/std/Progress.zig @@ -59,10 +59,6 @@ done: bool = true, /// while it was still being accessed by the `refresh` function. update_lock: std.Thread.Mutex = .{}, -/// Keeps track of how many columns in the terminal have been output, so that -/// we can move the cursor back later. -columns_written: usize = undefined, - /// Represents one unit of progress. Each node can have children nodes, or /// one can use integers with `update`. pub const Node = struct { @@ -159,7 +155,6 @@ pub fn start(self: *Progress, name: []const u8, estimated_total_items: usize) !* .unprotected_estimated_total_items = estimated_total_items, .unprotected_completed_items = 0, }; - self.columns_written = 0; self.prev_refresh_timestamp = 0; self.timer = try std.time.Timer.start(); self.done = false; @@ -187,64 +182,67 @@ pub fn refresh(self: *Progress) void { return self.refreshWithHeldLock(); } +// ED -- Clear screen +const ED = "\x1b[J"; +// DECSC -- Save cursor position +const DECSC = "\x1b7"; +// DECRC -- Restore cursor position +const DECRC = "\x1b8"; +// Note that ESC7/ESC8 are used instead of CSI s/CSI u as the latter are not +// supported by some terminals (eg. Terminal.app). + 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; var end: usize = 0; - if (self.columns_written > 0) { - // restore the cursor position by moving the cursor - // `columns_written` cells to the left, then clear the rest of the - // line - if (self.supports_ansi_escape_codes) { - end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[{d}D", .{self.columns_written}) catch unreachable).len; - end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[0K", .{}) catch unreachable).len; - } else if (std.builtin.os.tag == .windows) winapi: { - var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; - if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE) - unreachable; - - var cursor_pos = windows.COORD{ - .X = info.dwCursorPosition.X - @intCast(windows.SHORT, self.columns_written), - .Y = info.dwCursorPosition.Y, - }; - - if (cursor_pos.X < 0) - cursor_pos.X = 0; - - const fill_chars = @intCast(windows.DWORD, info.dwSize.X - cursor_pos.X); - - var written: windows.DWORD = undefined; - if (windows.kernel32.FillConsoleOutputAttribute( - file.handle, - info.wAttributes, - fill_chars, - cursor_pos, - &written, - ) != windows.TRUE) { - // Stop trying to write to this file. - self.terminal = null; - break :winapi; - } - if (windows.kernel32.FillConsoleOutputCharacterA( - file.handle, - ' ', - fill_chars, - cursor_pos, - &written, - ) != windows.TRUE) unreachable; - - if (windows.kernel32.SetConsoleCursorPosition(file.handle, cursor_pos) != windows.TRUE) - unreachable; - } else { - // we are in a "dumb" terminal like in acme or writing to a file - self.output_buffer[end] = '\n'; - end += 1; - } + // Save the cursor position and clear the part of the screen below. + // Clearing only the line is not enough as the terminal may wrap the line + // when it becomes too long. + var saved_cursor_pos: windows.COORD = undefined; + if (self.supports_ansi_escape_codes) { + const seq_before = DECSC ++ ED; + std.mem.copy(u8, self.output_buffer[end..], seq_before); + end += seq_before.len; + } else if (std.builtin.os.tag == .windows) winapi: { + var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; + if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE) + unreachable; + + saved_cursor_pos = info.dwCursorPosition; + + const window_height = @intCast(windows.DWORD, info.srWindow.Bottom - info.srWindow.Top + 1); + const window_width = @intCast(windows.DWORD, info.srWindow.Right - info.srWindow.Left + 1); + // Number of terminal cells to clear, starting from the cursor position + // and ending at the window bottom right corner. + const fill_chars = if (window_width == 0 or window_height == 0) 0 else chars: { + break :chars window_width * (window_height - + @intCast(windows.DWORD, info.dwCursorPosition.Y - info.srWindow.Top)) - + @intCast(windows.DWORD, info.dwCursorPosition.X - info.srWindow.Left); + }; - self.columns_written = 0; + var written: windows.DWORD = undefined; + if (windows.kernel32.FillConsoleOutputAttribute( + file.handle, + info.wAttributes, + fill_chars, + saved_cursor_pos, + &written, + ) != windows.TRUE) { + // Stop trying to write to this file. + self.terminal = null; + break :winapi; + } + if (windows.kernel32.FillConsoleOutputCharacterA( + file.handle, + ' ', + fill_chars, + saved_cursor_pos, + &written, + ) != windows.TRUE) { + unreachable; + } } if (!self.done) { @@ -279,10 +277,26 @@ fn refreshWithHeldLock(self: *Progress) void { } } + // We're done printing the updated message, restore the cursor position. + if (self.supports_ansi_escape_codes) { + const seq_after = DECRC; + std.mem.copy(u8, self.output_buffer[end..], seq_after); + end += seq_after.len; + } else if (std.builtin.os.tag != .windows) { + self.output_buffer[end] = '\n'; + end += 1; + } + _ = file.write(self.output_buffer[0..end]) catch |e| { // Stop trying to write to this file once it errors. self.terminal = null; }; + + if (std.builtin.os.tag == .windows) { + if (windows.kernel32.SetConsoleCursorPosition(file.handle, saved_cursor_pos) != windows.TRUE) + unreachable; + } + self.prev_refresh_timestamp = self.timer.read(); } @@ -293,17 +307,14 @@ pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void { self.terminal = null; return; }; - self.columns_written = 0; } fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: anytype) void { if (std.fmt.bufPrint(self.output_buffer[end.*..], format, args)) |written| { const amt = written.len; end.* += amt; - self.columns_written += amt; } else |err| switch (err) { error.NoSpaceLeft => { - self.columns_written += self.output_buffer.len - end.*; end.* = self.output_buffer.len; }, } @@ -311,7 +322,6 @@ fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: any const max_end = self.output_buffer.len - bytes_needed_for_esc_codes_at_end; if (end.* > max_end) { const suffix = "... "; - self.columns_written = self.columns_written - (end.* - max_end) + suffix.len; std.mem.copy(u8, self.output_buffer[max_end..], suffix); end.* = max_end + suffix.len; } diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index b9a69d151f..7e8a6226e6 100644 --- a/lib/std/Thread.zig +++ b/lib/std/Thread.zig @@ -362,7 +362,7 @@ pub fn spawn(comptime startFn: anytype, context: SpawnContextType(@TypeOf(startF os.EAGAIN => return error.SystemResources, os.EPERM => unreachable, os.EINVAL => unreachable, - else => return os.unexpectedErrno(@intCast(usize, err)), + else => return os.unexpectedErrno(err), } return thread_obj; diff --git a/lib/std/c.zig b/lib/std/c.zig index 1688824dd9..bd0ce04f75 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -37,9 +37,9 @@ pub usingnamespace switch (std.Target.current.os.tag) { else => struct {}, }; -pub fn getErrno(rc: anytype) u16 { +pub fn getErrno(rc: anytype) c_int { if (rc == -1) { - return @intCast(u16, _errno().*); + return _errno().*; } else { return 0; } diff --git a/lib/std/elf.zig b/lib/std/elf.zig index e644c6631a..36382ecc42 100644 --- a/lib/std/elf.zig +++ b/lib/std/elf.zig @@ -337,6 +337,7 @@ pub const ET = extern enum(u16) { /// All integers are native endian. pub const Header = struct { endian: builtin.Endian, + machine: EM, is_64: bool, entry: u64, phoff: u64, @@ -387,8 +388,14 @@ pub const Header = struct { else => return error.InvalidElfClass, }; + const machine = if (need_bswap) blk: { + const value = @enumToInt(hdr32.e_machine); + break :blk @intToEnum(EM, @byteSwap(@TypeOf(value), value)); + } else hdr32.e_machine; + return @as(Header, .{ .endian = endian, + .machine = machine, .is_64 = is_64, .entry = int(is_64, need_bswap, hdr32.e_entry, hdr64.e_entry), .phoff = int(is_64, need_bswap, hdr32.e_phoff, hdr64.e_phoff), @@ -422,16 +429,8 @@ pub fn ProgramHeaderIterator(ParseSource: anytype) type { if (self.elf_header.endian == std.builtin.endian) return phdr; // Convert fields to native endianness. - return Elf64_Phdr{ - .p_type = @byteSwap(@TypeOf(phdr.p_type), phdr.p_type), - .p_offset = @byteSwap(@TypeOf(phdr.p_offset), phdr.p_offset), - .p_vaddr = @byteSwap(@TypeOf(phdr.p_vaddr), phdr.p_vaddr), - .p_paddr = @byteSwap(@TypeOf(phdr.p_paddr), phdr.p_paddr), - .p_filesz = @byteSwap(@TypeOf(phdr.p_filesz), phdr.p_filesz), - .p_memsz = @byteSwap(@TypeOf(phdr.p_memsz), phdr.p_memsz), - .p_flags = @byteSwap(@TypeOf(phdr.p_flags), phdr.p_flags), - .p_align = @byteSwap(@TypeOf(phdr.p_align), phdr.p_align), - }; + bswapAllFields(Elf64_Phdr, &phdr); + return phdr; } var phdr: Elf32_Phdr = undefined; @@ -442,16 +441,7 @@ pub fn ProgramHeaderIterator(ParseSource: anytype) type { // ELF endianness does NOT match native endianness. if (self.elf_header.endian != std.builtin.endian) { // Convert fields to native endianness. - phdr = .{ - .p_type = @byteSwap(@TypeOf(phdr.p_type), phdr.p_type), - .p_offset = @byteSwap(@TypeOf(phdr.p_offset), phdr.p_offset), - .p_vaddr = @byteSwap(@TypeOf(phdr.p_vaddr), phdr.p_vaddr), - .p_paddr = @byteSwap(@TypeOf(phdr.p_paddr), phdr.p_paddr), - .p_filesz = @byteSwap(@TypeOf(phdr.p_filesz), phdr.p_filesz), - .p_memsz = @byteSwap(@TypeOf(phdr.p_memsz), phdr.p_memsz), - .p_flags = @byteSwap(@TypeOf(phdr.p_flags), phdr.p_flags), - .p_align = @byteSwap(@TypeOf(phdr.p_align), phdr.p_align), - }; + bswapAllFields(Elf32_Phdr, &phdr); } // Convert 32-bit header to 64-bit. @@ -562,6 +552,26 @@ pub fn int32(need_bswap: bool, int_32: anytype, comptime Int64: anytype) Int64 { } } +pub fn bswapAllFields(comptime S: type, ptr: *S) void { + if (@typeInfo(S) != .Struct) @compileError("bswapAllFields expects a struct as the first argument"); + inline for (std.meta.fields(S)) |f| { + @field(ptr, f.name) = @byteSwap(f.field_type, @field(ptr, f.name)); + } +} +test "bswapAllFields" { + var s: Elf32_Chdr = .{ + .ch_type = 0x12341234, + .ch_size = 0x56785678, + .ch_addralign = 0x12124242, + }; + bswapAllFields(Elf32_Chdr, &s); + std.testing.expectEqual(Elf32_Chdr{ + .ch_type = 0x34123412, + .ch_size = 0x78567856, + .ch_addralign = 0x42421212, + }, s); +} + pub const EI_NIDENT = 16; pub const EI_CLASS = 4; @@ -1515,6 +1525,8 @@ pub const EM = extern enum(u16) { /// Linux kernel bpf virtual machine _BPF = 247, + + _, }; /// Section data should be writable during execution. diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index 1f924bf00c..90c0d98539 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -35,11 +35,11 @@ pub const FormatOptions = struct { /// /// The format string must be comptime known and may contain placeholders following /// this format: -/// `{[position][specifier]:[fill][alignment][width].[precision]}` +/// `{[argument][specifier]:[fill][alignment][width].[precision]}` /// /// Each word between `[` and `]` is a parameter you have to replace with something: /// -/// - *position* is the index of the argument that should be inserted +/// - *argument* is either the index or the name of the argument that should be inserted /// - *specifier* is a type-dependent formatting option that determines how a type should formatted (see below) /// - *fill* is a single character which is used to pad the formatted text /// - *alignment* is one of the three characters `<`, `^` or `>`. they define if the text is *left*, *center*, or *right* aligned @@ -52,16 +52,10 @@ pub const FormatOptions = struct { /// the digits after `:` is interpreted as *width*, not *fill*. /// /// The *specifier* has several options for types: -/// - `x` and `X`: -/// - format the non-numeric value as a string of bytes in hexadecimal notation ("binary dump") in either lower case or upper case -/// - output numeric value in hexadecimal notation +/// - `x` and `X`: output numeric value in hexadecimal notation /// - `s`: /// - for pointer-to-many and C pointers of u8, print as a C-string using zero-termination /// - for slices of u8, print the entire slice as a string without zero-termination -/// - `z`: escape the string with @"" syntax if it is not a valid Zig identifier. -/// - `Z`: print the string escaping non-printable characters using Zig escape sequences. -/// - `B` and `Bi`: output a memory size in either metric (1000) or power-of-two (1024) based notation. works for both float and integer values. -/// - `e` and `E`: if printing a string, escape non-printable characters /// - `e`: output floating point value in scientific notation /// - `d`: output numeric value in decimal notation /// - `b`: output integer value in binary notation @@ -620,9 +614,9 @@ fn formatValue( writer: anytype, ) !void { if (comptime std.mem.eql(u8, fmt, "B")) { - return formatBytes(value, options, 1000, writer); + @compileError("specifier 'B' has been deprecated, wrap your argument in std.fmt.fmtIntSizeDec instead"); } else if (comptime std.mem.eql(u8, fmt, "Bi")) { - return formatBytes(value, options, 1024, writer); + @compileError("specifier 'Bi' has been deprecated, wrap your argument in std.fmt.fmtIntSizeBin instead"); } const T = @TypeOf(value); @@ -790,6 +784,67 @@ pub fn fmtSliceEscapeUpper(bytes: []const u8) std.fmt.Formatter(formatSliceEscap return .{ .data = bytes }; } +fn formatSizeImpl(comptime radix: comptime_int) type { + return struct { + fn f( + value: u64, + comptime fmt: []const u8, + options: FormatOptions, + writer: anytype, + ) !void { + if (value == 0) { + return writer.writeAll("0B"); + } + + const mags_si = " kMGTPEZY"; + const mags_iec = " KMGTPEZY"; + + const log2 = math.log2(value); + const magnitude = switch (radix) { + 1000 => math.min(log2 / comptime math.log2(1000), mags_si.len - 1), + 1024 => math.min(log2 / 10, mags_iec.len - 1), + else => unreachable, + }; + const new_value = lossyCast(f64, value) / math.pow(f64, lossyCast(f64, radix), lossyCast(f64, magnitude)); + const suffix = switch (radix) { + 1000 => mags_si[magnitude], + 1024 => mags_iec[magnitude], + else => unreachable, + }; + + try formatFloatDecimal(new_value, options, writer); + + if (suffix == ' ') { + return writer.writeAll("B"); + } + + const buf = switch (radix) { + 1000 => &[_]u8{ suffix, 'B' }, + 1024 => &[_]u8{ suffix, 'i', 'B' }, + else => unreachable, + }; + return writer.writeAll(buf); + } + }; +} + +const formatSizeDec = formatSizeImpl(1000).f; +const formatSizeBin = formatSizeImpl(1024).f; + +/// Return a Formatter for a u64 value representing a file size. +/// This formatter represents the number as multiple of 1000 and uses the SI +/// measurement units (kB, MB, GB, ...). +pub fn fmtIntSizeDec(value: u64) std.fmt.Formatter(formatSizeDec) { + return .{ .data = value }; +} + +/// Return a Formatter for a u64 value representing a file size. +/// This formatter represents the number as multiple of 1024 and uses the IEC +/// measurement units (KiB, MiB, GiB, ...). +pub fn fmtIntSizeBin(value: u64) std.fmt.Formatter(formatSizeBin) { + return .{ .data = value }; +} + pub fn formatText( bytes: []const u8, comptime fmt: []const u8, @@ -1111,47 +1166,6 @@ pub fn formatFloatDecimal( } } -pub fn formatBytes( - value: anytype, - options: FormatOptions, - comptime radix: usize, - writer: anytype, -) !void { - if (value == 0) { - return writer.writeAll("0B"); - } - - const is_float = comptime std.meta.trait.is(.Float)(@TypeOf(value)); - const mags_si = " kMGTPEZY"; - const mags_iec = " KMGTPEZY"; - - const log2 = if (is_float) @floatToInt(usize, math.log2(value)) else math.log2(value); - const magnitude = switch (radix) { - 1000 => math.min(log2 / comptime math.log2(1000), mags_si.len - 1), - 1024 => math.min(log2 / 10, mags_iec.len - 1), - else => unreachable, - }; - const new_value = lossyCast(f64, value) / math.pow(f64, lossyCast(f64, radix), lossyCast(f64, magnitude)); - const suffix = switch (radix) { - 1000 => mags_si[magnitude], - 1024 => mags_iec[magnitude], - else => unreachable, - }; - - try formatFloatDecimal(new_value, options, writer); - - if (suffix == ' ') { - return writer.writeAll("B"); - } - - const buf = switch (radix) { - 1000 => &[_]u8{ suffix, 'B' }, - 1024 => &[_]u8{ suffix, 'i', 'B' }, - else => unreachable, - }; - return writer.writeAll(buf); -} - pub fn formatInt( value: anytype, base: u8, @@ -1210,11 +1224,7 @@ pub fn formatIntBuf(out_buf: []u8, value: anytype, base: u8, uppercase: bool, op return fbs.pos; } -/// Formats a number of nanoseconds according to its magnitude: -/// -/// - #ns -/// - [#y][#w][#d][#h][#m]#[.###][u|m]s -pub fn formatDuration(ns: u64, writer: anytype) !void { +fn formatDuration(ns: u64, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { var ns_remaining = ns; inline for (.{ .{ .ns = 365 * std.time.ns_per_day, .sep = 'y' }, @@ -1256,12 +1266,18 @@ pub fn formatDuration(ns: u64, writer: anytype) !void { } } - try formatInt(ns, 10, false, .{}, writer); + try formatInt(ns_remaining, 10, false, .{}, writer); try writer.writeAll("ns"); return; } -test "formatDuration" { +/// Return a Formatter for number of nanoseconds according to its magnitude: +/// [#y][#w][#d][#h][#m]#[.###][n|u|m]s +pub fn fmtDuration(ns: u64) Formatter(formatDuration) { + return .{ .data = ns }; +} + +test "fmtDuration" { var buf: [24]u8 = undefined; inline for (.{ .{ .s = "0ns", .d = 0 }, @@ -1287,26 +1303,13 @@ test "formatDuration" { .{ .s = "1y1h999.999us", .d = 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms - 1 }, .{ .s = "1y1h1ms", .d = 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms }, .{ .s = "1y1h1ms", .d = 365 * std.time.ns_per_day + std.time.ns_per_hour + std.time.ns_per_ms + 1 }, + .{ .s = "1y1m999ns", .d = 365 * std.time.ns_per_day + std.time.ns_per_min + 999 }, }) |tc| { - const slice = try bufPrint(&buf, "{}", .{duration(tc.d)}); + const slice = try bufPrint(&buf, "{}", .{fmtDuration(tc.d)}); std.testing.expectEqualStrings(tc.s, slice); } } -/// Wraps a `u64` to format with `formatDuration`. -const Duration = struct { - ns: u64, - - pub fn format(self: Duration, comptime fmt: []const u8, options: FormatOptions, writer: anytype) !void { - return formatDuration(self.ns, writer); - } -}; - -/// Formats a number of nanoseconds according to its magnitude. See `formatDuration`. -pub fn duration(ns: u64) Duration { - return Duration{ .ns = ns }; -} - pub const ParseIntError = error{ /// The result cannot fit in the type specified Overflow, @@ -1806,8 +1809,12 @@ test "cstr" { } test "filesize" { - try expectFmt("file size: 63MiB\n", "file size: {Bi}\n", .{@as(usize, 63 * 1024 * 1024)}); - try expectFmt("file size: 66.06MB\n", "file size: {B:.2}\n", .{@as(usize, 63 * 1024 * 1024)}); + try expectFmt("file size: 42B\n", "file size: {}\n", .{fmtIntSizeDec(42)}); + try expectFmt("file size: 42B\n", "file size: {}\n", .{fmtIntSizeBin(42)}); + try expectFmt("file size: 63MB\n", "file size: {}\n", .{fmtIntSizeDec(63 * 1000 * 1000)}); + try expectFmt("file size: 63MiB\n", "file size: {}\n", .{fmtIntSizeBin(63 * 1024 * 1024)}); + try expectFmt("file size: 66.06MB\n", "file size: {:.2}\n", .{fmtIntSizeDec(63 * 1024 * 1024)}); + try expectFmt("file size: 60.08MiB\n", "file size: {:.2}\n", .{fmtIntSizeBin(63 * 1000 * 1000)}); } test "struct" { @@ -2213,8 +2220,6 @@ test "vector" { try expectFmt("{ -2, -1, +0, +1 }", "{d:5}", .{vi64}); try expectFmt("{ 1000, 2000, 3000, 4000 }", "{}", .{vu64}); try expectFmt("{ 3e8, 7d0, bb8, fa0 }", "{x}", .{vu64}); - try expectFmt("{ 1kB, 2kB, 3kB, 4kB }", "{B}", .{vu64}); - try expectFmt("{ 1000B, 1.953125KiB, 2.9296875KiB, 3.90625KiB }", "{Bi}", .{vu64}); } test "enum-literal" { diff --git a/lib/std/fmt/parse_float.zig b/lib/std/fmt/parse_float.zig index 324b06898e..19e17ef5a8 100644 --- a/lib/std/fmt/parse_float.zig +++ b/lib/std/fmt/parse_float.zig @@ -339,7 +339,7 @@ fn caseInEql(a: []const u8, b: []const u8) bool { } pub fn parseFloat(comptime T: type, s: []const u8) !T { - if (s.len == 0) { + if (s.len == 0 or (s.len == 1 and (s[0] == '+' or s[0] == '-'))) { return error.InvalidCharacter; } @@ -379,6 +379,8 @@ test "fmt.parseFloat" { testing.expectError(error.InvalidCharacter, parseFloat(T, "")); testing.expectError(error.InvalidCharacter, parseFloat(T, " 1")); testing.expectError(error.InvalidCharacter, parseFloat(T, "1abc")); + testing.expectError(error.InvalidCharacter, parseFloat(T, "+")); + testing.expectError(error.InvalidCharacter, parseFloat(T, "-")); expectEqual(try parseFloat(T, "0"), 0.0); expectEqual(try parseFloat(T, "0"), 0.0); diff --git a/lib/std/hash_map.zig b/lib/std/hash_map.zig index e2e0f056e1..62f430a672 100644 --- a/lib/std/hash_map.zig +++ b/lib/std/hash_map.zig @@ -50,20 +50,20 @@ pub fn getAutoEqlFn(comptime K: type) (fn (K, K) bool) { } pub fn AutoHashMap(comptime K: type, comptime V: type) type { - return HashMap(K, V, getAutoHashFn(K), getAutoEqlFn(K), DefaultMaxLoadPercentage); + return HashMap(K, V, getAutoHashFn(K), getAutoEqlFn(K), default_max_load_percentage); } pub fn AutoHashMapUnmanaged(comptime K: type, comptime V: type) type { - return HashMapUnmanaged(K, V, getAutoHashFn(K), getAutoEqlFn(K), DefaultMaxLoadPercentage); + return HashMapUnmanaged(K, V, getAutoHashFn(K), getAutoEqlFn(K), default_max_load_percentage); } /// Builtin hashmap for strings as keys. pub fn StringHashMap(comptime V: type) type { - return HashMap([]const u8, V, hashString, eqlString, DefaultMaxLoadPercentage); + return HashMap([]const u8, V, hashString, eqlString, default_max_load_percentage); } pub fn StringHashMapUnmanaged(comptime V: type) type { - return HashMapUnmanaged([]const u8, V, hashString, eqlString, DefaultMaxLoadPercentage); + return HashMapUnmanaged([]const u8, V, hashString, eqlString, default_max_load_percentage); } pub fn eqlString(a: []const u8, b: []const u8) bool { @@ -74,7 +74,10 @@ pub fn hashString(s: []const u8) u64 { return std.hash.Wyhash.hash(0, s); } -pub const DefaultMaxLoadPercentage = 80; +/// Deprecated use `default_max_load_percentage` +pub const DefaultMaxLoadPercentage = default_max_load_percentage; + +pub const default_max_load_percentage = 80; /// General purpose hash table. /// No order is guaranteed and any modification invalidates live iterators. @@ -89,13 +92,13 @@ pub fn HashMap( comptime V: type, comptime hashFn: fn (key: K) u64, comptime eqlFn: fn (a: K, b: K) bool, - comptime MaxLoadPercentage: u64, + comptime max_load_percentage: u64, ) type { return struct { unmanaged: Unmanaged, allocator: *Allocator, - pub const Unmanaged = HashMapUnmanaged(K, V, hashFn, eqlFn, MaxLoadPercentage); + pub const Unmanaged = HashMapUnmanaged(K, V, hashFn, eqlFn, max_load_percentage); pub const Entry = Unmanaged.Entry; pub const Hash = Unmanaged.Hash; pub const Iterator = Unmanaged.Iterator; @@ -251,9 +254,9 @@ pub fn HashMapUnmanaged( comptime V: type, hashFn: fn (key: K) u64, eqlFn: fn (a: K, b: K) bool, - comptime MaxLoadPercentage: u64, + comptime max_load_percentage: u64, ) type { - comptime assert(MaxLoadPercentage > 0 and MaxLoadPercentage < 100); + comptime assert(max_load_percentage > 0 and max_load_percentage < 100); return struct { const Self = @This(); @@ -274,12 +277,12 @@ pub fn HashMapUnmanaged( // Having a countdown to grow reduces the number of instructions to // execute when determining if the hashmap has enough capacity already. /// Number of available slots before a grow is needed to satisfy the - /// `MaxLoadPercentage`. + /// `max_load_percentage`. available: Size = 0, // This is purely empirical and not a /very smart magic constantâ„¢/. /// Capacity of the first grow when bootstrapping the hashmap. - const MinimalCapacity = 8; + const minimal_capacity = 8; // This hashmap is specially designed for sizes that fit in a u32. const Size = u32; @@ -382,7 +385,7 @@ pub fn HashMapUnmanaged( found_existing: bool, }; - pub const Managed = HashMap(K, V, hashFn, eqlFn, MaxLoadPercentage); + pub const Managed = HashMap(K, V, hashFn, eqlFn, max_load_percentage); pub fn promote(self: Self, allocator: *Allocator) Managed { return .{ @@ -392,7 +395,7 @@ pub fn HashMapUnmanaged( } fn isUnderMaxLoadPercentage(size: Size, cap: Size) bool { - return size * 100 < MaxLoadPercentage * cap; + return size * 100 < max_load_percentage * cap; } pub fn init(allocator: *Allocator) Self { @@ -425,7 +428,7 @@ pub fn HashMapUnmanaged( } fn capacityForSize(size: Size) Size { - var new_cap = @truncate(u32, (@as(u64, size) * 100) / MaxLoadPercentage + 1); + var new_cap = @truncate(u32, (@as(u64, size) * 100) / max_load_percentage + 1); new_cap = math.ceilPowerOfTwo(u32, new_cap) catch unreachable; return new_cap; } @@ -439,7 +442,7 @@ pub fn HashMapUnmanaged( if (self.metadata) |_| { self.initMetadatas(); self.size = 0; - self.available = @truncate(u32, (self.capacity() * MaxLoadPercentage) / 100); + self.available = @truncate(u32, (self.capacity() * max_load_percentage) / 100); } } @@ -712,9 +715,9 @@ pub fn HashMapUnmanaged( } // This counts the number of occupied slots, used + tombstones, which is - // what has to stay under the MaxLoadPercentage of capacity. + // what has to stay under the max_load_percentage of capacity. fn load(self: *const Self) Size { - const max_load = (self.capacity() * MaxLoadPercentage) / 100; + const max_load = (self.capacity() * max_load_percentage) / 100; assert(max_load >= self.available); return @truncate(Size, max_load - self.available); } @@ -733,7 +736,7 @@ pub fn HashMapUnmanaged( const new_cap = capacityForSize(self.size); try other.allocate(allocator, new_cap); other.initMetadatas(); - other.available = @truncate(u32, (new_cap * MaxLoadPercentage) / 100); + other.available = @truncate(u32, (new_cap * max_load_percentage) / 100); var i: Size = 0; var metadata = self.metadata.?; @@ -751,7 +754,7 @@ pub fn HashMapUnmanaged( } fn grow(self: *Self, allocator: *Allocator, new_capacity: Size) !void { - const new_cap = std.math.max(new_capacity, MinimalCapacity); + const new_cap = std.math.max(new_capacity, minimal_capacity); assert(new_cap > self.capacity()); assert(std.math.isPowerOfTwo(new_cap)); @@ -759,7 +762,7 @@ pub fn HashMapUnmanaged( defer map.deinit(allocator); try map.allocate(allocator, new_cap); map.initMetadatas(); - map.available = @truncate(u32, (new_cap * MaxLoadPercentage) / 100); + map.available = @truncate(u32, (new_cap * max_load_percentage) / 100); if (self.size != 0) { const old_capacity = self.capacity(); @@ -943,7 +946,7 @@ test "std.hash_map ensureCapacity with existing elements" { try map.put(0, 0); expectEqual(map.count(), 1); - expectEqual(map.capacity(), @TypeOf(map).Unmanaged.MinimalCapacity); + expectEqual(map.capacity(), @TypeOf(map).Unmanaged.minimal_capacity); try map.ensureCapacity(65); expectEqual(map.count(), 1); diff --git a/lib/std/io/c_writer.zig b/lib/std/io/c_writer.zig index fa7d7eb13a..26ae1fde01 100644 --- a/lib/std/io/c_writer.zig +++ b/lib/std/io/c_writer.zig @@ -30,7 +30,7 @@ fn cWriterWrite(c_file: *std.c.FILE, bytes: []const u8) std.fs.File.WriteError!u os.ENOSPC => return error.NoSpaceLeft, os.EPERM => return error.AccessDenied, os.EPIPE => return error.BrokenPipe, - else => |err| return os.unexpectedErrno(@intCast(usize, err)), + else => |err| return os.unexpectedErrno(err), } } diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 581fb16e6c..2bd5fdac7b 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -647,7 +647,10 @@ pub fn len(value: anytype) usize { indexOfSentinel(info.child, sentinel, value) else @compileError("length of pointer with no sentinel"), - .C => indexOfSentinel(info.child, 0, value), + .C => { + assert(value != null); + return indexOfSentinel(info.child, 0, value); + }, .Slice => value.len, }, .Struct => |info| if (info.is_tuple) { @@ -708,7 +711,10 @@ pub fn lenZ(ptr: anytype) usize { indexOfSentinel(info.child, sentinel, ptr) else @compileError("length of pointer with no sentinel"), - .C => indexOfSentinel(info.child, 0, ptr), + .C => { + assert(ptr != null); + return indexOfSentinel(info.child, 0, ptr); + }, .Slice => if (info.sentinel) |sentinel| indexOfSentinel(info.child, sentinel, ptr.ptr) else diff --git a/lib/std/meta.zig b/lib/std/meta.zig index 7ec29dcd0e..fd3e03bdbd 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -1094,6 +1094,58 @@ test "sizeof" { testing.expect(sizeof(c_void) == 1); } +pub const CIntLiteralRadix = enum { decimal, octal, hexadecimal }; + +fn PromoteIntLiteralReturnType(comptime SuffixType: type, comptime number: comptime_int, comptime radix: CIntLiteralRadix) type { + const signed_decimal = [_]type{ c_int, c_long, c_longlong }; + const signed_oct_hex = [_]type{ c_int, c_uint, c_long, c_ulong, c_longlong, c_ulonglong }; + const unsigned = [_]type{ c_uint, c_ulong, c_ulonglong }; + + const list: []const type = if (@typeInfo(SuffixType).Int.signedness == .unsigned) + &unsigned + else if (radix == .decimal) + &signed_decimal + else + &signed_oct_hex; + + var pos = mem.indexOfScalar(type, list, SuffixType).?; + + while (pos < list.len) : (pos += 1) { + if (number >= math.minInt(list[pos]) and number <= math.maxInt(list[pos])) { + return list[pos]; + } + } + @compileError("Integer literal is too large"); +} + +/// Promote the type of an integer literal until it fits as C would. +/// This is for translate-c and is not intended for general use. +pub fn promoteIntLiteral( + comptime SuffixType: type, + comptime number: comptime_int, + comptime radix: CIntLiteralRadix, +) PromoteIntLiteralReturnType(SuffixType, number, radix) { + return number; +} + +test "promoteIntLiteral" { + const signed_hex = promoteIntLiteral(c_int, math.maxInt(c_int) + 1, .hexadecimal); + testing.expectEqual(c_uint, @TypeOf(signed_hex)); + + if (math.maxInt(c_longlong) == math.maxInt(c_int)) return; + + const signed_decimal = promoteIntLiteral(c_int, math.maxInt(c_int) + 1, .decimal); + const unsigned = promoteIntLiteral(c_uint, math.maxInt(c_uint) + 1, .hexadecimal); + + if (math.maxInt(c_long) > math.maxInt(c_int)) { + testing.expectEqual(c_long, @TypeOf(signed_decimal)); + testing.expectEqual(c_ulong, @TypeOf(unsigned)); + } else { + testing.expectEqual(c_longlong, @TypeOf(signed_decimal)); + testing.expectEqual(c_ulonglong, @TypeOf(unsigned)); + } +} + /// For a given function type, returns a tuple type which fields will /// correspond to the argument types. /// diff --git a/lib/std/multi_array_list.zig b/lib/std/multi_array_list.zig index 99a9fff7f0..f4d89d198c 100644 --- a/lib/std/multi_array_list.zig +++ b/lib/std/multi_array_list.zig @@ -8,6 +8,7 @@ const assert = std.debug.assert; const meta = std.meta; const mem = std.mem; const Allocator = mem.Allocator; +const testing = std.testing; pub fn MultiArrayList(comptime S: type) type { return struct { @@ -27,8 +28,11 @@ pub fn MultiArrayList(comptime S: type) type { capacity: usize, pub fn items(self: Slice, comptime field: Field) []FieldType(field) { - const byte_ptr = self.ptrs[@enumToInt(field)]; const F = FieldType(field); + if (self.len == 0) { + return &[_]F{}; + } + const byte_ptr = self.ptrs[@enumToInt(field)]; const casted_ptr = @ptrCast([*]F, @alignCast(@alignOf(F), byte_ptr)); return casted_ptr[0..self.len]; } @@ -247,6 +251,7 @@ pub fn MultiArrayList(comptime S: type) type { .exact, ); if (self.len == 0) { + gpa.free(self.allocatedBytes()); self.bytes = new_bytes.ptr; self.capacity = new_capacity; return; @@ -287,7 +292,6 @@ pub fn MultiArrayList(comptime S: type) type { } test "basic usage" { - const testing = std.testing; const ally = testing.allocator; const Foo = struct { @@ -299,6 +303,8 @@ test "basic usage" { var list = MultiArrayList(Foo){}; defer list.deinit(ally); + testing.expectEqual(@as(usize, 0), list.items(.a).len); + try list.ensureCapacity(ally, 2); list.appendAssumeCapacity(.{ @@ -369,7 +375,7 @@ test "basic usage" { // This was observed to fail on aarch64 with LLVM 11, when the capacityInBytes // function used the @reduce code path. test "regression test for @reduce bug" { - const ally = std.testing.allocator; + const ally = testing.allocator; var list = MultiArrayList(struct { tag: std.zig.Token.Tag, start: u32, @@ -412,35 +418,70 @@ test "regression test for @reduce bug" { try list.append(ally, .{ .tag = .eof, .start = 123 }); const tags = list.items(.tag); - std.testing.expectEqual(tags[1], .identifier); - std.testing.expectEqual(tags[2], .equal); - std.testing.expectEqual(tags[3], .builtin); - std.testing.expectEqual(tags[4], .l_paren); - std.testing.expectEqual(tags[5], .string_literal); - std.testing.expectEqual(tags[6], .r_paren); - std.testing.expectEqual(tags[7], .semicolon); - std.testing.expectEqual(tags[8], .keyword_pub); - std.testing.expectEqual(tags[9], .keyword_fn); - std.testing.expectEqual(tags[10], .identifier); - std.testing.expectEqual(tags[11], .l_paren); - std.testing.expectEqual(tags[12], .r_paren); - std.testing.expectEqual(tags[13], .identifier); - std.testing.expectEqual(tags[14], .bang); - std.testing.expectEqual(tags[15], .identifier); - std.testing.expectEqual(tags[16], .l_brace); - std.testing.expectEqual(tags[17], .identifier); - std.testing.expectEqual(tags[18], .period); - std.testing.expectEqual(tags[19], .identifier); - std.testing.expectEqual(tags[20], .period); - std.testing.expectEqual(tags[21], .identifier); - std.testing.expectEqual(tags[22], .l_paren); - std.testing.expectEqual(tags[23], .string_literal); - std.testing.expectEqual(tags[24], .comma); - std.testing.expectEqual(tags[25], .period); - std.testing.expectEqual(tags[26], .l_brace); - std.testing.expectEqual(tags[27], .r_brace); - std.testing.expectEqual(tags[28], .r_paren); - std.testing.expectEqual(tags[29], .semicolon); - std.testing.expectEqual(tags[30], .r_brace); - std.testing.expectEqual(tags[31], .eof); + testing.expectEqual(tags[1], .identifier); + testing.expectEqual(tags[2], .equal); + testing.expectEqual(tags[3], .builtin); + testing.expectEqual(tags[4], .l_paren); + testing.expectEqual(tags[5], .string_literal); + testing.expectEqual(tags[6], .r_paren); + testing.expectEqual(tags[7], .semicolon); + testing.expectEqual(tags[8], .keyword_pub); + testing.expectEqual(tags[9], .keyword_fn); + testing.expectEqual(tags[10], .identifier); + testing.expectEqual(tags[11], .l_paren); + testing.expectEqual(tags[12], .r_paren); + testing.expectEqual(tags[13], .identifier); + testing.expectEqual(tags[14], .bang); + testing.expectEqual(tags[15], .identifier); + testing.expectEqual(tags[16], .l_brace); + testing.expectEqual(tags[17], .identifier); + testing.expectEqual(tags[18], .period); + testing.expectEqual(tags[19], .identifier); + testing.expectEqual(tags[20], .period); + testing.expectEqual(tags[21], .identifier); + testing.expectEqual(tags[22], .l_paren); + testing.expectEqual(tags[23], .string_literal); + testing.expectEqual(tags[24], .comma); + testing.expectEqual(tags[25], .period); + testing.expectEqual(tags[26], .l_brace); + testing.expectEqual(tags[27], .r_brace); + testing.expectEqual(tags[28], .r_paren); + testing.expectEqual(tags[29], .semicolon); + testing.expectEqual(tags[30], .r_brace); + testing.expectEqual(tags[31], .eof); +} + +test "ensure capacity on empty list" { + const ally = testing.allocator; + + const Foo = struct { + a: u32, + b: u8, + }; + + var list = MultiArrayList(Foo){}; + defer list.deinit(ally); + + try list.ensureCapacity(ally, 2); + list.appendAssumeCapacity(.{ .a = 1, .b = 2 }); + list.appendAssumeCapacity(.{ .a = 3, .b = 4 }); + + testing.expectEqualSlices(u32, &[_]u32{ 1, 3 }, list.items(.a)); + testing.expectEqualSlices(u8, &[_]u8{ 2, 4 }, list.items(.b)); + + list.len = 0; + list.appendAssumeCapacity(.{ .a = 5, .b = 6 }); + list.appendAssumeCapacity(.{ .a = 7, .b = 8 }); + + testing.expectEqualSlices(u32, &[_]u32{ 5, 7 }, list.items(.a)); + testing.expectEqualSlices(u8, &[_]u8{ 6, 8 }, list.items(.b)); + + list.len = 0; + try list.ensureCapacity(ally, 16); + + list.appendAssumeCapacity(.{ .a = 9, .b = 10 }); + list.appendAssumeCapacity(.{ .a = 11, .b = 12 }); + + testing.expectEqualSlices(u32, &[_]u32{ 9, 11 }, list.items(.a)); + testing.expectEqualSlices(u8, &[_]u8{ 10, 12 }, list.items(.b)); } diff --git a/lib/std/os.zig b/lib/std/os.zig index a2e62ed0ae..362a58f7fb 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -144,25 +144,27 @@ pub fn getrandom(buffer: []u8) GetRandomError!void { std.c.versionCheck(builtin.Version{ .major = 2, .minor = 25, .patch = 0 }).ok; while (buf.len != 0) { - var err: u16 = undefined; - - const num_read = if (use_c) blk: { + const res = if (use_c) blk: { const rc = std.c.getrandom(buf.ptr, buf.len, 0); - err = std.c.getErrno(rc); - break :blk @bitCast(usize, rc); + break :blk .{ + .num_read = @bitCast(usize, rc), + .err = std.c.getErrno(rc), + }; } else blk: { const rc = linux.getrandom(buf.ptr, buf.len, 0); - err = linux.getErrno(rc); - break :blk rc; + break :blk .{ + .num_read = rc, + .err = linux.getErrno(rc), + }; }; - switch (err) { - 0 => buf = buf[num_read..], + switch (res.err) { + 0 => buf = buf[res.num_read..], EINVAL => unreachable, EFAULT => unreachable, EINTR => continue, ENOSYS => return getRandomBytesDevURandom(buf), - else => return unexpectedErrno(err), + else => return unexpectedErrno(res.err), } } return; @@ -1500,7 +1502,7 @@ pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 { EINVAL => unreachable, ENOENT => return error.CurrentWorkingDirectoryUnlinked, ERANGE => return error.NameTooLong, - else => return unexpectedErrno(@intCast(usize, err)), + else => return unexpectedErrno(err), } } @@ -3661,7 +3663,7 @@ pub fn mmap( const err = if (builtin.link_libc) blk: { const rc = std.c.mmap(ptr, length, prot, flags, fd, offset); if (rc != std.c.MAP_FAILED) return @ptrCast([*]align(mem.page_size) u8, @alignCast(mem.page_size, rc))[0..length]; - break :blk @intCast(usize, system._errno().*); + break :blk system._errno().*; } else blk: { const rc = system.mmap(ptr, length, prot, flags, fd, offset); const err = errno(rc); @@ -4321,7 +4323,7 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP ENAMETOOLONG => return error.NameTooLong, ELOOP => return error.SymLinkLoop, EIO => return error.InputOutput, - else => |err| return unexpectedErrno(@intCast(usize, err)), + else => |err| return unexpectedErrno(err), }; return mem.spanZ(result_path); } @@ -4622,7 +4624,11 @@ pub const UnexpectedError = error{ /// Call this when you made a syscall or something that sets errno /// and you get an unexpected error. -pub fn unexpectedErrno(err: usize) UnexpectedError { +pub fn unexpectedErrno(err: anytype) UnexpectedError { + if (@typeInfo(@TypeOf(err)) != .Int) { + @compileError("err is expected to be an integer"); + } + if (unexpected_error_tracing) { std.debug.warn("unexpected errno: {d}\n", .{err}); std.debug.dumpCurrentStackTrace(null); diff --git a/lib/std/os/linux/io_uring.zig b/lib/std/os/linux/io_uring.zig index 340020cf9b..e900bdcd6a 100644 --- a/lib/std/os/linux/io_uring.zig +++ b/lib/std/os/linux/io_uring.zig @@ -163,9 +163,9 @@ pub const IO_Uring = struct { /// Returns the number of SQEs submitted. /// Matches the implementation of io_uring_submit_and_wait() in liburing. pub fn submit_and_wait(self: *IO_Uring, wait_nr: u32) !u32 { - var submitted = self.flush_sq(); + const submitted = self.flush_sq(); var flags: u32 = 0; - if (self.sq_ring_needs_enter(submitted, &flags) or wait_nr > 0) { + if (self.sq_ring_needs_enter(&flags) or wait_nr > 0) { if (wait_nr > 0 or (self.flags & linux.IORING_SETUP_IOPOLL) != 0) { flags |= linux.IORING_ENTER_GETEVENTS; } @@ -236,9 +236,9 @@ pub const IO_Uring = struct { /// or if IORING_SQ_NEED_WAKEUP is set and the SQ thread must be explicitly awakened. /// For the latter case, we set the SQ thread wakeup flag. /// Matches the implementation of sq_ring_needs_enter() in liburing. - pub fn sq_ring_needs_enter(self: *IO_Uring, submitted: u32, flags: *u32) bool { + pub fn sq_ring_needs_enter(self: *IO_Uring, flags: *u32) bool { assert(flags.* == 0); - if ((self.flags & linux.IORING_SETUP_SQPOLL) == 0 and submitted > 0) return true; + if ((self.flags & linux.IORING_SETUP_SQPOLL) == 0) return true; if ((@atomicLoad(u32, self.sq.flags, .Unordered) & linux.IORING_SQ_NEED_WAKEUP) != 0) { flags.* |= linux.IORING_ENTER_SQ_WAKEUP; return true; diff --git a/lib/std/special/init-exe/build.zig b/lib/std/special/init-exe/build.zig index 3a66254b02..714c97e008 100644 --- a/lib/std/special/init-exe/build.zig +++ b/lib/std/special/init-exe/build.zig @@ -1,6 +1,6 @@ -const Builder = @import("std").build.Builder; +const std = @import("std"); -pub fn build(b: *Builder) void { +pub fn build(b: *std.build.Builder) void { // Standard target options allows the person running `zig build` to choose // what target to build for. Here we do not override the defaults, which // means any target is allowed, and the default is native. Other options diff --git a/lib/std/special/init-lib/build.zig b/lib/std/special/init-lib/build.zig index 558e447c15..f7d261f152 100644 --- a/lib/std/special/init-lib/build.zig +++ b/lib/std/special/init-lib/build.zig @@ -1,7 +1,10 @@ -const Builder = @import("std").build.Builder; +const std = @import("std"); -pub fn build(b: *Builder) void { +pub fn build(b: *std.build.Builder) void { + // Standard release options allow the person running `zig build` to select + // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. const mode = b.standardReleaseOptions(); + const lib = b.addStaticLibrary("$", "src/main.zig"); lib.setBuildMode(mode); lib.install(); diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig index 46b58e9465..0b0459ec88 100644 --- a/lib/std/zig/ast.zig +++ b/lib/std/zig/ast.zig @@ -275,8 +275,10 @@ pub const Tree = struct { .extra_volatile_qualifier => { return stream.writeAll("extra volatile qualifier"); }, - .invalid_align => { - return stream.writeAll("alignment not allowed on arrays"); + .ptr_mod_on_array_child_type => { + return stream.print("pointer modifier '{s}' not allowed on array child type", .{ + token_tags[parse_error.token].symbol(), + }); }, .invalid_and => { return stream.writeAll("`&&` is invalid; note that `and` is boolean AND"); @@ -525,7 +527,9 @@ pub const Tree = struct { => { // Look for a label. const lbrace = main_tokens[n]; - if (token_tags[lbrace - 1] == .colon) { + if (token_tags[lbrace - 1] == .colon and + token_tags[lbrace - 2] == .identifier) + { end_offset += 2; } return lbrace - end_offset; @@ -766,7 +770,7 @@ pub const Tree = struct { .container_decl_arg => { const members = tree.extraData(datas[n].rhs, Node.SubRange); if (members.end - members.start == 0) { - end_offset += 1; // for the rparen + end_offset += 3; // for the rparen + lbrace + rbrace n = datas[n].lhs; } else { end_offset += 1; // for the rbrace @@ -989,13 +993,13 @@ pub const Tree = struct { }, .slice => { const extra = tree.extraData(datas[n].rhs, Node.Slice); - assert(extra.end != 0); // should have used SliceOpen + assert(extra.end != 0); // should have used slice_open end_offset += 1; // rbracket n = extra.end; }, .slice_sentinel => { const extra = tree.extraData(datas[n].rhs, Node.SliceSentinel); - assert(extra.sentinel != 0); // should have used Slice + assert(extra.sentinel != 0); // should have used slice end_offset += 1; // rbracket n = extra.sentinel; }, @@ -2386,7 +2390,7 @@ pub const Error = struct { extra_allowzero_qualifier, extra_const_qualifier, extra_volatile_qualifier, - invalid_align, + ptr_mod_on_array_child_type, invalid_and, invalid_bit_range, invalid_token, @@ -2925,6 +2929,7 @@ pub const Node = struct { pub const SliceSentinel = struct { start: Index, + /// May be 0 if the slice is "open" end: Index, sentinel: Index, }; diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index 9b755c2033..805ee95571 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -937,14 +937,17 @@ const Parser = struct { /// If a parse error occurs, reports an error, but then finds the next statement /// and returns that one instead. If a parse error occurs but there is no following /// statement, returns 0. - fn expectStatementRecoverable(p: *Parser) error{OutOfMemory}!Node.Index { + fn expectStatementRecoverable(p: *Parser) Error!Node.Index { while (true) { return p.expectStatement() catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.ParseError => { p.findNextStmt(); // Try to skip to the next statement. - if (p.token_tags[p.tok_i] == .r_brace) return null_node; - continue; + switch (p.token_tags[p.tok_i]) { + .r_brace => return null_node, + .eof => return error.ParseError, + else => continue, + } }, }; } @@ -1609,13 +1612,15 @@ const Parser = struct { /// PrefixTypeOp /// <- QUESTIONMARK /// / KEYWORD_anyframe MINUSRARROW - /// / ArrayTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* + /// / SliceTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* /// / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* + /// / ArrayTypeStart + /// SliceTypeStart <- LBRACKET (COLON Expr)? RBRACKET /// PtrTypeStart /// <- ASTERISK /// / ASTERISK2 /// / LBRACKET ASTERISK (LETTERC / COLON Expr)? RBRACKET - /// ArrayTypeStart <- LBRACKET Expr? (COLON Expr)? RBRACKET + /// ArrayTypeStart <- LBRACKET Expr (COLON Expr)? RBRACKET fn parseTypeExpr(p: *Parser) Error!Node.Index { switch (p.token_tags[p.tok_i]) { .question_mark => return p.addNode(.{ @@ -1782,15 +1787,15 @@ const Parser = struct { else 0; _ = try p.expectToken(.r_bracket); - const mods = try p.parsePtrModifiers(); - const elem_type = try p.expectTypeExpr(); - if (mods.bit_range_start != 0) { - try p.warnMsg(.{ - .tag = .invalid_bit_range, - .token = p.nodes.items(.main_token)[mods.bit_range_start], - }); - } if (len_expr == 0) { + const mods = try p.parsePtrModifiers(); + const elem_type = try p.expectTypeExpr(); + if (mods.bit_range_start != 0) { + try p.warnMsg(.{ + .tag = .invalid_bit_range, + .token = p.nodes.items(.main_token)[mods.bit_range_start], + }); + } if (sentinel == 0) { return p.addNode(.{ .tag = .ptr_type_aligned, @@ -1823,12 +1828,15 @@ const Parser = struct { }); } } else { - if (mods.align_node != 0) { - try p.warnMsg(.{ - .tag = .invalid_align, - .token = p.nodes.items(.main_token)[mods.align_node], - }); + switch (p.token_tags[p.tok_i]) { + .keyword_align, + .keyword_const, + .keyword_volatile, + .keyword_allowzero, + => return p.fail(.ptr_mod_on_array_child_type), + else => {}, } + const elem_type = try p.expectTypeExpr(); if (sentinel == 0) { return p.addNode(.{ .tag = .array_type, @@ -1978,7 +1986,7 @@ const Parser = struct { } }, .keyword_inline => { - p.tok_i += 2; + p.tok_i += 1; switch (p.token_tags[p.tok_i]) { .keyword_for => return p.parseForExpr(), .keyword_while => return p.parseWhileExpr(), @@ -3438,7 +3446,7 @@ const Parser = struct { } /// SuffixOp - /// <- LBRACKET Expr (DOT2 (Expr (COLON Expr)?)?)? RBRACKET + /// <- LBRACKET Expr (DOT2 (Expr? (COLON Expr)?)?)? RBRACKET /// / DOT IDENTIFIER /// / DOTASTERISK /// / DOTQUESTIONMARK @@ -3450,17 +3458,6 @@ const Parser = struct { if (p.eatToken(.ellipsis2)) |_| { const end_expr = try p.parseExpr(); - if (end_expr == 0) { - _ = try p.expectToken(.r_bracket); - return p.addNode(.{ - .tag = .slice_open, - .main_token = lbracket, - .data = .{ - .lhs = lhs, - .rhs = index_expr, - }, - }); - } if (p.eatToken(.colon)) |_| { const sentinel = try p.parseExpr(); _ = try p.expectToken(.r_bracket); @@ -3476,20 +3473,29 @@ const Parser = struct { }), }, }); - } else { - _ = try p.expectToken(.r_bracket); + } + _ = try p.expectToken(.r_bracket); + if (end_expr == 0) { return p.addNode(.{ - .tag = .slice, + .tag = .slice_open, .main_token = lbracket, .data = .{ .lhs = lhs, - .rhs = try p.addExtra(Node.Slice{ - .start = index_expr, - .end = end_expr, - }), + .rhs = index_expr, }, }); } + return p.addNode(.{ + .tag = .slice, + .main_token = lbracket, + .data = .{ + .lhs = lhs, + .rhs = try p.addExtra(Node.Slice{ + .start = index_expr, + .end = end_expr, + }), + }, + }); } _ = try p.expectToken(.r_bracket); return p.addNode(.{ diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index c083d23932..b6bd2844a4 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -852,6 +852,7 @@ test "zig fmt: slices" { try testCanonical( \\const a = b[0..]; \\const c = d[0..1]; + \\const d = f[0.. :0]; \\const e = f[0..1 :0]; \\ ); @@ -861,6 +862,7 @@ test "zig fmt: slices with spaces in bounds" { try testCanonical( \\const a = b[0 + 0 ..]; \\const c = d[0 + 0 .. 1]; + \\const c = d[0 + 0 .. :0]; \\const e = f[0 .. 1 + 1 :0]; \\ ); @@ -963,6 +965,27 @@ test "zig fmt: allowzero pointer" { ); } +test "zig fmt: empty enum decls" { + try testCanonical( + \\const A = enum {}; + \\const B = enum(u32) {}; + \\const C = extern enum(c_int) {}; + \\const D = packed enum(u8) {}; + \\ + ); +} + +test "zig fmt: empty union decls" { + try testCanonical( + \\const A = union {}; + \\const B = union(enum) {}; + \\const C = union(Foo) {}; + \\const D = extern union {}; + \\const E = packed union {}; + \\ + ); +} + test "zig fmt: enum literal" { try testCanonical( \\const x = .hi; @@ -4276,6 +4299,18 @@ test "zig fmt: respect extra newline between switch items" { ); } +test "zig fmt: assignment with inline for and inline while" { + try testCanonical( + \\const tmp = inline for (items) |item| {}; + \\ + ); + + try testCanonical( + \\const tmp2 = inline while (true) {}; + \\ + ); +} + test "zig fmt: insert trailing comma if there are comments between switch values" { try testTransform( \\const a = switch (b) { @@ -4315,11 +4350,17 @@ test "zig fmt: error for invalid bit range" { }); } -test "zig fmt: error for invalid align" { +test "zig fmt: error for ptr mod on array child type" { try testError( - \\var x: [10]align(10)u8 = bar; + \\var a: [10]align(10) u8 = e; + \\var b: [10]const u8 = f; + \\var c: [10]volatile u8 = g; + \\var d: [10]allowzero u8 = h; , &[_]Error{ - .invalid_align, + .ptr_mod_on_array_child_type, + .ptr_mod_on_array_child_type, + .ptr_mod_on_array_child_type, + .ptr_mod_on_array_child_type, }); } @@ -4580,6 +4621,16 @@ test "recovery: missing comma in params" { }); } +test "recovery: missing while rbrace" { + try testError( + \\fn a() b { + \\ while (d) { + \\} + , &[_]Error{ + .expected_statement, + }); +} + const std = @import("std"); const mem = std.mem; const warn = std.debug.warn; diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index 069b62af79..30e83e9a7c 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -470,9 +470,9 @@ fn renderExpression(gpa: *Allocator, ais: *Ais, tree: ast.Tree, node: ast.Node.I return renderToken(ais, tree, rbracket, space); // ] }, - .slice_open => return renderSlice(gpa, ais, tree, tree.sliceOpen(node), space), - .slice => return renderSlice(gpa, ais, tree, tree.slice(node), space), - .slice_sentinel => return renderSlice(gpa, ais, tree, tree.sliceSentinel(node), space), + .slice_open => return renderSlice(gpa, ais, tree, node, tree.sliceOpen(node), space), + .slice => return renderSlice(gpa, ais, tree, node, tree.slice(node), space), + .slice_sentinel => return renderSlice(gpa, ais, tree, node, tree.sliceSentinel(node), space), .deref => { try renderExpression(gpa, ais, tree, datas[node].lhs, .none); @@ -815,6 +815,7 @@ fn renderSlice( gpa: *Allocator, ais: *Ais, tree: ast.Tree, + slice_node: ast.Node.Index, slice: ast.full.Slice, space: Space, ) Error!void { @@ -822,7 +823,9 @@ fn renderSlice( const after_start_space_bool = nodeCausesSliceOpSpace(node_tags[slice.ast.start]) or if (slice.ast.end != 0) nodeCausesSliceOpSpace(node_tags[slice.ast.end]) else false; const after_start_space = if (after_start_space_bool) Space.space else Space.none; - const after_dots_space = if (slice.ast.end != 0) after_start_space else Space.none; + const after_dots_space = if (slice.ast.end != 0) + after_start_space + else if (slice.ast.sentinel != 0) Space.space else Space.none; try renderExpression(gpa, ais, tree, slice.ast.sliced, .none); try renderToken(ais, tree, slice.ast.lbracket, .none); // lbracket @@ -830,20 +833,18 @@ fn renderSlice( const start_last = tree.lastToken(slice.ast.start); try renderExpression(gpa, ais, tree, slice.ast.start, after_start_space); try renderToken(ais, tree, start_last + 1, after_dots_space); // ellipsis2 ("..") - if (slice.ast.end == 0) { - return renderToken(ais, tree, start_last + 2, space); // rbracket + + if (slice.ast.end != 0) { + const after_end_space = if (slice.ast.sentinel != 0) Space.space else Space.none; + try renderExpression(gpa, ais, tree, slice.ast.end, after_end_space); } - const end_last = tree.lastToken(slice.ast.end); - const after_end_space = if (slice.ast.sentinel != 0) Space.space else Space.none; - try renderExpression(gpa, ais, tree, slice.ast.end, after_end_space); - if (slice.ast.sentinel == 0) { - return renderToken(ais, tree, end_last + 1, space); // rbracket + if (slice.ast.sentinel != 0) { + try renderToken(ais, tree, tree.firstToken(slice.ast.sentinel) - 1, .none); // colon + try renderExpression(gpa, ais, tree, slice.ast.sentinel, .none); } - try renderToken(ais, tree, end_last + 1, .none); // colon - try renderExpression(gpa, ais, tree, slice.ast.sentinel, .none); - try renderToken(ais, tree, tree.lastToken(slice.ast.sentinel) + 1, space); // rbracket + try renderToken(ais, tree, tree.lastToken(slice_node), space); // rbracket } fn renderAsmOutput( |
