Commit 1480c42806

Jonathan Marler <johnnymarler@gmail.com>
2021-01-03 10:20:37
require specifier for arrayish types
1 parent 6a5a638
lib/std/build.zig
@@ -739,7 +739,7 @@ pub const Builder = struct {
                 return args.default_target;
             },
             else => |e| {
-                warn("Unable to parse target '{}': {s}\n\n", .{ triple, @errorName(e) });
+                warn("Unable to parse target '{s}': {s}\n\n", .{ triple, @errorName(e) });
                 self.markInvalidUserInput();
                 return args.default_target;
             },
lib/std/fmt.zig
@@ -69,6 +69,7 @@ pub const FormatOptions = struct {
 /// - `c`: output integer as an ASCII character. Integer type must have 8 bits at max.
 /// - `u`: output integer as an UTF-8 sequence. Integer type must have 21 bits at max.
 /// - `*`: output the address of the value instead of the value itself.
+/// - `any`: output a value of any type using its default format
 ///
 /// If a formatted user type contains a function of the type
 /// ```
@@ -387,17 +388,32 @@ pub fn formatAddress(value: anytype, options: FormatOptions, writer: anytype) @T
                 return;
             }
         },
-        .Array => |info| {
-            try writer.writeAll(@typeName(info.child) ++ "@");
-            try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, writer);
-            return;
-        },
         else => {},
     }
 
     @compileError("Cannot format non-pointer type " ++ @typeName(T) ++ " with * specifier");
 }
 
