Commit 7e2a26c0c4

Andrew Kelley <andrew@ziglang.org>
2025-07-07 00:51:44
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.
1 parent 5378fdb
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 });
                         }
                     }
                 }
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});
         },
     };
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}),
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| {
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;
-            }
-            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;
+        .@"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);
             }
-            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());
 }
 
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,...)" {
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.
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, .{});
 }
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| ==
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)"));
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| {
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});
         }
 
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);
-
-        var buf: [100]u8 = undefined;
-        try expectFmt(
-            try bufPrint(buf[0..], "array: [3]u8@{x}\n", .{@intFromPtr(&value)}),
-            "array: {*}\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);
 
-    {
-        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" {
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});
+            }
         }
     };
 
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 {
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' });
         },
     };
 }
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.
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;
 }
 
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 };
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,
         }
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)}),
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.?),
         }),
     };
 }
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" }) };
         }
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;
         }
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 },
                         );
                     }
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 }),
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)) {
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});
         }
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});