From 7e2a26c0c441a902968726442114e3590820433b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 6 Jul 2025 15:51:44 -0700 Subject: [PATCH] std.io.Writer.printValue: rework logic Alignment and fill options only apply to numbers. Rework the implementation to mainly branch on the format string rather than the type information. This is more straightforward to maintain and more straightforward for comptime evaluation. Enums support being printed as decimal, hexadecimal, octal, and binary. `formatInteger` is another possible format method that is unconditionally called when the value type is struct and one of the integer-printing format specifiers are used. --- lib/compiler/aro/aro/Diagnostics.zig | 4 +- lib/compiler/aro/aro/Preprocessor.zig | 2 +- lib/compiler/aro/aro/Value.zig | 3 +- lib/std/Build/Step/CheckObject.zig | 11 +- lib/std/Target.zig | 25 +- lib/std/Uri.zig | 14 + lib/std/fmt.zig | 102 +++--- lib/std/io/Writer.zig | 491 +++++++++++++++----------- lib/std/json/dynamic_test.zig | 4 +- lib/std/json/stringify.zig | 1 - lib/std/json/stringify_test.zig | 12 +- lib/std/math/big/int.zig | 32 +- lib/std/math/big/int_test.zig | 8 +- lib/std/zig.zig | 4 +- lib/std/zig/llvm/Builder.zig | 230 ++++++++---- lib/std/zon/stringify.zig | 2 +- src/Builtin.zig | 4 +- src/Package/Fetch.zig | 9 +- src/Package/Fetch/git.zig | 32 +- src/Sema/LowerZon.zig | 2 +- src/arch/riscv64/CodeGen.zig | 4 +- src/arch/riscv64/bits.zig | 6 + src/arch/x86_64/CodeGen.zig | 54 +-- src/arch/x86_64/bits.zig | 6 + src/arch/x86_64/encoder.zig | 2 +- src/link.zig | 14 +- src/link/C.zig | 4 +- src/main.zig | 4 +- src/print_value.zig | 2 +- 29 files changed, 636 insertions(+), 452 deletions(-) diff --git a/lib/compiler/aro/aro/Diagnostics.zig b/lib/compiler/aro/aro/Diagnostics.zig index 5e92d555cdcf68385b7e6f5184097107f1269eaa..dbf6e29af51311c782af925165925d4cd1b91315 100644 --- a/lib/compiler/aro/aro/Diagnostics.zig +++ b/lib/compiler/aro/aro/Diagnostics.zig @@ -461,10 +461,10 @@ pub fn renderMessage(comp: *Compilation, m: anytype, msg: Message) void { try writer.writeByte(@intCast(codepoint)); } else if (codepoint < 0xFFFF) { try writer.writeAll("\\u"); - try writer.printIntOptions(codepoint, 16, .upper, .{ .fill = '0', .width = 4 }); + try writer.printInt(codepoint, 16, .upper, .{ .fill = '0', .width = 4 }); } else { try writer.writeAll("\\U"); - try writer.printIntOptions(codepoint, 16, .upper, .{ .fill = '0', .width = 8 }); + try writer.printInt(codepoint, 16, .upper, .{ .fill = '0', .width = 8 }); } } } diff --git a/lib/compiler/aro/aro/Preprocessor.zig b/lib/compiler/aro/aro/Preprocessor.zig index d71bf4f85737ddf12096f568ead57bfe3f037125..c8695edd6baa03aef675b64f86e1d59f983c018d 100644 --- a/lib/compiler/aro/aro/Preprocessor.zig +++ b/lib/compiler/aro/aro/Preprocessor.zig @@ -3262,7 +3262,7 @@ fn printLinemarker( // containing the same bytes as the input regardless of encoding. else => { try w.writeAll("\\x"); - // TODO try w.printIntOptions(byte, 16, .lower, .{ .width = 2, .fill = '0' }); + // TODO try w.printInt(byte, 16, .lower, .{ .width = 2, .fill = '0' }); try w.print("{x:0>2}", .{byte}); }, }; diff --git a/lib/compiler/aro/aro/Value.zig b/lib/compiler/aro/aro/Value.zig index 670068203ee87e97d01effdfe9cbb10443c315c9..183c55797607fa5e81a5e7e40d876deac8bd5b30 100644 --- a/lib/compiler/aro/aro/Value.zig +++ b/lib/compiler/aro/aro/Value.zig @@ -961,8 +961,7 @@ pub fn print(v: Value, ty: Type, comp: *const Compilation, w: anytype) @TypeOf(w switch (key) { .null => return w.writeAll("nullptr_t"), .int => |repr| switch (repr) { - inline .u64, .i64 => |x| return w.print("{d}", .{x}), - .big_int => |x| return w.print("{fd}", .{x}), + inline .u64, .i64, .big_int => |x| return w.print("{d}", .{x}), }, .float => |repr| switch (repr) { .f16 => |x| return w.print("{d}", .{@round(@as(f64, @floatCast(x)) * 1000) / 1000}), diff --git a/lib/std/Build/Step/CheckObject.zig b/lib/std/Build/Step/CheckObject.zig index ba51632c675540465b2f3ba09c38687a1a6b1d53..870170a02e24d1b5a9ca6751b1620a7c557bdf19 100644 --- a/lib/std/Build/Step/CheckObject.zig +++ b/lib/std/Build/Step/CheckObject.zig @@ -230,12 +230,7 @@ const ComputeCompareExpected = struct { literal: u64, }, - pub fn format( - value: ComputeCompareExpected, - bw: *Writer, - comptime fmt: []const u8, - ) !void { - if (fmt.len != 0) std.fmt.invalidFmtError(fmt, value); + pub fn format(value: ComputeCompareExpected, bw: *Writer) Writer.Error!void { try bw.print("{s} ", .{@tagName(value.op)}); switch (value.value) { .variable => |name| try bw.writeAll(name), @@ -571,7 +566,9 @@ fn make(step: *Step, make_options: Step.MakeOptions) !void { null, .of(u64), null, - ) catch |err| return step.fail("unable to read '{f'}': {s}", .{ src_path, @errorName(err) }); + ) catch |err| return step.fail("unable to read '{f}': {s}", .{ + std.fmt.alt(src_path, .formatEscapeChar), @errorName(err), + }); var vars: std.StringHashMap(u64) = .init(gpa); for (check_object.checks.items) |chk| { diff --git a/lib/std/Target.zig b/lib/std/Target.zig index 91eed5fee9d84f1e5338f12a4dbe014b7470e669..91deb9ddc1342da1288f0f292db03b2ead93f810 100644 --- a/lib/std/Target.zig +++ b/lib/std/Target.zig @@ -301,24 +301,13 @@ pub const Os = struct { /// This function is defined to serialize a Zig source code representation of this /// type, that, when parsed, will deserialize into the same data. - pub fn format(ver: WindowsVersion, w: *std.io.Writer, comptime f: []const u8) std.io.Writer.Error!void { - const maybe_name = std.enums.tagName(WindowsVersion, ver); - if (comptime std.mem.eql(u8, f, "s")) { - if (maybe_name) |name| - try w.print(".{s}", .{name}) - else - try w.print(".{d}", .{@intFromEnum(ver)}); - } else if (comptime std.mem.eql(u8, f, "c")) { - if (maybe_name) |name| - try w.print(".{s}", .{name}) - else - try w.print("@enumFromInt(0x{X:0>8})", .{@intFromEnum(ver)}); - } else if (f.len == 0) { - if (maybe_name) |name| - try w.print("WindowsVersion.{s}", .{name}) - else - try w.print("WindowsVersion(0x{X:0>8})", .{@intFromEnum(ver)}); - } else std.fmt.invalidFmtError(f, ver); + pub fn format(wv: WindowsVersion, w: *std.io.Writer) std.io.Writer.Error!void { + if (std.enums.tagName(WindowsVersion, wv)) |name| { + var vecs: [2][]const u8 = .{ ".", name }; + return w.writeVecAll(&vecs); + } else { + return w.print("@enumFromInt(0x{X:0>8})", .{wv}); + } } }; diff --git a/lib/std/Uri.zig b/lib/std/Uri.zig index 611d6581c3fad719a0abe18044ae0c0b88a5d232..19af1512c2a83282e802465c6fb24c15bb9470a5 100644 --- a/lib/std/Uri.zig +++ b/lib/std/Uri.zig @@ -240,6 +240,10 @@ pub fn parseAfterScheme(scheme: []const u8, text: []const u8) ParseError!Uri { return uri; } +pub fn format(uri: *const Uri, writer: *std.io.Writer) std.io.Writer.Error!void { + return writeToStream(uri, writer, .all); +} + pub fn writeToStream(uri: *const Uri, writer: *std.io.Writer, flags: Format.Flags) std.io.Writer.Error!void { if (flags.scheme) { try writer.print("{s}:", .{uri.scheme}); @@ -302,6 +306,16 @@ pub const Format = struct { fragment: bool = false, /// When true, include the port part of the URI. Ignored when `port` is null. port: bool = true, + + pub const all: Flags = .{ + .scheme = true, + .authentication = true, + .authority = true, + .path = true, + .query = true, + .fragment = true, + .port = true, + }; }; pub fn default(f: Format, writer: *std.io.Writer) std.io.Writer.Error!void { diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index b5938fcd8f4eeeaf597e9abada459edff361efda..8588c8213957485209a96cbeb0b9ff8fb33cfd23 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -53,16 +53,20 @@ pub const Options = struct { /// - when using a field name, you are required to enclose the field name (an identifier) in square /// brackets, e.g. {[score]...} as opposed to the numeric index form which can be written e.g. {2...} /// - *specifier* is a type-dependent formatting option that determines how a type should formatted (see below) -/// - *fill* is a single byte which is used to pad the formatted text -/// - *alignment* is one of the three bytes '<', '^', or '>' to make the text left-, center-, or right-aligned, respectively -/// - *width* is the total width of the field in bytes. This is generally only -/// useful for ASCII text, such as numbers. -/// - *precision* specifies how many decimals a formatted number should have +/// - *fill* is a single byte which is used to pad formatted numbers. +/// - *alignment* is one of the three bytes '<', '^', or '>' to make numbers +/// left, center, or right-aligned, respectively. +/// - *width* is the total width of the field in bytes. This only applies to number formatting. +/// - *precision* specifies how many decimals a formatted number should have. /// -/// Note that most of the parameters are optional and may be omitted. Also you can leave out separators like `:` and `.` when -/// all parameters after the separator are omitted. -/// Only exception is the *fill* parameter. If a non-zero *fill* character is required at the same time as *width* is specified, -/// one has to specify *alignment* as well, as otherwise the digit following `:` is interpreted as *width*, not *fill*. +/// Note that most of the parameters are optional and may be omitted. Also you +/// can leave out separators like `:` and `.` when all parameters after the +/// separator are omitted. +/// +/// Only exception is the *fill* parameter. If a non-zero *fill* character is +/// required at the same time as *width* is specified, one has to specify +/// *alignment* as well, as otherwise the digit following `:` is interpreted as +/// *width*, not *fill*. /// /// The *specifier* has several options for types: /// - `x` and `X`: output numeric value in hexadecimal notation, or string in hexadecimal bytes @@ -405,9 +409,9 @@ pub const ArgState = struct { /// Asserts the rendered integer value fits in `buffer`. /// Returns the end index within `buffer`. pub fn printInt(buffer: []u8, value: anytype, base: u8, case: Case, options: Options) usize { - var bw: Writer = .fixed(buffer); - bw.printIntOptions(value, base, case, options) catch unreachable; - return bw.end; + var w: Writer = .fixed(buffer); + w.printInt(value, base, case, options) catch unreachable; + return w.end; } /// Converts values in the range [0, 100) to a base 10 string. @@ -956,35 +960,23 @@ fn expectArrayFmt(expected: []const u8, comptime template: []const u8, comptime } test "array" { - { - const value: [3]u8 = "abc".*; - try expectArrayFmt("array: abc\n", "array: {s}\n", value); - try expectArrayFmt("array: { 97, 98, 99 }\n", "array: {d}\n", value); - try expectArrayFmt("array: 616263\n", "array: {x}\n", value); - try expectArrayFmt("array: { 97, 98, 99 }\n", "array: {any}\n", value); + const value: [3]u8 = "abc".*; + try expectArrayFmt("array: abc\n", "array: {s}\n", value); + try expectArrayFmt("array: 616263\n", "array: {x}\n", value); + try expectArrayFmt("array: { 97, 98, 99 }\n", "array: {any}\n", value); - var buf: [100]u8 = undefined; - try expectFmt( - try bufPrint(buf[0..], "array: [3]u8@{x}\n", .{@intFromPtr(&value)}), - "array: {*}\n", - .{&value}, - ); - } - - { - const value = [2][3]u8{ "abc".*, "def".* }; - - try expectArrayFmt("array: { abc, def }\n", "array: {s}\n", value); - try expectArrayFmt("array: { { 97, 98, 99 }, { 100, 101, 102 } }\n", "array: {d}\n", value); - try expectArrayFmt("array: { 616263, 646566 }\n", "array: {x}\n", value); - } + var buf: [100]u8 = undefined; + try expectFmt( + try bufPrint(buf[0..], "array: [3]u8@{x}\n", .{@intFromPtr(&value)}), + "array: {*}\n", + .{&value}, + ); } test "slice" { { const value: []const u8 = "abc"; try expectFmt("slice: abc\n", "slice: {s}\n", .{value}); - try expectFmt("slice: { 97, 98, 99 }\n", "slice: {d}\n", .{value}); try expectFmt("slice: 616263\n", "slice: {x}\n", .{value}); try expectFmt("slice: { 97, 98, 99 }\n", "slice: {any}\n", .{value}); } @@ -999,17 +991,12 @@ test "slice" { try expectFmt("buf: \x00hello\x00\n", "buf: {s}\n", .{null_term_slice}); } - try expectFmt("buf: Test\n", "buf: {s:5}\n", .{"Test"}); try expectFmt("buf: Test\n Other text", "buf: {s}\n Other text", .{"Test"}); { var int_slice = [_]u32{ 1, 4096, 391891, 1111111111 }; - var runtime_zero: usize = 0; - _ = &runtime_zero; - try expectFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {any}", .{int_slice[runtime_zero..]}); - try expectFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {d}", .{int_slice[runtime_zero..]}); - try expectFmt("int: { 1, 1000, 5fad3, 423a35c7 }", "int: {x}", .{int_slice[runtime_zero..]}); - try expectFmt("int: { 00001, 01000, 5fad3, 423a35c7 }", "int: {x:0>5}", .{int_slice[runtime_zero..]}); + const input: []const u32 = &int_slice; + try expectFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {any}", .{input}); } { const S1 = struct { @@ -1054,11 +1041,6 @@ test "cstr" { "cstr: {s}\n", .{@as([*c]const u8, @ptrCast("Test C"))}, ); - try expectFmt( - "cstr: Test C\n", - "cstr: {s:10}\n", - .{@as([*c]const u8, @ptrCast("Test C"))}, - ); } test "struct" { @@ -1428,16 +1410,12 @@ test "enum-literal" { test "padding" { try expectFmt("Simple", "{s}", .{"Simple"}); - try expectFmt(" true", "{:10}", .{true}); - try expectFmt(" true", "{:>10}", .{true}); - try expectFmt("======true", "{:=>10}", .{true}); - try expectFmt("true======", "{:=<10}", .{true}); - try expectFmt(" true ", "{:^10}", .{true}); - try expectFmt("===true===", "{:=^10}", .{true}); - try expectFmt(" Minimum width", "{s:18} width", .{"Minimum"}); - try expectFmt("==================Filled", "{s:=>24}", .{"Filled"}); - try expectFmt(" Centered ", "{s:^24}", .{"Centered"}); - try expectFmt("-", "{s:-^1}", .{""}); + try expectFmt(" 1234", "{:10}", .{1234}); + try expectFmt(" 1234", "{:>10}", .{1234}); + try expectFmt("======1234", "{:=>10}", .{1234}); + try expectFmt("1234======", "{:=<10}", .{1234}); + try expectFmt(" 1234 ", "{:^10}", .{1234}); + try expectFmt("===1234===", "{:=^10}", .{1234}); try expectFmt("====a", "{c:=>5}", .{'a'}); try expectFmt("==a==", "{c:=^5}", .{'a'}); try expectFmt("a====", "{c:=<5}", .{'a'}); @@ -1485,17 +1463,17 @@ test "named arguments" { test "runtime width specifier" { const width: usize = 9; - try expectFmt("~~hello~~", "{s:~^[1]}", .{ "hello", width }); - try expectFmt("~~hello~~", "{s:~^[width]}", .{ .string = "hello", .width = width }); - try expectFmt(" hello", "{s:[1]}", .{ "hello", width }); - try expectFmt("42 hello", "{d} {s:[2]}", .{ 42, "hello", width }); + try expectFmt("~~12345~~", "{d:~^[1]}", .{ 12345, width }); + try expectFmt("~~12345~~", "{d:~^[width]}", .{ .string = 12345, .width = width }); + try expectFmt(" 12345", "{d:[1]}", .{ 12345, width }); + try expectFmt("42 12345", "{d} {d:[2]}", .{ 42, 12345, width }); } test "runtime precision specifier" { const number: f32 = 3.1415; const precision: usize = 2; - try expectFmt("3.14e0", "{:1.[1]}", .{ number, precision }); - try expectFmt("3.14e0", "{:1.[precision]}", .{ .number = number, .precision = precision }); + try expectFmt("3.14e0", "{e:1.[1]}", .{ number, precision }); + try expectFmt("3.14e0", "{e:1.[precision]}", .{ .number = number, .precision = precision }); } test "recursive format function" { diff --git a/lib/std/io/Writer.zig b/lib/std/io/Writer.zig index 29376db2e58ecdeb5b5f1dfae7191edba2a4bf94..a5cb0aded11816cab405aa4987e02e521f0c8752 100644 --- a/lib/std/io/Writer.zig +++ b/lib/std/io/Writer.zig @@ -777,15 +777,15 @@ pub fn printAddress(w: *Writer, value: anytype) Error!void { .pointer => |info| { try w.writeAll(@typeName(info.child) ++ "@"); if (info.size == .slice) - try w.printIntOptions(@intFromPtr(value.ptr), 16, .lower, .{}) + try w.printInt(@intFromPtr(value.ptr), 16, .lower, .{}) else - try w.printIntOptions(@intFromPtr(value), 16, .lower, .{}); + try w.printInt(@intFromPtr(value), 16, .lower, .{}); return; }, .optional => |info| { if (@typeInfo(info.child) == .pointer) { try w.writeAll(@typeName(info.child) ++ "@"); - try w.printIntOptions(@intFromPtr(value), 16, .lower, .{}); + try w.printInt(@intFromPtr(value), 16, .lower, .{}); return; } }, @@ -804,11 +804,147 @@ pub fn printValue( ) Error!void { const T = @TypeOf(value); - if (fmt.len == 1) switch (fmt[0]) { - '*' => return w.printAddress(value), - 'f' => return value.format(w), + switch (fmt.len) { + 1 => switch (fmt[0]) { + '*' => return w.printAddress(value), + 'f' => return value.format(w), + 'd' => switch (@typeInfo(T)) { + .float, .comptime_float => return printFloat(w, value, .decimal, options), + .int, .comptime_int => return printInt(w, value, 10, .lower, options), + .@"struct" => return value.formatInteger(w, 10, .lower), + .@"enum" => return printInt(w, @intFromEnum(value), 10, .lower, options), + .vector => return printVector(w, fmt, options, value, max_depth), + else => invalidFmtError(fmt, value), + }, + 'c' => return w.printAsciiChar(value, options), + 'u' => return w.printUnicodeCodepoint(value), + 'b' => switch (@typeInfo(T)) { + .int, .comptime_int => return printInt(w, value, 2, .lower, options), + .@"enum" => return printInt(w, @intFromEnum(value), 2, .lower, options), + .@"struct" => return value.formatInteger(w, 2, .lower), + .vector => return printVector(w, fmt, options, value, max_depth), + else => invalidFmtError(fmt, value), + }, + 'o' => switch (@typeInfo(T)) { + .int, .comptime_int => return printInt(w, value, 8, .lower, options), + .@"enum" => return printInt(w, @intFromEnum(value), 8, .lower, options), + .@"struct" => return value.formatInteger(w, 8, .lower), + .vector => return printVector(w, fmt, options, value, max_depth), + else => invalidFmtError(fmt, value), + }, + 'x' => switch (@typeInfo(T)) { + .float, .comptime_float => return printFloatHexOptions(w, value, .lower, options), + .int, .comptime_int => return printInt(w, value, 16, .lower, options), + .@"enum" => return printInt(w, @intFromEnum(value), 16, .lower, options), + .@"struct" => return value.formatInteger(w, 16, .lower), + .pointer => |info| switch (info.size) { + .one, .slice => { + const slice: []const u8 = value; + return printHex(w, slice, .lower); + }, + .many, .c => { + const slice: [:0]const u8 = std.mem.span(value); + return printHex(w, slice, .lower); + }, + }, + .array => { + const slice: []const u8 = &value; + return printHex(w, slice, .lower); + }, + .vector => return printVector(w, fmt, options, value, max_depth), + else => invalidFmtError(fmt, value), + }, + 'X' => switch (@typeInfo(T)) { + .float, .comptime_float => return printFloatHexOptions(w, value, .lower, options), + .int, .comptime_int => return printInt(w, value, 16, .upper, options), + .@"enum" => return printInt(w, @intFromEnum(value), 16, .upper, options), + .@"struct" => return value.formatInteger(w, 16, .upper), + .pointer => |info| switch (info.size) { + .one, .slice => { + const slice: []const u8 = value; + return printHex(w, slice, .upper); + }, + .many, .c => { + const slice: [:0]const u8 = std.mem.span(value); + return printHex(w, slice, .upper); + }, + }, + .array => { + const slice: []const u8 = &value; + return printHex(w, slice, .upper); + }, + .vector => return printVector(w, fmt, options, value, max_depth), + else => invalidFmtError(fmt, value), + }, + 's' => switch (@typeInfo(T)) { + .pointer => |info| switch (info.size) { + .one, .slice => { + const slice: []const u8 = value; + return w.writeAll(slice); + }, + .many, .c => { + const slice: [:0]const u8 = std.mem.span(value); + return w.writeAll(slice); + }, + }, + .array => { + const slice: []const u8 = &value; + return w.writeAll(slice); + }, + else => invalidFmtError(fmt, value), + }, + 'B' => switch (@typeInfo(T)) { + .int, .comptime_int => return w.printByteSize(value, .decimal, options), + .@"struct" => return value.formatByteSize(w, .decimal), + else => invalidFmtError(fmt, value), + }, + 'D' => switch (@typeInfo(T)) { + .int, .comptime_int => return w.printDuration(value, options), + .@"struct" => return value.formatDuration(w), + else => invalidFmtError(fmt, value), + }, + 'e' => switch (@typeInfo(T)) { + .float, .comptime_float => return printFloat(w, value, .scientific, options), + .@"struct" => return value.formatFloat(w, .scientific), + else => invalidFmtError(fmt, value), + }, + 't' => switch (@typeInfo(T)) { + .error_set => return w.writeAll(@errorName(value)), + .@"enum", .@"union" => return w.writeAll(@tagName(value)), + else => invalidFmtError(fmt, value), + }, + else => {}, + }, + 2 => switch (fmt[0]) { + 'B' => switch (fmt[1]) { + 'i' => switch (@typeInfo(T)) { + .int, .comptime_int => return w.printByteSize(value, .binary, options), + .@"struct" => return value.formatByteSize(w, .binary), + else => invalidFmtError(fmt, value), + }, + else => {}, + }, + else => {}, + }, + 3 => if (fmt[0] == 'b' and fmt[1] == '6' and fmt[2] == '4') switch (@typeInfo(T)) { + .pointer => |info| switch (info.size) { + .one, .slice => { + const slice: []const u8 = value; + return w.printBase64(slice); + }, + .many, .c => { + const slice: [:0]const u8 = std.mem.span(value); + return w.printBase64(slice); + }, + }, + .array => { + const slice: []const u8 = &value; + return w.printBase64(slice); + }, + else => invalidFmtError(fmt, value), + }, else => {}, - }; + } const is_any = comptime std.mem.eql(u8, fmt, ANY); if (!is_any and std.meta.hasMethod(T, "format") and fmt.len == 0) { @@ -817,15 +953,21 @@ pub fn printValue( } switch (@typeInfo(T)) { - .float, .comptime_float => return w.printFloat(if (is_any) "d" else fmt, options, value), - .int, .comptime_int => return w.printInt(if (is_any) "d" else fmt, options, value), + .float, .comptime_float => { + if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); + return printFloat(w, value, .decimal, options); + }, + .int, .comptime_int => { + if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); + return printInt(w, value, 10, .lower, options); + }, .bool => { if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); - return w.alignBufferOptions(if (value) "true" else "false", options); + return w.writeAll(if (value) "true" else "false"); }, .void => { if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); - return w.alignBufferOptions("void", options); + return w.writeAll("void"); }, .optional => { const remaining_fmt = comptime if (fmt.len > 0 and fmt[0] == '?') @@ -854,40 +996,18 @@ pub fn printValue( } }, .error_set => { - if (fmt.len == 1 and fmt[0] == 't') return w.writeAll(@errorName(value)); if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); - try printErrorSet(w, value); + return printErrorSet(w, value); }, - .@"enum" => { - if (fmt.len == 1 and fmt[0] == 't') { - try w.writeAll(@tagName(value)); - return; + .@"enum" => |info| { + if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); + if (info.is_exhaustive) { + return printEnumExhaustive(w, value); + } else { + return printEnumNonexhaustive(w, value); } - if (!is_any) { - if (fmt.len != 0) return printValue(w, fmt, options, @intFromEnum(value), max_depth); - return printValue(w, ANY, options, value, max_depth); - } - const enum_info = @typeInfo(T).@"enum"; - if (enum_info.is_exhaustive) { - var vecs: [2][]const u8 = .{ ".", @tagName(value) }; - try w.writeVecAll(&vecs); - return; - } - if (std.enums.tagName(T, value)) |tag_name| { - var vecs: [2][]const u8 = .{ ".", tag_name }; - try w.writeVecAll(&vecs); - return; - } - try w.writeAll("@enumFromInt("); - try w.printValue(ANY, options, @intFromEnum(value), max_depth); - try w.writeByte(')'); - return; }, .@"union" => |info| { - if (fmt.len == 1 and fmt[0] == 't') { - try w.writeAll(@tagName(value)); - return; - } if (!is_any) { if (fmt.len != 0) invalidFmtError(fmt, value); return printValue(w, ANY, options, value, max_depth); @@ -971,38 +1091,18 @@ pub fn printValue( else => { var buffers: [2][]const u8 = .{ @typeName(ptr_info.child), "@" }; try w.writeVecAll(&buffers); - try w.printIntOptions(@intFromPtr(value), 16, .lower, options); + try w.printInt(@intFromPtr(value), 16, .lower, options); return; }, }, .many, .c => { - if (ptr_info.sentinel() != null) - return w.printValue(fmt, options, std.mem.span(value), max_depth); - if (fmt.len == 1 and fmt[0] == 's' and ptr_info.child == u8) - return w.alignBufferOptions(std.mem.span(value), options); - if (!is_any and fmt.len == 0) - @compileError("cannot format pointer without a specifier (i.e. {s} or {*})"); - if (!is_any and fmt.len != 0) - invalidFmtError(fmt, value); + if (!is_any) @compileError("cannot format pointer without a specifier (i.e. {s} or {*})"); try w.printAddress(value); }, .slice => { - if (!is_any and fmt.len == 0) + if (!is_any) @compileError("cannot format slice without a specifier (i.e. {s}, {x}, {b64}, or {any})"); - if (max_depth == 0) - return w.writeAll("{ ... }"); - if (ptr_info.child == u8) switch (fmt.len) { - 1 => switch (fmt[0]) { - 's' => return w.alignBufferOptions(value, options), - 'x' => return w.printHex(value, .lower), - 'X' => return w.printHex(value, .upper), - else => {}, - }, - 3 => if (fmt[0] == 'b' and fmt[1] == '6' and fmt[2] == '4') { - return w.printBase64(value); - }, - else => {}, - }; + if (max_depth == 0) return w.writeAll("{ ... }"); try w.writeAll("{ "); for (value, 0..) |elem, i| { try w.printValue(fmt, options, elem, max_depth - 1); @@ -1013,21 +1113,9 @@ pub fn printValue( try w.writeAll(" }"); }, }, - .array => |info| { - if (fmt.len == 0) - @compileError("cannot format array without a specifier (i.e. {s} or {any})"); - if (max_depth == 0) { - return w.writeAll("{ ... }"); - } - if (info.child == u8) { - if (fmt[0] == 's') { - return w.alignBufferOptions(&value, options); - } else if (fmt[0] == 'x') { - return w.printHex(&value, .lower); - } else if (fmt[0] == 'X') { - return w.printHex(&value, .upper); - } - } + .array => { + if (!is_any) @compileError("cannot format array without a specifier (i.e. {s} or {any})"); + if (max_depth == 0) return w.writeAll("{ ... }"); try w.writeAll("{ "); for (value, 0..) |elem, i| { try w.printValue(fmt, options, elem, max_depth - 1); @@ -1037,33 +1125,23 @@ pub fn printValue( } try w.writeAll(" }"); }, - .vector => |info| { - if (max_depth == 0) { - return w.writeAll("{ ... }"); - } - try w.writeAll("{ "); - var i: usize = 0; - while (i < info.len) : (i += 1) { - try w.printValue(fmt, options, value[i], max_depth - 1); - if (i < info.len - 1) { - try w.writeAll(", "); - } - } - try w.writeAll(" }"); + .vector => { + if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); + return printVector(w, fmt, options, value, max_depth); }, .@"fn" => @compileError("unable to format function body type, use '*const " ++ @typeName(T) ++ "' for a function pointer type"), .type => { if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); - return w.alignBufferOptions(@typeName(value), options); + return w.writeAll(@typeName(value)); }, .enum_literal => { if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); - const buffer = [_]u8{'.'} ++ @tagName(value); - return w.alignBufferOptions(buffer, options); + var vecs: [2][]const u8 = .{ ".", @tagName(value) }; + return w.writeVecAll(&vecs); }, .null => { if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); - return w.alignBufferOptions("null", options); + return w.writeAll("null"); }, else => @compileError("unable to format type '" ++ @typeName(T) ++ "'"), } @@ -1074,75 +1152,68 @@ fn printErrorSet(w: *Writer, error_set: anyerror) Error!void { try w.writeVecAll(&vecs); } -pub fn printInt( +fn printEnumExhaustive(w: *Writer, value: anytype) Error!void { + var vecs: [2][]const u8 = .{ ".", @tagName(value) }; + try w.writeVecAll(&vecs); +} + +fn printEnumNonexhaustive(w: *Writer, value: anytype) Error!void { + if (std.enums.tagName(@TypeOf(value), value)) |tag_name| { + var vecs: [2][]const u8 = .{ ".", tag_name }; + try w.writeVecAll(&vecs); + return; + } + try w.writeAll("@enumFromInt("); + try w.printInt(@intFromEnum(value), 10, .lower, .{}); + try w.writeByte(')'); +} + +pub fn printVector( w: *Writer, comptime fmt: []const u8, options: std.fmt.Options, value: anytype, + max_depth: usize, ) Error!void { - const int_value = if (@TypeOf(value) == comptime_int) blk: { - const Int = std.math.IntFittingRange(value, value); - break :blk @as(Int, value); - } else value; - - switch (fmt.len) { - 0 => return w.printIntOptions(int_value, 10, .lower, options), - 1 => switch (fmt[0]) { - 'd' => return w.printIntOptions(int_value, 10, .lower, options), - 'c' => { - if (@typeInfo(@TypeOf(int_value)).int.bits <= 8) { - return w.printAsciiChar(@as(u8, int_value), options); - } else { - @compileError("cannot print integer that is larger than 8 bits as an ASCII character"); - } - }, - 'u' => { - if (@typeInfo(@TypeOf(int_value)).int.bits <= 21) { - return w.printUnicodeCodepoint(@as(u21, int_value), options); - } else { - @compileError("cannot print integer that is larger than 21 bits as an UTF-8 sequence"); - } - }, - 'b' => return w.printIntOptions(int_value, 2, .lower, options), - 'x' => return w.printIntOptions(int_value, 16, .lower, options), - 'X' => return w.printIntOptions(int_value, 16, .upper, options), - 'o' => return w.printIntOptions(int_value, 8, .lower, options), - 'B' => return w.printByteSize(int_value, .decimal, options), - 'D' => return w.printDuration(int_value, options), - else => invalidFmtError(fmt, value), - }, - 2 => { - if (fmt[0] == 'B' and fmt[1] == 'i') { - return w.printByteSize(int_value, .binary, options); - } else { - invalidFmtError(fmt, value); - } - }, - else => invalidFmtError(fmt, value), + const len = @typeInfo(@TypeOf(value)).vector.len; + if (max_depth == 0) return w.writeAll("{ ... }"); + try w.writeAll("{ "); + inline for (0..len) |i| { + try w.printValue(fmt, options, value[i], max_depth - 1); + if (i < len - 1) try w.writeAll(", "); } - comptime unreachable; -} - -pub fn printAsciiChar(w: *Writer, c: u8, options: std.fmt.Options) Error!void { - return w.alignBufferOptions(@as(*const [1]u8, &c), options); + try w.writeAll(" }"); } -pub fn printAscii(w: *Writer, bytes: []const u8, options: std.fmt.Options) Error!void { - return w.alignBufferOptions(bytes, options); -} - -pub fn printUnicodeCodepoint(w: *Writer, c: u21, options: std.fmt.Options) Error!void { - var buf: [4]u8 = undefined; - const len = std.unicode.utf8Encode(c, &buf) catch |err| switch (err) { - error.Utf8CannotEncodeSurrogateHalf, error.CodepointTooLarge => l: { - buf[0..3].* = std.unicode.replacement_character_utf8; - break :l 3; +// A wrapper around `printIntAny` to avoid the generic explosion of this +// function by funneling smaller integer types through `isize` and `usize`. +pub inline fn printInt( + w: *Writer, + value: anytype, + base: u8, + case: std.fmt.Case, + options: std.fmt.Options, +) Error!void { + switch (@TypeOf(value)) { + isize, usize => {}, + comptime_int => { + if (comptime std.math.cast(usize, value)) |x| return printIntAny(w, x, base, case, options); + if (comptime std.math.cast(isize, value)) |x| return printIntAny(w, x, base, case, options); + const Int = std.math.IntFittingRange(value, value); + return printIntAny(w, @as(Int, value), base, case, options); }, - }; - return w.alignBufferOptions(buf[0..len], options); + else => switch (@typeInfo(@TypeOf(value)).int.signedness) { + .signed => if (std.math.cast(isize, value)) |x| return printIntAny(w, x, base, case, options), + .unsigned => if (std.math.cast(usize, value)) |x| return printIntAny(w, x, base, case, options), + }, + } + return printIntAny(w, value, base, case, options); } -pub fn printIntOptions( +/// In general, prefer `printInt` to avoid generic explosion. However this +/// function may be used when optimal codegen for a particular integer type is +/// desired. +pub fn printIntAny( w: *Writer, value: anytype, base: u8, @@ -1150,20 +1221,14 @@ pub fn printIntOptions( options: std.fmt.Options, ) Error!void { assert(base >= 2); - - const int_value = if (@TypeOf(value) == comptime_int) blk: { - const Int = std.math.IntFittingRange(value, value); - break :blk @as(Int, value); - } else value; - - const value_info = @typeInfo(@TypeOf(int_value)).int; + const value_info = @typeInfo(@TypeOf(value)).int; // The type must have the same size as `base` or be wider in order for the // division to work const min_int_bits = comptime @max(value_info.bits, 8); const MinInt = std.meta.Int(.unsigned, min_int_bits); - const abs_value = @abs(int_value); + const abs_value = @abs(value); // The worst case in terms of space needed is base 2, plus 1 for the sign var buf: [1 + @max(@as(comptime_int, value_info.bits), 1)]u8 = undefined; @@ -1210,38 +1275,49 @@ pub fn printIntOptions( return w.alignBufferOptions(buf[index..], options); } +pub fn printAsciiChar(w: *Writer, c: u8, options: std.fmt.Options) Error!void { + return w.alignBufferOptions(@as(*const [1]u8, &c), options); +} + +pub fn printAscii(w: *Writer, bytes: []const u8, options: std.fmt.Options) Error!void { + return w.alignBufferOptions(bytes, options); +} + +pub fn printUnicodeCodepoint(w: *Writer, c: u21) Error!void { + var buf: [4]u8 = undefined; + const len = std.unicode.utf8Encode(c, &buf) catch |err| switch (err) { + error.Utf8CannotEncodeSurrogateHalf, error.CodepointTooLarge => l: { + buf[0..3].* = std.unicode.replacement_character_utf8; + break :l 3; + }, + }; + return w.writeAll(buf[0..len]); +} + pub fn printFloat( w: *Writer, - comptime fmt: []const u8, - options: std.fmt.Options, value: anytype, + mode: std.fmt.float.Mode, + options: std.fmt.Options, ) Error!void { var buf: [std.fmt.float.bufferSize(.decimal, f64)]u8 = undefined; + const s = std.fmt.float.render(&buf, value, .{ + .mode = mode, + .precision = options.precision, + }) catch |err| switch (err) { + error.BufferTooSmall => "(float)", + }; + return w.alignBufferOptions(s, options); +} - if (fmt.len > 1) invalidFmtError(fmt, value); - switch (if (fmt.len == 0) 'e' else fmt[0]) { - 'e' => { - const s = std.fmt.float.render(&buf, value, .{ .mode = .scientific, .precision = options.precision }) catch |err| switch (err) { - error.BufferTooSmall => "(float)", - }; - return w.alignBufferOptions(s, options); - }, - 'd' => { - const s = std.fmt.float.render(&buf, value, .{ .mode = .decimal, .precision = options.precision }) catch |err| switch (err) { - error.BufferTooSmall => "(float)", - }; - return w.alignBufferOptions(s, options); - }, - 'x' => { - var sub_bw: Writer = .fixed(&buf); - sub_bw.printFloatHexadecimal(value, options.precision) catch unreachable; - return w.alignBufferOptions(sub_bw.buffered(), options); - }, - else => invalidFmtError(fmt, value), - } +pub fn printFloatHexOptions(w: *Writer, value: anytype, case: std.fmt.Case, options: std.fmt.Options) Error!void { + var buf: [50]u8 = undefined; // for aligning + var sub_writer: Writer = .fixed(&buf); + printFloatHex(&sub_writer, value, case, options.precision) catch unreachable; // buf is large enough + return w.alignBufferOptions(sub_writer.buffered(), options); } -pub fn printFloatHexadecimal(w: *Writer, value: anytype, opt_precision: ?usize) Error!void { +pub fn printFloatHex(w: *Writer, value: anytype, case: std.fmt.Case, opt_precision: ?usize) Error!void { if (std.math.signbit(value)) try w.writeByte('-'); if (std.math.isNan(value)) return w.writeAll("nan"); if (std.math.isInf(value)) return w.writeAll("inf"); @@ -1320,7 +1396,7 @@ pub fn printFloatHexadecimal(w: *Writer, value: anytype, opt_precision: ?usize) // +1 for the decimal part. var buf: [1 + mantissa_digits]u8 = undefined; - assert(std.fmt.printInt(&buf, mantissa, 16, .lower, .{ .fill = '0', .width = 1 + mantissa_digits }) == buf.len); + assert(std.fmt.printInt(&buf, mantissa, 16, case, .{ .fill = '0', .width = 1 + mantissa_digits }) == buf.len); try w.writeAll("0x"); try w.writeByte(buf[0]); @@ -1337,7 +1413,7 @@ pub fn printFloatHexadecimal(w: *Writer, value: anytype, opt_precision: ?usize) try w.splatByteAll('0', precision - trimmed.len); }; try w.writeAll("p"); - try w.printIntOptions(exponent - exponent_bias, 10, .lower, .{}); + try w.printInt(exponent - exponent_bias, 10, case, .{}); } pub const ByteSizeUnits = enum { @@ -1433,7 +1509,7 @@ pub fn printDurationUnsigned(w: *Writer, ns: u64) Error!void { }) |unit| { if (ns_remaining >= unit.ns) { const units = ns_remaining / unit.ns; - try w.printIntOptions(units, 10, .lower, .{}); + try w.printInt(units, 10, .lower, .{}); try w.writeByte(unit.sep); ns_remaining -= units * unit.ns; if (ns_remaining == 0) return; @@ -1447,13 +1523,13 @@ pub fn printDurationUnsigned(w: *Writer, ns: u64) Error!void { }) |unit| { const kunits = ns_remaining * 1000 / unit.ns; if (kunits >= 1000) { - try w.printIntOptions(kunits / 1000, 10, .lower, .{}); + try w.printInt(kunits / 1000, 10, .lower, .{}); const frac = kunits % 1000; if (frac > 0) { // Write up to 3 decimal places var decimal_buf = [_]u8{ '.', 0, 0, 0 }; var inner: Writer = .fixed(decimal_buf[1..]); - inner.printIntOptions(frac, 10, .lower, .{ .fill = '0', .width = 3 }) catch unreachable; + inner.printInt(frac, 10, .lower, .{ .fill = '0', .width = 3 }) catch unreachable; var end: usize = 4; while (end > 1) : (end -= 1) { if (decimal_buf[end - 1] != '0') break; @@ -1464,7 +1540,7 @@ pub fn printDurationUnsigned(w: *Writer, ns: u64) Error!void { } } - try w.printIntOptions(ns_remaining, 10, .lower, .{}); + try w.printInt(ns_remaining, 10, .lower, .{}); try w.writeAll("ns"); } @@ -1474,12 +1550,18 @@ pub fn printDurationUnsigned(w: *Writer, ns: u64) Error!void { pub fn printDuration(w: *Writer, nanoseconds: anytype, options: std.fmt.Options) Error!void { // worst case: "-XXXyXXwXXdXXhXXmXX.XXXs".len = 24 var buf: [24]u8 = undefined; - var sub_bw: Writer = .fixed(&buf); - switch (@typeInfo(@TypeOf(nanoseconds)).int.signedness) { - .signed => sub_bw.printDurationSigned(nanoseconds) catch unreachable, - .unsigned => sub_bw.printDurationUnsigned(nanoseconds) catch unreachable, + var sub_writer: Writer = .fixed(&buf); + if (@TypeOf(nanoseconds) == comptime_int) { + if (nanoseconds >= 0) { + sub_writer.printDurationUnsigned(nanoseconds) catch unreachable; + } else { + sub_writer.printDurationSigned(nanoseconds) catch unreachable; + } + } else switch (@typeInfo(@TypeOf(nanoseconds)).int.signedness) { + .signed => sub_writer.printDurationSigned(nanoseconds) catch unreachable, + .unsigned => sub_writer.printDurationUnsigned(nanoseconds) catch unreachable, } - return w.alignBufferOptions(sub_bw.buffered(), options); + return w.alignBufferOptions(sub_writer.buffered(), options); } pub fn printHex(w: *Writer, bytes: []const u8, case: std.fmt.Case) Error!void { @@ -1749,7 +1831,7 @@ fn testDurationCaseSigned(expected: []const u8, input: i64) !void { try testing.expectEqualStrings(expected, w.buffered()); } -test printIntOptions { +test printInt { try testPrintIntCase("-1", @as(i1, -1), 10, .lower, .{}); try testPrintIntCase("-101111000110000101001110", @as(i32, -12345678), 2, .lower, .{}); @@ -1765,27 +1847,22 @@ test printIntOptions { try testPrintIntCase("+42", @as(i32, 42), 10, .lower, .{ .width = 3 }); try testPrintIntCase("-42", @as(i32, -42), 10, .lower, .{ .width = 3 }); -} -test "printInt with comptime_int" { - var buf: [20]u8 = undefined; - var w: Writer = .fixed(&buf); - try w.printInt("", .{}, @as(comptime_int, 123456789123456789)); - try std.testing.expectEqualStrings("123456789123456789", w.buffered()); + try testPrintIntCase("123456789123456789", @as(comptime_int, 123456789123456789), 10, .lower, .{}); } test "printFloat with comptime_float" { var buf: [20]u8 = undefined; var w: Writer = .fixed(&buf); - try w.printFloat("", .{}, @as(comptime_float, 1.0)); + try w.printFloat(@as(comptime_float, 1.0), .scientific, .{}); try std.testing.expectEqualStrings(w.buffered(), "1e0"); - try std.testing.expectFmt("1e0", "{}", .{1.0}); + try std.testing.expectFmt("1", "{}", .{1.0}); } fn testPrintIntCase(expected: []const u8, value: anytype, base: u8, case: std.fmt.Case, options: std.fmt.Options) !void { var buffer: [100]u8 = undefined; var w: Writer = .fixed(&buffer); - try w.printIntOptions(value, base, case, options); + try w.printInt(value, base, case, options); try testing.expectEqualStrings(expected, w.buffered()); } diff --git a/lib/std/json/dynamic_test.zig b/lib/std/json/dynamic_test.zig index 45cdc0d0c7c093bcb7ff47c5d40558399c07cafc..1362e3cfadfb92cdda271127e34d568e2ec683d3 100644 --- a/lib/std/json/dynamic_test.zig +++ b/lib/std/json/dynamic_test.zig @@ -254,7 +254,7 @@ test "Value.jsonStringify" { \\ true, \\ 42, \\ 43, - \\ 4.2e1, + \\ 42, \\ "weeee", \\ [ \\ 1, @@ -266,7 +266,7 @@ test "Value.jsonStringify" { \\ } \\] ; - try testing.expectEqualSlices(u8, expected, fbs.getWritten()); + try testing.expectEqualStrings(expected, fbs.getWritten()); } test "parseFromValue(std.json.Value,...)" { diff --git a/lib/std/json/stringify.zig b/lib/std/json/stringify.zig index 0b575fc587a73cf06dd621286dcb7444bd25d522..b3c208756cc136df4d03ea6f590d787f3ea8638a 100644 --- a/lib/std/json/stringify.zig +++ b/lib/std/json/stringify.zig @@ -469,7 +469,6 @@ pub fn WriteStream( /// * When option `emit_nonportable_numbers_as_strings` is true, if the value is outside the range `+-1<<53` (the precise integer range of f64), it is rendered as a JSON string in base 10. Otherwise, it is rendered as JSON number. /// * Zig floats -> JSON number or string. /// * If the value cannot be precisely represented by an f64, it is rendered as a JSON string. Otherwise, it is rendered as JSON number. - /// * TODO: Float rendering will likely change in the future, e.g. to remove the unnecessary "e+00". /// * Zig `[]const u8`, `[]u8`, `*[N]u8`, `@Vector(N, u8)`, and similar -> JSON string. /// * See `StringifyOptions.emit_strings_as_arrays`. /// * If the content is not valid UTF-8, rendered as an array of numbers instead. diff --git a/lib/std/json/stringify_test.zig b/lib/std/json/stringify_test.zig index 122616b596cab6739568c6fa12ac5c576b0efe49..22dd50428593c453325e2dfeef709d8daf64bf17 100644 --- a/lib/std/json/stringify_test.zig +++ b/lib/std/json/stringify_test.zig @@ -74,16 +74,16 @@ fn testBasicWriteStream(w: anytype, slice_stream: anytype) !void { \\{ \\ "object": { \\ "one": 1, - \\ "two": 2e0 + \\ "two": 2 \\ }, \\ "string": "This is a string", \\ "array": [ \\ "Another string", \\ 1, - \\ 3.5e0 + \\ 3.5 \\ ], \\ "int": 10, - \\ "float": 3.5e0 + \\ "float": 3.5 \\} ; try std.testing.expectEqualStrings(expected, result); @@ -123,12 +123,12 @@ test "stringify basic types" { try testStringify("null", @as(?u8, null), .{}); try testStringify("null", @as(?*u32, null), .{}); try testStringify("42", 42, .{}); - try testStringify("4.2e1", 42.0, .{}); + try testStringify("42", 42.0, .{}); try testStringify("42", @as(u8, 42), .{}); try testStringify("42", @as(u128, 42), .{}); try testStringify("9999999999999999", 9999999999999999, .{}); - try testStringify("4.2e1", @as(f32, 42), .{}); - try testStringify("4.2e1", @as(f64, 42), .{}); + try testStringify("42", @as(f32, 42), .{}); + try testStringify("42", @as(f64, 42), .{}); try testStringify("\"ItBroke\"", @as(anyerror, error.ItBroke), .{}); try testStringify("\"ItBroke\"", error.ItBroke, .{}); } diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index 000b578dc083230ea8bea9514dc0e328827f6610..c657aeeed30f21943f80a282bbde5404166f5923 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -2028,6 +2028,14 @@ pub const Mutable = struct { pub fn normalize(r: *Mutable, length: usize) void { r.len = llnormalize(r.limbs[0..length]); } + + pub fn format(self: Mutable, w: *std.io.Writer) std.io.Writer.Error!void { + return formatInteger(self, w, 10, .lower); + } + + pub fn formatInteger(self: Const, w: *std.io.Writer, base: u8, case: std.fmt.Case) std.io.Writer.Error!void { + return self.toConst().formatInteger(w, base, case); + } }; /// A arbitrary-precision big integer, with a fixed set of immutable limbs. @@ -2321,7 +2329,7 @@ pub const Const = struct { /// this function will fail to print the string, printing "(BigInt)" instead of a number. /// This is because the rendering algorithm requires reversing a string, which requires O(N) memory. /// See `toString` and `toStringAlloc` for a way to print big integers without failure. - pub fn print(self: Const, w: *std.io.Writer, base: u8, case: std.fmt.Case) std.io.Writer.Error!void { + pub fn formatInteger(self: Const, w: *std.io.Writer, base: u8, case: std.fmt.Case) std.io.Writer.Error!void { const available_len = 64; if (self.limbs.len > available_len) return w.writeAll("(BigInt)"); @@ -2337,20 +2345,6 @@ pub const Const = struct { return w.writeAll(buf[0..len]); } - const Format = struct { - int: Const, - base: u8, - case: std.fmt.Case, - - pub fn default(f: Format, w: *std.io.Writer) std.io.Writer.Error!void { - return print(f.int, w, f.base, f.case); - } - }; - - pub fn fmt(self: Const, base: u8, case: std.fmt.Case) std.fmt.Formatter(Format, Format.default) { - return .{ .data = .{ .int = self, .base = base, .case = case } }; - } - /// Converts self to a string in the requested base. /// Caller owns returned memory. /// Asserts that `base` is in the range [2, 36]. @@ -2918,16 +2912,16 @@ pub const Managed = struct { } /// To allow `std.fmt.format` to work with `Managed`. - pub fn format(self: Managed, w: *std.io.Writer, comptime f: []const u8) std.io.Writer.Error!void { - return self.toConst().format(w, f); + pub fn format(self: Managed, w: *std.io.Writer) std.io.Writer.Error!void { + return formatInteger(self, w, 10, .lower); } /// If the absolute value of integer is greater than or equal to `pow(2, 64 * @sizeOf(usize) * 8)`, /// this function will fail to print the string, printing "(BigInt)" instead of a number. /// This is because the rendering algorithm requires reversing a string, which requires O(N) memory. /// See `toString` and `toStringAlloc` for a way to print big integers without failure. - pub fn fmt(self: Managed, base: u8, case: std.fmt.Case) std.fmt.Formatter(Const.Format, Const.Format.default) { - return .{ .data = .{ .int = self.toConst(), .base = base, .case = case } }; + pub fn formatInteger(self: Managed, w: *std.io.Writer, base: u8, case: std.fmt.Case) std.io.Writer.Error!void { + return self.toConst().formatInteger(w, base, case); } /// Returns math.Order.lt, math.Order.eq, math.Order.gt if |a| < |b|, |a| == diff --git a/lib/std/math/big/int_test.zig b/lib/std/math/big/int_test.zig index 58f5a01a050ab95c00433e4cb34de1bfae285e91..f09797352c094657173351af60f33a5acce39dcb 100644 --- a/lib/std/math/big/int_test.zig +++ b/lib/std/math/big/int_test.zig @@ -3813,8 +3813,8 @@ test "(BigInt) positive" { try a.pow(&a, 64 * @sizeOf(Limb) * 8); try b.sub(&a, &c); - try testing.expectFmt("(BigInt)", "{f}", .{a.fmt(10, .lower)}); - try testing.expectFmt("1044388881413152506691752710716624382579964249047383780384233483283953907971557456848826811934997558340890106714439262837987573438185793607263236087851365277945956976543709998340361590134383718314428070011855946226376318839397712745672334684344586617496807908705803704071284048740118609114467977783598029006686938976881787785946905630190260940599579453432823469303026696443059025015972399867714215541693835559885291486318237914434496734087811872639496475100189041349008417061675093668333850551032972088269550769983616369411933015213796825837188091833656751221318492846368125550225998300412344784862595674492194617023806505913245610825731835380087608622102834270197698202313169017678006675195485079921636419370285375124784014907159135459982790513399611551794271106831134090584272884279791554849782954323534517065223269061394905987693002122963395687782878948440616007412945674919823050571642377154816321380631045902916136926708342856440730447899971901781465763473223850267253059899795996090799469201774624817718449867455659250178329070473119433165550807568221846571746373296884912819520317457002440926616910874148385078411929804522981857338977648103126085903001302413467189726673216491511131602920781738033436090243804708340403154190335", "{f}", .{b.fmt(10, .lower)}); + try testing.expectFmt("(BigInt)", "{d}", .{a}); + try testing.expectFmt("1044388881413152506691752710716624382579964249047383780384233483283953907971557456848826811934997558340890106714439262837987573438185793607263236087851365277945956976543709998340361590134383718314428070011855946226376318839397712745672334684344586617496807908705803704071284048740118609114467977783598029006686938976881787785946905630190260940599579453432823469303026696443059025015972399867714215541693835559885291486318237914434496734087811872639496475100189041349008417061675093668333850551032972088269550769983616369411933015213796825837188091833656751221318492846368125550225998300412344784862595674492194617023806505913245610825731835380087608622102834270197698202313169017678006675195485079921636419370285375124784014907159135459982790513399611551794271106831134090584272884279791554849782954323534517065223269061394905987693002122963395687782878948440616007412945674919823050571642377154816321380631045902916136926708342856440730447899971901781465763473223850267253059899795996090799469201774624817718449867455659250178329070473119433165550807568221846571746373296884912819520317457002440926616910874148385078411929804522981857338977648103126085903001302413467189726673216491511131602920781738033436090243804708340403154190335", "{d}", .{b}); } test "(BigInt) negative" { @@ -3832,10 +3832,10 @@ test "(BigInt) negative" { a.negate(); try b.add(&a, &c); - const a_fmt = try std.fmt.allocPrint(testing.allocator, "{f}", .{a.fmt(10, .lower)}); + const a_fmt = try std.fmt.allocPrint(testing.allocator, "{d}", .{a}); defer testing.allocator.free(a_fmt); - const b_fmt = try std.fmt.allocPrint(testing.allocator, "{f}", .{b.fmt(10, .lower)}); + const b_fmt = try std.fmt.allocPrint(testing.allocator, "{d}", .{b}); defer testing.allocator.free(b_fmt); try testing.expect(mem.eql(u8, a_fmt, "(BigInt)")); diff --git a/lib/std/zig.zig b/lib/std/zig.zig index 5d7f16a2a0881e977f6c64d61312a6035a9abf98..be3c982d09e2c2994664720a4b610b784d88e05e 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -475,7 +475,7 @@ pub fn stringEscape(bytes: []const u8, w: *std.io.Writer) std.io.Writer.Error!vo ' ', '!', '#'...'&', '('...'[', ']'...'~' => try w.writeByte(byte), else => { try w.writeAll("\\x"); - try w.printIntOptions(byte, 16, .lower, .{ .width = 2, .fill = '0' }); + try w.printInt(byte, 16, .lower, .{ .width = 2, .fill = '0' }); }, }; } @@ -492,7 +492,7 @@ pub fn charEscape(bytes: []const u8, w: *std.io.Writer) std.io.Writer.Error!void ' ', '!', '#'...'&', '('...'[', ']'...'~' => try w.writeByte(byte), else => { try w.writeAll("\\x"); - try w.printIntOptions(byte, 16, .lower, .{ .width = 2, .fill = '0' }); + try w.printInt(byte, 16, .lower, .{ .width = 2, .fill = '0' }); }, }; } diff --git a/lib/std/zig/llvm/Builder.zig b/lib/std/zig/llvm/Builder.zig index 4691b969337e1e268ffc95e92c1f33a02d84ed4c..b3e0311cddc0b77dfe8ed071b3542daef2ae0344 100644 --- a/lib/std/zig/llvm/Builder.zig +++ b/lib/std/zig/llvm/Builder.zig @@ -246,7 +246,7 @@ pub const Type = enum(u32) { _, pub const ptr_amdgpu_constant = - @field(Type, std.fmt.comptimePrint("ptr{f }", .{AddrSpace.amdgpu.constant})); + @field(Type, std.fmt.comptimePrint("ptr{f}", .{AddrSpace.amdgpu.constant.fmt(" ")})); pub const Tag = enum(u4) { simple, @@ -779,7 +779,7 @@ pub const Type = enum(u32) { } }, .integer => try w.print("i{d}", .{item.data}), - .pointer => try w.print("ptr{f }", .{@as(AddrSpace, @enumFromInt(item.data))}), + .pointer => try w.print("ptr{f}", .{@as(AddrSpace, @enumFromInt(item.data)).fmt(" ")}), .target => { var extra = data.builder.typeExtraDataTrail(Type.Target, item.data); const types = extra.trail.next(extra.data.types_len, Type, data.builder); @@ -1242,7 +1242,7 @@ pub const Attribute = union(Kind) { .sret, .elementtype, => |ty| try w.print(" {s}({f})", .{ @tagName(attribute), ty.fmt(data.builder, .percent) }), - .@"align" => |alignment| try w.print("{f }", .{alignment}), + .@"align" => |alignment| try w.print("{f}", .{alignment.fmt(" ")}), .dereferenceable, .dereferenceable_or_null, => |size| try w.print(" {s}({d})", .{ @tagName(attribute), size }), @@ -1853,10 +1853,31 @@ pub const ThreadLocal = enum(u3) { initialexec = 3, localexec = 4, - pub fn format(self: ThreadLocal, w: *Writer, comptime prefix: []const u8) Writer.Error!void { - if (self == .default) return; - try w.print("{s}thread_local", .{prefix}); - if (self != .generaldynamic) try w.print("({s})", .{@tagName(self)}); + pub fn format(tl: ThreadLocal, w: *Writer) Writer.Error!void { + return Prefixed.format(.{ .thread_local = tl, .prefix = "" }, w); + } + + pub const Prefixed = struct { + thread_local: ThreadLocal, + prefix: []const u8, + + pub fn format(p: Prefixed, w: *Writer) Writer.Error!void { + switch (p.thread_local) { + .default => return, + .generaldynamic => { + var vecs: [2][]const u8 = .{ p.prefix, "thread_local" }; + return w.writeVecAll(&vecs); + }, + else => { + var vecs: [4][]const u8 = .{ p.prefix, "thread_local(", @tagName(p.thread_local), ")" }; + return w.writeVecAll(&vecs); + }, + } + } + }; + + pub fn fmt(tl: ThreadLocal, prefix: []const u8) Prefixed { + return .{ .thread_local = tl, .prefix = prefix }; } }; @@ -1961,8 +1982,24 @@ pub const AddrSpace = enum(u24) { pub const funcref: AddrSpace = @enumFromInt(20); }; - pub fn format(self: AddrSpace, w: *Writer, comptime prefix: []const u8) Writer.Error!void { - if (self != .default) try w.print("{s}addrspace({d})", .{ prefix, @intFromEnum(self) }); + pub fn format(addr_space: AddrSpace, w: *Writer) Writer.Error!void { + return Prefixed.format(.{ .addr_space = addr_space, .prefix = "" }, w); + } + + pub const Prefixed = struct { + addr_space: AddrSpace, + prefix: []const u8, + + pub fn format(p: Prefixed, w: *Writer) Writer.Error!void { + switch (p.addr_space) { + .default => return, + else => return w.print("{s}addrspace({d})", .{ p.prefix, p.addr_space }), + } + } + }; + + pub fn fmt(addr_space: AddrSpace, prefix: []const u8) Prefixed { + return .{ .addr_space = addr_space, .prefix = prefix }; } }; @@ -1994,8 +2031,18 @@ pub const Alignment = enum(u6) { return if (self == .default) 0 else (@intFromEnum(self) + 1); } - pub fn format(self: Alignment, w: *Writer, comptime prefix: []const u8) Writer.Error!void { - try w.print("{s}align {d}", .{ prefix, self.toByteUnits() orelse return }); + pub const Prefixed = struct { + alignment: Alignment, + prefix: []const u8, + + pub fn format(p: Prefixed, w: *Writer) Writer.Error!void { + const byte_units = p.alignment.toByteUnits() orelse return; + return w.print("{s}align ({d})", .{ p.prefix, byte_units }); + } + }; + + pub fn fmt(alignment: Alignment, prefix: []const u8) Prefixed { + return .{ .alignment = alignment, .prefix = prefix }; } }; @@ -6978,8 +7025,27 @@ pub const MemoryAccessKind = enum(u1) { normal, @"volatile", - pub fn format(self: MemoryAccessKind, w: *Writer, comptime prefix: []const u8) Writer.Error!void { - if (self != .normal) try w.print("{s}{s}", .{ prefix, @tagName(self) }); + pub fn format(memory_access_kind: MemoryAccessKind, w: *Writer) Writer.Error!void { + return Prefixed.format(.{ .memory_access_kind = memory_access_kind, .prefix = "" }, w); + } + + pub const Prefixed = struct { + memory_access_kind: MemoryAccessKind, + prefix: []const u8, + + pub fn format(p: Prefixed, w: *Writer) Writer.Error!void { + switch (p.memory_access_kind) { + .normal => return, + .@"volatile" => { + var vecs: [2][]const u8 = .{ p.prefix, "volatile" }; + return w.writeVecAll(&vecs); + }, + } + } + }; + + pub fn fmt(memory_access_kind: MemoryAccessKind, prefix: []const u8) Prefixed { + return .{ .memory_access_kind = memory_access_kind, .prefix = prefix }; } }; @@ -6987,10 +7053,27 @@ pub const SyncScope = enum(u1) { singlethread, system, - pub fn format(self: SyncScope, w: *Writer, comptime prefix: []const u8) Writer.Error!void { - if (self != .system) try w.print( - \\{s}syncscope("{s}") - , .{ prefix, @tagName(self) }); + pub fn format(sync_scope: SyncScope, w: *Writer) Writer.Error!void { + return Prefixed.format(.{ .sync_scope = sync_scope, .prefix = "" }, w); + } + + pub const Prefixed = struct { + sync_scope: SyncScope, + prefix: []const u8, + + pub fn format(p: Prefixed, w: *Writer) Writer.Error!void { + switch (p.sync_scope) { + .system => return, + .singlethread => { + var vecs: [2][]const u8 = .{ p.prefix, "syncscope(\"singlethread\")" }; + return w.writeVecAll(&vecs); + }, + } + } + }; + + pub fn fmt(sync_scope: SyncScope, prefix: []const u8) Prefixed { + return .{ .sync_scope = sync_scope, .prefix = prefix }; } }; @@ -7003,8 +7086,27 @@ pub const AtomicOrdering = enum(u3) { acq_rel = 5, seq_cst = 6, - pub fn format(self: AtomicOrdering, w: *Writer, comptime prefix: []const u8) Writer.Error!void { - if (self != .none) try w.print("{s}{s}", .{ prefix, @tagName(self) }); + pub fn format(atomic_ordering: AtomicOrdering, w: *Writer) Writer.Error!void { + return Prefixed.format(.{ .atomic_ordering = atomic_ordering, .prefix = "" }, w); + } + + pub const Prefixed = struct { + atomic_ordering: AtomicOrdering, + prefix: []const u8, + + pub fn format(p: Prefixed, w: *Writer) Writer.Error!void { + switch (p.atomic_ordering) { + .none => return, + else => { + var vecs: [2][]const u8 = .{ p.prefix, @tagName(p.atomic_ordering) }; + return w.writeVecAll(&vecs); + }, + } + } + }; + + pub fn fmt(atomic_ordering: AtomicOrdering, prefix: []const u8) Prefixed { + return .{ .atomic_ordering = atomic_ordering, .prefix = prefix }; } }; @@ -8550,7 +8652,7 @@ pub fn init(options: Options) Allocator.Error!Builder { inline for (.{ 0, 4 }) |addr_space_index| { const addr_space: AddrSpace = @enumFromInt(addr_space_index); assert(self.ptrTypeAssumeCapacity(addr_space) == - @field(Type, std.fmt.comptimePrint("ptr{f }", .{addr_space}))); + @field(Type, std.fmt.comptimePrint("ptr{f}", .{addr_space.fmt(" ")}))); } } @@ -9469,7 +9571,7 @@ pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void metadata_formatter.need_comma = true; defer metadata_formatter.need_comma = undefined; try w.print( - \\{f} ={f}{f}{f}{f}{f }{f}{f }{f} {s} {f}{f}{f, }{f} + \\{f} ={f}{f}{f}{f}{f}{f}{f}{f} {s} {f}{f}{f}{f} \\ , .{ variable.global.fmt(self), @@ -9479,14 +9581,14 @@ pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void global.preemption, global.visibility, global.dll_storage_class, - variable.thread_local, + variable.thread_local.fmt(" "), global.unnamed_addr, - global.addr_space, + global.addr_space.fmt(" "), global.externally_initialized, @tagName(variable.mutability), global.type.fmt(self, .percent), variable.init.fmt(self, .{ .space = true }), - variable.alignment, + variable.alignment.fmt(", "), try metadata_formatter.fmt("!dbg ", global.dbg, null), }); } @@ -9500,7 +9602,7 @@ pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void metadata_formatter.need_comma = true; defer metadata_formatter.need_comma = undefined; try w.print( - \\{f} ={f}{f}{f}{f}{f }{f} alias {f}, {f}{f} + \\{f} ={f}{f}{f}{f}{f}{f} alias {f}, {f}{f} \\ , .{ alias.global.fmt(self), @@ -9508,7 +9610,7 @@ pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void global.preemption, global.visibility, global.dll_storage_class, - alias.thread_local, + alias.thread_local.fmt(" "), global.unnamed_addr, global.type.fmt(self, .percent), alias.aliasee.fmt(self, .{ .percent = true }), @@ -9564,15 +9666,15 @@ pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void try w.writeAll("..."); }, } - try w.print("){f}{f }", .{ global.unnamed_addr, global.addr_space }); + try w.print("){f}{f}", .{ global.unnamed_addr, global.addr_space.fmt(" ") }); if (function_attributes != .none) try w.print(" #{d}", .{ (try attribute_groups.getOrPutValue(self.gpa, function_attributes, {})).index, }); { metadata_formatter.need_comma = false; defer metadata_formatter.need_comma = undefined; - try w.print("{f }{f}", .{ - function.alignment, + try w.print("{f}{f}", .{ + function.alignment.fmt(" "), try metadata_formatter.fmt(" !dbg ", global.dbg, null), }); } @@ -9709,7 +9811,7 @@ pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void .@"alloca inalloca", => |tag| { const extra = function.extraData(Function.Instruction.Alloca, instruction.data); - try w.print(" %{f} = {s} {f}{f}{f, }{f, }", .{ + try w.print(" %{f} = {s} {f}{f}{f}{f}", .{ instruction_index.name(&function).fmt(self), @tagName(tag), extra.type.fmt(self, .percent), @@ -9720,24 +9822,24 @@ pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void .comma = true, .percent = true, }), - extra.info.alignment, - extra.info.addr_space, + extra.info.alignment.fmt(", "), + extra.info.addr_space.fmt(", "), }); }, .arg => unreachable, .atomicrmw => |tag| { const extra = function.extraData(Function.Instruction.AtomicRmw, instruction.data); - try w.print(" %{f} = {s}{f } {s} {f}, {f}{f }{f }{f, }", .{ + try w.print(" %{f} = {t}{f} {t} {f}, {f}{f}{f}{f}", .{ instruction_index.name(&function).fmt(self), - @tagName(tag), - extra.info.access_kind, - @tagName(extra.info.atomic_rmw_operation), + tag, + extra.info.access_kind.fmt(" "), + extra.info.atomic_rmw_operation, extra.ptr.fmt(function_index, self, .{ .percent = true }), extra.val.fmt(function_index, self, .{ .percent = true }), - extra.info.sync_scope, - extra.info.success_ordering, - extra.info.alignment, + extra.info.sync_scope.fmt(" "), + extra.info.success_ordering.fmt(" "), + extra.info.alignment.fmt(", "), }); }, .block => { @@ -9792,8 +9894,8 @@ pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void }), .none => unreachable, } - try w.print("{s}{f}{f}{f} {f} {f}(", .{ - @tagName(tag), + try w.print("{t}{f}{f}{f} {f} {f}(", .{ + tag, extra.data.info.call_conv, extra.data.attributes.ret(self).fmt(self, .{}), extra.data.callee.typeOf(function_index, self).pointerAddrSpace(self), @@ -9831,17 +9933,17 @@ pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void => |tag| { const extra = function.extraData(Function.Instruction.CmpXchg, instruction.data); - try w.print(" %{f} = {s}{f } {f}, {f}, {f}{f }{f }{f }{f, }", .{ + try w.print(" %{f} = {t}{f} {f}, {f}, {f}{f}{f}{f}{f}", .{ instruction_index.name(&function).fmt(self), - @tagName(tag), - extra.info.access_kind, + tag, + extra.info.access_kind.fmt(" "), extra.ptr.fmt(function_index, self, .{ .percent = true }), extra.cmp.fmt(function_index, self, .{ .percent = true }), extra.new.fmt(function_index, self, .{ .percent = true }), - extra.info.sync_scope, - extra.info.success_ordering, - extra.info.failure_ordering, - extra.info.alignment, + extra.info.sync_scope.fmt(" "), + extra.info.success_ordering.fmt(" "), + extra.info.failure_ordering.fmt(" "), + extra.info.alignment.fmt(", "), }); }, .extractelement => |tag| { @@ -9869,10 +9971,10 @@ pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void }, .fence => |tag| { const info: MemoryAccessInfo = @bitCast(instruction.data); - try w.print(" {s}{f }{f }", .{ - @tagName(tag), - info.sync_scope, - info.success_ordering, + try w.print(" {t}{f}{f}", .{ + tag, + info.sync_scope.fmt(" "), + info.success_ordering.fmt(" "), }); }, .fneg, @@ -9947,15 +10049,15 @@ pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void .@"load atomic", => |tag| { const extra = function.extraData(Function.Instruction.Load, instruction.data); - try w.print(" %{f} = {s}{f } {f}, {f}{f }{f }{f, }", .{ + try w.print(" %{f} = {t}{f} {f}, {f}{f}{f}{f}", .{ instruction_index.name(&function).fmt(self), - @tagName(tag), - extra.info.access_kind, + tag, + extra.info.access_kind.fmt(" "), extra.type.fmt(self, .percent), extra.ptr.fmt(function_index, self, .{ .percent = true }), - extra.info.sync_scope, - extra.info.success_ordering, - extra.info.alignment, + extra.info.sync_scope.fmt(" "), + extra.info.success_ordering.fmt(" "), + extra.info.alignment.fmt(", "), }); }, .phi, @@ -10015,14 +10117,14 @@ pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void .@"store atomic", => |tag| { const extra = function.extraData(Function.Instruction.Store, instruction.data); - try w.print(" {s}{f } {f}, {f}{f }{f }{f, }", .{ - @tagName(tag), - extra.info.access_kind, + try w.print(" {t}{f} {f}, {f}{f}{f}{f}", .{ + tag, + extra.info.access_kind.fmt(" "), extra.val.fmt(function_index, self, .{ .percent = true }), extra.ptr.fmt(function_index, self, .{ .percent = true }), - extra.info.sync_scope, - extra.info.success_ordering, - extra.info.alignment, + extra.info.sync_scope.fmt(" "), + extra.info.success_ordering.fmt(" "), + extra.info.alignment.fmt(", "), }); }, .@"switch" => |tag| { diff --git a/lib/std/zon/stringify.zig b/lib/std/zon/stringify.zig index c25bf733e37dc62a11350a2674efbe0844b7fea3..2b4fc642df35c805c577bde2894d502d9e220ea6 100644 --- a/lib/std/zon/stringify.zig +++ b/lib/std/zon/stringify.zig @@ -615,7 +615,7 @@ pub fn Serializer(Writer: type) type { /// Serialize an integer. pub fn int(self: *Self, val: anytype) Writer.Error!void { - //try self.writer.printIntOptions(val, 10, .lower, .{}); + //try self.writer.printInt(val, 10, .lower, .{}); try std.fmt.deprecatedFormat(self.writer, "{d}", .{val}); } diff --git a/src/Builtin.zig b/src/Builtin.zig index ef674cf889e3698cf6a0310127c27c3449795164..b2cb603f53fc7e1251ffd246819fac5bb6d736e3 100644 --- a/src/Builtin.zig +++ b/src/Builtin.zig @@ -200,8 +200,8 @@ pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void { }), .windows => |windows| try buffer.print( \\ .windows = .{{ - \\ .min = {fc}, - \\ .max = {fc}, + \\ .min = {f}, + \\ .max = {f}, \\ }}}}, \\ , .{ windows.min, windows.max }), diff --git a/src/Package/Fetch.zig b/src/Package/Fetch.zig index e7ba4ec2b548e42f1c4585d23b1fe965c36ea5c7..b9a1d4376accd4015c9b7d7116d648788043417d 100644 --- a/src/Package/Fetch.zig +++ b/src/Package/Fetch.zig @@ -227,9 +227,9 @@ pub const JobQueue = struct { } try buf.writer().print( - \\ pub const build_root = "{fq}"; + \\ pub const build_root = "{f}"; \\ - , .{fetch.package_root}); + , .{std.fmt.alt(fetch.package_root, .formatEscapeString)}); if (fetch.has_build_zig) { try buf.writer().print( @@ -1079,7 +1079,10 @@ fn initResource(f: *Fetch, uri: std.Uri, server_header_buffer: []u8) RunError!Re }); const notes_start = try eb.reserveNotes(notes_len); eb.extra.items[notes_start] = @intFromEnum(try eb.addErrorMessage(.{ - .msg = try eb.printString("try .url = \"{f;+/}#{f}\",", .{ uri, want_oid }), + .msg = try eb.printString("try .url = \"{f}#{f}\",", .{ + uri.fmt(.{ .scheme = true, .authority = true, .path = true }), + want_oid, + }), })); return error.FetchFailed; } diff --git a/src/Package/Fetch/git.zig b/src/Package/Fetch/git.zig index 8dfcbb24539a51fbae69e8a4c3686beff181643e..4d2dae904fb85e1b383a725132653bdbe03f5a36 100644 --- a/src/Package/Fetch/git.zig +++ b/src/Package/Fetch/git.zig @@ -662,13 +662,21 @@ pub const Session = struct { fn init(allocator: Allocator, uri: std.Uri) !Location { const scheme = try allocator.dupe(u8, uri.scheme); errdefer allocator.free(scheme); - const user = if (uri.user) |user| try std.fmt.allocPrint(allocator, "{fuser}", .{user}) else null; + const user = if (uri.user) |user| try std.fmt.allocPrint(allocator, "{f}", .{ + std.fmt.alt(user, .formatUser), + }) else null; errdefer if (user) |s| allocator.free(s); - const password = if (uri.password) |password| try std.fmt.allocPrint(allocator, "{fpassword}", .{password}) else null; + const password = if (uri.password) |password| try std.fmt.allocPrint(allocator, "{f}", .{ + std.fmt.alt(password, .formatPassword), + }) else null; errdefer if (password) |s| allocator.free(s); - const host = if (uri.host) |host| try std.fmt.allocPrint(allocator, "{fhost}", .{host}) else null; + const host = if (uri.host) |host| try std.fmt.allocPrint(allocator, "{f}", .{ + std.fmt.alt(host, .formatHost), + }) else null; errdefer if (host) |s| allocator.free(s); - const path = try std.fmt.allocPrint(allocator, "{fpath}", .{uri.path}); + const path = try std.fmt.allocPrint(allocator, "{f}", .{ + std.fmt.alt(uri.path, .formatPath), + }); errdefer allocator.free(path); // The query and fragment are not used as part of the base server URI. return .{ @@ -699,7 +707,9 @@ pub const Session = struct { fn getCapabilities(session: *Session, http_headers_buffer: []u8) !CapabilityIterator { var info_refs_uri = session.location.uri; { - const session_uri_path = try std.fmt.allocPrint(session.allocator, "{fpath}", .{session.location.uri.path}); + const session_uri_path = try std.fmt.allocPrint(session.allocator, "{f}", .{ + std.fmt.alt(session.location.uri.path, .formatPath), + }); defer session.allocator.free(session_uri_path); info_refs_uri.path = .{ .percent_encoded = try std.fs.path.resolvePosix(session.allocator, &.{ "/", session_uri_path, "info/refs" }) }; } @@ -723,7 +733,9 @@ pub const Session = struct { if (request.response.status != .ok) return error.ProtocolError; const any_redirects_occurred = request.redirect_behavior.remaining() < max_redirects; if (any_redirects_occurred) { - const request_uri_path = try std.fmt.allocPrint(session.allocator, "{fpath}", .{request.uri.path}); + const request_uri_path = try std.fmt.allocPrint(session.allocator, "{f}", .{ + std.fmt.alt(request.uri.path, .formatPath), + }); defer session.allocator.free(request_uri_path); if (!mem.endsWith(u8, request_uri_path, "/info/refs")) return error.UnparseableRedirect; var new_uri = request.uri; @@ -810,7 +822,9 @@ pub const Session = struct { pub fn listRefs(session: Session, options: ListRefsOptions) !RefIterator { var upload_pack_uri = session.location.uri; { - const session_uri_path = try std.fmt.allocPrint(session.allocator, "{fpath}", .{session.location.uri.path}); + const session_uri_path = try std.fmt.allocPrint(session.allocator, "{f}", .{ + std.fmt.alt(session.location.uri.path, .formatPath), + }); defer session.allocator.free(session_uri_path); upload_pack_uri.path = .{ .percent_encoded = try std.fs.path.resolvePosix(session.allocator, &.{ "/", session_uri_path, "git-upload-pack" }) }; } @@ -925,7 +939,9 @@ pub const Session = struct { ) !FetchStream { var upload_pack_uri = session.location.uri; { - const session_uri_path = try std.fmt.allocPrint(session.allocator, "{fpath}", .{session.location.uri.path}); + const session_uri_path = try std.fmt.allocPrint(session.allocator, "{f}", .{ + std.fmt.alt(session.location.uri.path, .formatPath), + }); defer session.allocator.free(session_uri_path); upload_pack_uri.path = .{ .percent_encoded = try std.fs.path.resolvePosix(session.allocator, &.{ "/", session_uri_path, "git-upload-pack" }) }; } diff --git a/src/Sema/LowerZon.zig b/src/Sema/LowerZon.zig index b55bab127bfe2a387b9839c620a4410b3e4f6c4b..8c70a5b7845432c34a430600daf294059cc43939 100644 --- a/src/Sema/LowerZon.zig +++ b/src/Sema/LowerZon.zig @@ -492,7 +492,7 @@ fn lowerInt( if (!val.fitsInTwosComp(int_info.signedness, int_info.bits)) { return self.fail( node, - "type '{f}' cannot represent integer value '{f}'", + "type '{f}' cannot represent integer value '{d}'", .{ res_ty.fmt(self.sema.pt), val }, ); } diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index f0fdc3a98a4c0e46c027457aff9e69784cfe49b5..49baf7acad7e1541b590d87b837148765032c024 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -1151,7 +1151,7 @@ fn gen(func: *Func) !void { func.ret_mcv.long.address().offset(-func.ret_mcv.short.indirect.off), ); func.ret_mcv.long = .{ .load_frame = .{ .index = frame_index } }; - tracking_log.debug("spill {} to {f}", .{ func.ret_mcv.long, frame_index }); + tracking_log.debug("spill {} to {}", .{ func.ret_mcv.long, frame_index }); }, else => unreachable, } @@ -1987,7 +1987,7 @@ fn allocFrameIndex(func: *Func, alloc: FrameAlloc) !FrameIndex { } const frame_index: FrameIndex = @enumFromInt(func.frame_allocs.len); try func.frame_allocs.append(func.gpa, alloc); - log.debug("allocated frame {f}", .{frame_index}); + log.debug("allocated frame {}", .{frame_index}); return frame_index; } diff --git a/src/arch/riscv64/bits.zig b/src/arch/riscv64/bits.zig index 2f79da71163fcba6a1efe01dca7d759a68a7161d..94c64dfc98de50e7ecb8183a2008c7014e222ebd 100644 --- a/src/arch/riscv64/bits.zig +++ b/src/arch/riscv64/bits.zig @@ -249,6 +249,12 @@ pub const FrameIndex = enum(u32) { spill_frame, /// Other indices are used for local variable stack slots _, + + pub const named_count = @typeInfo(FrameIndex).@"enum".fields.len; + + pub fn isNamed(fi: FrameIndex) bool { + return @intFromEnum(fi) < named_count; + } }; /// A linker symbol not yet allocated in VM. diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 177d6b7b4921e84b859cf86104be0b86bf8f70df..486497a365488c98715f78e7ed2c1a754425c5c4 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -525,47 +525,47 @@ pub const MCValue = union(enum) { }; } - pub fn format(mcv: MCValue, bw: *Writer) Writer.Error!void { + pub fn format(mcv: MCValue, w: *Writer) Writer.Error!void { switch (mcv) { - .none, .unreach, .dead, .undef => try bw.print("({s})", .{@tagName(mcv)}), - .immediate => |pl| try bw.print("0x{x}", .{pl}), - .memory => |pl| try bw.print("[ds:0x{x}]", .{pl}), - inline .eflags, .register => |pl| try bw.print("{s}", .{@tagName(pl)}), - .register_pair => |pl| try bw.print("{s}:{s}", .{ @tagName(pl[1]), @tagName(pl[0]) }), - .register_triple => |pl| try bw.print("{s}:{s}:{s}", .{ + .none, .unreach, .dead, .undef => try w.print("({s})", .{@tagName(mcv)}), + .immediate => |pl| try w.print("0x{x}", .{pl}), + .memory => |pl| try w.print("[ds:0x{x}]", .{pl}), + inline .eflags, .register => |pl| try w.print("{s}", .{@tagName(pl)}), + .register_pair => |pl| try w.print("{s}:{s}", .{ @tagName(pl[1]), @tagName(pl[0]) }), + .register_triple => |pl| try w.print("{s}:{s}:{s}", .{ @tagName(pl[2]), @tagName(pl[1]), @tagName(pl[0]), }), - .register_quadruple => |pl| try bw.print("{s}:{s}:{s}:{s}", .{ + .register_quadruple => |pl| try w.print("{s}:{s}:{s}:{s}", .{ @tagName(pl[3]), @tagName(pl[2]), @tagName(pl[1]), @tagName(pl[0]), }), - .register_offset => |pl| try bw.print("{s} + 0x{x}", .{ @tagName(pl.reg), pl.off }), - .register_overflow => |pl| try bw.print("{s}:{s}", .{ + .register_offset => |pl| try w.print("{s} + 0x{x}", .{ @tagName(pl.reg), pl.off }), + .register_overflow => |pl| try w.print("{s}:{s}", .{ @tagName(pl.eflags), @tagName(pl.reg), }), - .register_mask => |pl| try bw.print("mask({s},{f}):{c}{s}", .{ + .register_mask => |pl| try w.print("mask({s},{f}):{c}{s}", .{ @tagName(pl.info.kind), pl.info.scalar, @as(u8, if (pl.info.inverted) '!' else ' '), @tagName(pl.reg), }), - .indirect => |pl| try bw.print("[{s} + 0x{x}]", .{ @tagName(pl.reg), pl.off }), - .indirect_load_frame => |pl| try bw.print("[[{f} + 0x{x}]]", .{ pl.index, pl.off }), - .load_frame => |pl| try bw.print("[{f} + 0x{x}]", .{ pl.index, pl.off }), - .lea_frame => |pl| try bw.print("{f} + 0x{x}", .{ pl.index, pl.off }), - .load_nav => |pl| try bw.print("[nav:{d}]", .{@intFromEnum(pl)}), - .lea_nav => |pl| try bw.print("nav:{d}", .{@intFromEnum(pl)}), - .load_uav => |pl| try bw.print("[uav:{d}]", .{@intFromEnum(pl.val)}), - .lea_uav => |pl| try bw.print("uav:{d}", .{@intFromEnum(pl.val)}), - .load_lazy_sym => |pl| try bw.print("[lazy:{s}:{d}]", .{ @tagName(pl.kind), @intFromEnum(pl.ty) }), - .lea_lazy_sym => |pl| try bw.print("lazy:{s}:{d}", .{ @tagName(pl.kind), @intFromEnum(pl.ty) }), - .load_extern_func => |pl| try bw.print("[extern:{d}]", .{@intFromEnum(pl)}), - .lea_extern_func => |pl| try bw.print("extern:{d}", .{@intFromEnum(pl)}), - .elementwise_args => |pl| try bw.print("elementwise:{d}:[{f} + 0x{x}]", .{ + .indirect => |pl| try w.print("[{s} + 0x{x}]", .{ @tagName(pl.reg), pl.off }), + .indirect_load_frame => |pl| try w.print("[[{} + 0x{x}]]", .{ pl.index, pl.off }), + .load_frame => |pl| try w.print("[{} + 0x{x}]", .{ pl.index, pl.off }), + .lea_frame => |pl| try w.print("{} + 0x{x}", .{ pl.index, pl.off }), + .load_nav => |pl| try w.print("[nav:{d}]", .{@intFromEnum(pl)}), + .lea_nav => |pl| try w.print("nav:{d}", .{@intFromEnum(pl)}), + .load_uav => |pl| try w.print("[uav:{d}]", .{@intFromEnum(pl.val)}), + .lea_uav => |pl| try w.print("uav:{d}", .{@intFromEnum(pl.val)}), + .load_lazy_sym => |pl| try w.print("[lazy:{s}:{d}]", .{ @tagName(pl.kind), @intFromEnum(pl.ty) }), + .lea_lazy_sym => |pl| try w.print("lazy:{s}:{d}", .{ @tagName(pl.kind), @intFromEnum(pl.ty) }), + .load_extern_func => |pl| try w.print("[extern:{d}]", .{@intFromEnum(pl)}), + .lea_extern_func => |pl| try w.print("extern:{d}", .{@intFromEnum(pl)}), + .elementwise_args => |pl| try w.print("elementwise:{d}:[{} + 0x{x}]", .{ pl.regs, pl.frame_index, pl.frame_off, }), - .reserved_frame => |pl| try bw.print("(dead:{f})", .{pl}), - .air_ref => |pl| try bw.print("(air:0x{x})", .{@intFromEnum(pl)}), + .reserved_frame => |pl| try w.print("(dead:{})", .{pl}), + .air_ref => |pl| try w.print("(air:0x{x})", .{@intFromEnum(pl)}), } } }; @@ -2026,7 +2026,7 @@ fn gen( .{}, ); self.ret_mcv.long = .{ .load_frame = .{ .index = frame_index } }; - tracking_log.debug("spill {f} to {f}", .{ self.ret_mcv.long, frame_index }); + tracking_log.debug("spill {f} to {}", .{ self.ret_mcv.long, frame_index }); }, else => unreachable, } diff --git a/src/arch/x86_64/bits.zig b/src/arch/x86_64/bits.zig index 2e9b1f7f21291b982b42b450c341ec4531d21526..16361acb4d8107d81651e089ff2eefc6d5282a63 100644 --- a/src/arch/x86_64/bits.zig +++ b/src/arch/x86_64/bits.zig @@ -721,6 +721,12 @@ pub const FrameIndex = enum(u32) { call_frame, // Other indices are used for local variable stack slots _, + + pub const named_count = @typeInfo(FrameIndex).@"enum".fields.len; + + pub fn isNamed(fi: FrameIndex) bool { + return @intFromEnum(fi) < named_count; + } }; pub const FrameAddr = struct { index: FrameIndex, off: i32 = 0 }; diff --git a/src/arch/x86_64/encoder.zig b/src/arch/x86_64/encoder.zig index 59938990f6ee7c43c8f8b8817b62ab76c6dbb7bd..43d23af5fc3dd67ec62dceaff5512ff5a3116235 100644 --- a/src/arch/x86_64/encoder.zig +++ b/src/arch/x86_64/encoder.zig @@ -259,7 +259,7 @@ pub const Instruction = struct { switch (sib.base) { .none => any = false, .reg => |reg| try w.print("{s}", .{@tagName(reg)}), - .frame => |frame_index| try w.print("{f}", .{frame_index}), + .frame => |frame_index| try w.print("{}", .{frame_index}), .table => try w.print("Table", .{}), .rip_inst => |inst_index| try w.print("RipInst({d})", .{inst_index}), .nav => |nav| try w.print("Nav({d})", .{@intFromEnum(nav)}), diff --git a/src/link.zig b/src/link.zig index 41b184d79292f31ea7c1b77900c450f43d66303f..0fbd4b28cfdb359d7923acb633d6960320489d78 100644 --- a/src/link.zig +++ b/src/link.zig @@ -838,8 +838,10 @@ pub const File = struct { const cached_pp_file_path = the_key.status.success.object_path; cached_pp_file_path.root_dir.handle.copyFile(cached_pp_file_path.sub_path, emit.root_dir.handle, emit.sub_path, .{}) catch |err| { const diags = &base.comp.link_diags; - return diags.fail("failed to copy '{f'}' to '{f'}': {s}", .{ - @as(Path, cached_pp_file_path), @as(Path, emit), @errorName(err), + return diags.fail("failed to copy '{f}' to '{f}': {s}", .{ + std.fmt.alt(@as(Path, cached_pp_file_path), .formatEscapeChar), + std.fmt.alt(@as(Path, emit), .formatEscapeChar), + @errorName(err), }); }; return; @@ -2086,14 +2088,14 @@ fn resolvePathInputLib( }) { var file = test_path.root_dir.handle.openFile(test_path.sub_path, .{}) catch |err| switch (err) { error.FileNotFound => return .no_match, - else => |e| fatal("unable to search for {s} library '{f'}': {s}", .{ - @tagName(link_mode), test_path, @errorName(e), + else => |e| fatal("unable to search for {s} library '{f}': {s}", .{ + @tagName(link_mode), std.fmt.alt(test_path, .formatEscapeChar), @errorName(e), }), }; errdefer file.close(); try ld_script_bytes.resize(gpa, @max(std.elf.MAGIC.len, std.elf.ARMAG.len)); - const n = file.preadAll(ld_script_bytes.items, 0) catch |err| fatal("failed to read '{f'}': {s}", .{ - test_path, @errorName(err), + const n = file.preadAll(ld_script_bytes.items, 0) catch |err| fatal("failed to read '{f}': {s}", .{ + std.fmt.alt(test_path, .formatEscapeChar), @errorName(err), }); const buf = ld_script_bytes.items[0..n]; if (mem.startsWith(u8, buf, std.elf.MAGIC) or mem.startsWith(u8, buf, std.elf.ARMAG)) { diff --git a/src/link/C.zig b/src/link/C.zig index 6f32857c1ed9c826a531032f7aed9e6d53ae33bd..5ffb0fa986e1a45dd809793aa85ec9a37853f7c9 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -503,8 +503,8 @@ pub fn flush(self: *C, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.P var fw = file.writer(&.{}); var w = &fw.interface; w.writeVecAll(f.all_buffers.items) catch |err| switch (err) { - error.WriteFailed => return diags.fail("failed to write to '{f'}': {s}", .{ - self.base.emit, @errorName(fw.err.?), + error.WriteFailed => return diags.fail("failed to write to '{f}': {s}", .{ + std.fmt.alt(self.base.emit, .formatEscapeChar), @errorName(fw.err.?), }), }; } diff --git a/src/main.zig b/src/main.zig index 99cdf887f4a4e8d4d15809b6c4f524a1464e9790..f639630604586bf3817b101c10ed03b250bc9036 100644 --- a/src/main.zig +++ b/src/main.zig @@ -6964,7 +6964,9 @@ fn cmdFetch( std.log.info("resolved ref '{s}' to commit {s}", .{ target_ref, latest_commit_hex }); // include the original refspec in a query parameter, could be used to check for updates - uri.query = .{ .percent_encoded = try std.fmt.allocPrint(arena, "ref={f%}", .{fragment}) }; + uri.query = .{ .percent_encoded = try std.fmt.allocPrint(arena, "ref={f}", .{ + std.fmt.alt(fragment, .formatEscaped), + }) }; } else { std.log.info("resolved to commit {s}", .{latest_commit_hex}); } diff --git a/src/print_value.zig b/src/print_value.zig index ae9ceb1a16b2a8e55b8cc9e0733b71a33723bfc0..e0a489ee4076cc5007156796773f4b0848a0e090 100644 --- a/src/print_value.zig +++ b/src/print_value.zig @@ -77,7 +77,7 @@ pub fn print( .func => |func| try writer.print("(function '{f}')", .{ip.getNav(func.owner_nav).name.fmt(ip)}), .int => |int| switch (int.storage) { inline .u64, .i64 => |x| try writer.print("{d}", .{x}), - .big_int => |x| try writer.print("{fd}", .{x}), + .big_int => |x| try writer.print("{d}", .{x}), .lazy_align => |ty| if (opt_sema != null) { const a = try Type.fromInterned(ty).abiAlignmentSema(pt); try writer.print("{d}", .{a.toByteUnits() orelse 0}); -- 2.54.0