+// This ANY const is a workaround for: https://github.com/ziglang/zig/issues/7948
+const ANY = "any";
+
+fn defaultSpec(comptime T: type) [:0]const u8 {
+    switch (@typeInfo(T)) {
+        .Array => |_| return ANY,
+        .Pointer => |ptr_info| switch (ptr_info.size) {
+            .One => switch (@typeInfo(ptr_info.child)) {
+                .Array => |_| return "*",
+                else => {},
+            },
+            .Many, .C => return "*",
+            .Slice => return ANY,
+        },
+        .Optional => |info| return defaultSpec(info.child),
+        else => {},
+    }
+    return "";
+}
+
 pub fn formatType(
     value: anytype,
     comptime fmt: []const u8,
@@ -405,18 +421,19 @@ pub fn formatType(
     writer: anytype,
     max_depth: usize,
 ) @TypeOf(writer).Error!void {
-    if (comptime std.mem.eql(u8, fmt, "*")) {
+    const actual_fmt = comptime if (std.mem.eql(u8, fmt, ANY)) defaultSpec(@TypeOf(value)) else fmt;
+    if (comptime std.mem.eql(u8, actual_fmt, "*")) {
         return formatAddress(value, options, writer);
     }
 
     const T = @TypeOf(value);
     if (comptime std.meta.trait.hasFn("format")(T)) {
-        return try value.format(fmt, options, writer);
+        return try value.format(actual_fmt, options, writer);
     }
 
     switch (@typeInfo(T)) {
         .ComptimeInt, .Int, .ComptimeFloat, .Float => {
-            return formatValue(value, fmt, options, writer);
+            return formatValue(value, actual_fmt, options, writer);
         },
         .Void => {
             return formatBuf("void", options, writer);
@@ -426,16 +443,16 @@ pub fn formatType(
         },
         .Optional => {
             if (value) |payload| {
-                return formatType(payload, fmt, options, writer, max_depth);
+                return formatType(payload, actual_fmt, options, writer, max_depth);
             } else {
                 return formatBuf("null", options, writer);
             }
         },
         .ErrorUnion => {
             if (value) |payload| {
-                return formatType(payload, fmt, options, writer, max_depth);
+                return formatType(payload, actual_fmt, options, writer, max_depth);
             } else |err| {
-                return formatType(err, fmt, options, writer, max_depth);
+                return formatType(err, actual_fmt, options, writer, max_depth);
             }
         },
         .ErrorSet => {
@@ -461,7 +478,7 @@ pub fn formatType(
             }
 
             try writer.writeAll("(");
-            try formatType(@enumToInt(value), fmt, options, writer, max_depth);
+            try formatType(@enumToInt(value), actual_fmt, options, writer, max_depth);
             try writer.writeAll(")");
         },
         .Union => |info| {
@@ -475,7 +492,7 @@ pub fn formatType(
                 try writer.writeAll(" = ");
                 inline for (info.fields) |u_field| {
                     if (value == @field(UnionTagType, u_field.name)) {
-                        try formatType(@field(value, u_field.name), fmt, options, writer, max_depth - 1);
+                        try formatType(@field(value, u_field.name), ANY, options, writer, max_depth - 1);
                     }
                 }
                 try writer.writeAll(" }");
@@ -497,48 +514,54 @@ pub fn formatType(
                 }
                 try writer.writeAll(f.name);
                 try writer.writeAll(" = ");
-                try formatType(@field(value, f.name), fmt, options, writer, max_depth - 1);
+                try formatType(@field(value, f.name), ANY, options, writer, max_depth - 1);
             }
             try writer.writeAll(" }");
         },
         .Pointer => |ptr_info| switch (ptr_info.size) {
             .One => switch (@typeInfo(ptr_info.child)) {
                 .Array => |info| {
+                    if (actual_fmt.len == 0)
+                        @compileError("cannot format array ref without a specifier (i.e. {s} or {*})");
                     if (info.child == u8) {
-                        if (fmt.len > 0 and comptime mem.indexOfScalar(u8, "sxXeEzZ", fmt[0]) != null) {
-                            return formatText(value, fmt, options, writer);
+                        if (comptime mem.indexOfScalar(u8, "sxXeEzZ", actual_fmt[0]) != null) {
+                            return formatText(value, actual_fmt, options, writer);
                         }
                     }
-                    return format(writer, "{s}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value) });
+                    @compileError("Unknown format string: '" ++ actual_fmt ++ "'");
                 },
                 .Enum, .Union, .Struct => {
-                    return formatType(value.*, fmt, options, writer, max_depth);
+                    return formatType(value.*, actual_fmt, options, writer, max_depth);
                 },
                 else => return format(writer, "{s}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value) }),
             },
             .Many, .C => {
+                if (actual_fmt.len == 0)
+                    @compileError("cannot format pointer without a specifier (i.e. {s} or {*})");
                 if (ptr_info.sentinel) |sentinel| {
-                    return formatType(mem.span(value), fmt, options, writer, max_depth);
+                    return formatType(mem.span(value), actual_fmt, options, writer, max_depth);
                 }
                 if (ptr_info.child == u8) {
-                    if (fmt.len > 0 and comptime mem.indexOfScalar(u8, "sxXeEzZ", fmt[0]) != null) {
-                        return formatText(mem.span(value), fmt, options, writer);
+                    if (comptime mem.indexOfScalar(u8, "sxXeEzZ", actual_fmt[0]) != null) {
+                        return formatText(mem.span(value), actual_fmt, options, writer);
                     }
                 }
-                return format(writer, "{s}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value) });
+                @compileError("Unknown format string: '" ++ actual_fmt ++ "'");
             },
             .Slice => {
+                if (actual_fmt.len == 0)
+                    @compileError("cannot format slice without a specifier (i.e. {s} or {any})");
                 if (max_depth == 0) {
                     return writer.writeAll("{ ... }");
                 }
                 if (ptr_info.child == u8) {
-                    if (fmt.len > 0 and comptime mem.indexOfScalar(u8, "sxXeEzZ", fmt[0]) != null) {
-                        return formatText(value, fmt, options, writer);
+                    if (comptime mem.indexOfScalar(u8, "sxXeEzZ", actual_fmt[0]) != null) {
+                        return formatText(value, actual_fmt, options, writer);
                     }
                 }
                 try writer.writeAll("{ ");
                 for (value) |elem, i| {
-                    try formatType(elem, fmt, options, writer, max_depth - 1);
+                    try formatType(elem, actual_fmt, options, writer, max_depth - 1);
                     if (i != value.len - 1) {
                         try writer.writeAll(", ");
                     }
@@ -547,17 +570,19 @@ pub fn formatType(
             },
         },
         .Array => |info| {
+            if (actual_fmt.len == 0)
+                @compileError("cannot format array without a specifier (i.e. {s} or {any})");
             if (max_depth == 0) {
                 return writer.writeAll("{ ... }");
             }
             if (info.child == u8) {
-                if (fmt.len > 0 and comptime mem.indexOfScalar(u8, "sxXeEzZ", fmt[0]) != null) {
-                    return formatText(&value, fmt, options, writer);
+                if (comptime mem.indexOfScalar(u8, "sxXeEzZ", actual_fmt[0]) != null) {
+                    return formatText(&value, actual_fmt, options, writer);
                 }
             }
             try writer.writeAll("{ ");
             for (value) |elem, i| {
-                try formatType(elem, fmt, options, writer, max_depth - 1);
+                try formatType(elem, actual_fmt, options, writer, max_depth - 1);
                 if (i < value.len - 1) {
                     try writer.writeAll(", ");
                 }
@@ -568,7 +593,7 @@ pub fn formatType(
             try writer.writeAll("{ ");
             var i: usize = 0;
             while (i < info.len) : (i += 1) {
-                try formatValue(value[i], fmt, options, writer);
+                try formatValue(value[i], actual_fmt, options, writer);
                 if (i < info.len - 1) {
                     try writer.writeAll(", ");
                 }
@@ -1668,7 +1693,7 @@ test "slice" {
     {
         var int_slice = [_]u32{ 1, 4096, 391891, 1111111111 };
         var runtime_zero: usize = 0;
-        try expectFmt("int: { 1, 4096, 391891, 1111111111 }", "int: {}", .{int_slice[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..]});
lib/std/testing.zig
@@ -29,7 +29,7 @@ pub var zig_exe_path: []const u8 = undefined;
 /// and then aborts when actual_error_union is not expected_error.
 pub fn expectError(expected_error: anyerror, actual_error_union: anytype) void {
     if (actual_error_union) |actual_payload| {
-        std.debug.panic("expected error.{s}, found {}", .{ @errorName(expected_error), actual_payload });
+        std.debug.panic("expected error.{s}, found {any}", .{ @errorName(expected_error), actual_payload });
     } else |actual_error| {
         if (expected_error != actual_error) {
             std.debug.panic("expected error.{s}, found error.{s}", .{
@@ -88,7 +88,7 @@ pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) void {
                 },
                 .Slice => {
                     if (actual.ptr != expected.ptr) {
-                        std.debug.panic("expected slice ptr {}, found {}", .{ expected.ptr, actual.ptr });
+                        std.debug.panic("expected slice ptr {*}, found {*}", .{ expected.ptr, actual.ptr });
                     }
                     if (actual.len != expected.len) {
                         std.debug.panic("expected slice len {}, found {}", .{ expected.len, actual.len });
@@ -145,11 +145,11 @@ pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) void {
                 if (actual) |actual_payload| {
                     expectEqual(expected_payload, actual_payload);
                 } else {
-                    std.debug.panic("expected {}, found null", .{expected_payload});
+                    std.debug.panic("expected {any}, found null", .{expected_payload});
                 }
             } else {
                 if (actual) |actual_payload| {
-                    std.debug.panic("expected null, found {}", .{actual_payload});
+                    std.debug.panic("expected null, found {any}", .{actual_payload});
                 }
             }
         },
@@ -159,11 +159,11 @@ pub fn expectEqual(expected: anytype, actual: @TypeOf(expected)) void {
                 if (actual) |actual_payload| {
                     expectEqual(expected_payload, actual_payload);
                 } else |actual_err| {
-                    std.debug.panic("expected {}, found {}", .{ expected_payload, actual_err });
+                    std.debug.panic("expected {any}, found {}", .{ expected_payload, actual_err });
                 }
             } else |expected_err| {
                 if (actual) |actual_payload| {
-                    std.debug.panic("expected {}, found {}", .{ expected_err, actual_payload });
+                    std.debug.panic("expected {}, found {any}", .{ expected_err, actual_payload });
                 } else |actual_err| {
                     expectEqual(expected_err, actual_err);
                 }
@@ -279,7 +279,7 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const
     var i: usize = 0;
     while (i < expected.len) : (i += 1) {
         if (!std.meta.eql(expected[i], actual[i])) {
-            std.debug.panic("index {} incorrect. expected {}, found {}", .{ i, expected[i], actual[i] });
+            std.debug.panic("index {} incorrect. expected {any}, found {any}", .{ i, expected[i], actual[i] });
         }
     }
 }
src/codegen.zig
@@ -2223,7 +2223,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
                             writeInt(u32, try self.code.addManyAsArray(4), Instruction.cmp(.al, reg, op).toU32());
                             break :blk .ne;
                         },
-                        else => return self.fail(inst.base.src, "TODO implement condbr {} when condition is {}", .{ self.target.cpu.arch, @tagName(cond) }),
+                        else => return self.fail(inst.base.src, "TODO implement condbr {} when condition is {s}", .{ self.target.cpu.arch, @tagName(cond) }),
                     };
 
                     const reloc = Reloc{
src/Module.zig
@@ -2400,7 +2400,7 @@ fn getAnonTypeName(self: *Module, scope: *Scope, base_token: std.zig.ast.TokenIn
         else => unreachable,
     };
     const loc = tree.tokenLocationLoc(0, tree.token_locs[base_token]);
-    return std.fmt.allocPrint(self.gpa, "{}:{}:{}", .{ base_name, loc.line, loc.column });
+    return std.fmt.allocPrint(self.gpa, "{s}:{}:{}", .{ base_name, loc.line, loc.column });
 }
 
 fn getNextAnonNameIndex(self: *Module) usize {
src/zir_sema.zig
@@ -1832,7 +1832,7 @@ fn zirBitwise(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*In
     const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
 
     if (!is_int) {
-        return mod.fail(scope, inst.base.src, "invalid operands to binary bitwise expression: '{}' and '{}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) });
+        return mod.fail(scope, inst.base.src, "invalid operands to binary bitwise expression: '{s}' and '{s}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) });
     }
 
     if (casted_lhs.value()) |lhs_val| {
test/standalone/cat/main.zig
@@ -41,6 +41,6 @@ pub fn main() !void {
 }
 
 fn usage(exe: []const u8) !void {
-    warn("Usage: {} [FILE]...\n", .{exe});
+    warn("Usage: {s} [FILE]...\n", .{exe});
     return error.Invalid;
 }
test/cli.zig
@@ -51,9 +51,9 @@ fn unwrapArg(arg: UnwrapArgError![]u8) UnwrapArgError![]u8 {
 }
 
 fn printCmd(cwd: []const u8, argv: []const []const u8) void {
-    std.debug.warn("cd {} && ", .{cwd});
+    std.debug.warn("cd {s} && ", .{cwd});
     for (argv) |arg| {
-        std.debug.warn("{} ", .{arg});
+        std.debug.warn("{s} ", .{arg});
     }
     std.debug.warn("\n", .{});
 }
@@ -75,14 +75,14 @@ fn exec(cwd: []const u8, expect_0: bool, argv: []const []const u8) !ChildProcess
             if ((code != 0) == expect_0) {
                 std.debug.warn("The following command exited with error code {}:\n", .{code});
                 printCmd(cwd, argv);
-                std.debug.warn("stderr:\n{}\n", .{result.stderr});
+                std.debug.warn("stderr:\n{s}\n", .{result.stderr});
                 return error.CommandFailed;
             }
         },
         else => {
             std.debug.warn("The following command terminated unexpectedly:\n", .{});
             printCmd(cwd, argv);
-            std.debug.warn("stderr:\n{}\n", .{result.stderr});
+            std.debug.warn("stderr:\n{s}\n", .{result.stderr});
             return error.CommandFailed;
         },
     }