sdt.testing.expectEqualDeep error

By working on upgrade to zig 0.15.2 standard of gcc-arm-to-zig project, I encountered a weird error while running tests.

└─ run test
 └─ compile test Debug native 1 errors
path/zig-x86_64-linux-0.15.2/lib/std/Io/Writer.zig:1200:9: error: ambiguous format string; specify {f} to call format method, or {any} to skip it
    @compileError(“ambiguous format string; specify {f} to call format method, or {any} to skip it”);
    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
referenced by:
   print__anon_17990: path/zig-x86_64-linux-0.15.2/lib/std/Io/Writer.zig:700:25
   print__anon_17985: path/zig-x86_64-linux-0.15.2/lib/std/debug.zig:231:23

I rapidly isolated the responsible test of this error:

test "Valid Conversion" {
      const some_query = std.Target.Query{
          .cpu_arch = .thumb,
          .os_tag = .freestanding,
          .abi = .eabihf,
          .cpu_model = std.Target.Query.CpuModel{ .explicit = &std.Target.arm.cpu.cortex_m7 },
          .cpu_features_add = std.Target.arm.featureSet(&[_]std.Target.arm.Feature{std.Target.arm.Feature.fp_armv8d16sp}),
      };
      const my_target: Target = .{ .cpu = gcc.cpu.@"cortex-m7", .instruction_set = .thumb, .float_abi = .hard, .fpu = gcc.fpu.@"fpv5-sp-d16" };
      try std.testing.expectEqual(some_query, try my_target.toTargetQuery());
}

After digging deep into, I finally isolated the std.Target member behind this weird error. It can be reproduce with this test:

test "expectEqualDeep error"{
    try std.testing.expectEqualDeep(null, std.Target.Os.WindowsVersion.win10);
}

The windows version value doesn’t matter, nor the other member of expectation test.I tryed to understand why this fail but without success. Before opening a ticket, I checked if zig main branch encounter the same behavior but no. So, for now, I simply replaced expectEqualDeep by expectEqual.But If someone can explain me where this behavior come from, I’m in.

PS: I’m not English speaker, sorry for the poor quality of my text.

Run it with -freference-trace and it will print the whole section for this:

Somewhere previous to the call to debug, there will be a format string that uses only {} without any specifier, in that place the code needs to either use the {f} or {any} format string. This is needed because Zig made calling of format functions on the type explicit, before an empty format string just called the format function if it was present. (which meant that adding or removing a format function could silently change the output somewhere else, to prevent that it was made explicit)

So using your provided reproduction (thanks for that) we get:
zig test formattingerror.zig -freference-trace

/home/sze/development/workspace/zig/active/zig-x86_64-linux-0.15.2/lib/std/Io/Writer.zig:1200:9: error: ambiguous format string; specify {f} to call format method, or {any} to skip it
        @compileError("ambiguous format string; specify {f} to call format method, or {any} to skip it");
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
referenced by:
    print__anon_1126: /home/sze/development/workspace/zig/active/zig-x86_64-linux-0.15.2/lib/std/Io/Writer.zig:700:25
    print__anon_950: /home/sze/development/workspace/zig/active/zig-x86_64-linux-0.15.2/lib/std/debug.zig:231:23
    print__anon_913: /home/sze/development/workspace/zig/active/zig-x86_64-linux-0.15.2/lib/std/testing.zig:48:24
    expectEqualDeepInner__anon_685: /home/sze/development/workspace/zig/active/zig-x86_64-linux-0.15.2/lib/std/testing.zig:766:22
    expectEqualDeep [inlined]: /home/sze/development/workspace/zig/active/zig-x86_64-linux-0.15.2/lib/std/testing.zig:732:32
    expectEqualDeepInner__anon_508: /home/sze/development/workspace/zig/active/zig-x86_64-linux-0.15.2/lib/std/testing.zig:874:40
    expectEqualDeep [inlined]: /home/sze/development/workspace/zig/active/zig-x86_64-linux-0.15.2/lib/std/testing.zig:732:32
    test.expectEqualDeep error: formattingerror.zig:4:36

Walking back the reference-trace and looking at the code you land at:
zig-x86_64-linux-0.15.2/lib/std/testing.zig:766:22

        .bool,
        .int,
        .float,
        .comptime_float,
        .comptime_int,
        .enum_literal,
        .@"enum",
        .@"fn",
        .error_set,
        => {
            if (actual != expected) {
                print("expected {}, found {}\n", .{ expected, actual });
                return error.TestExpectedEqual;
            }
        },

On master this code looks like this:

if (actual != expected) {
    print("expected {any}, found {any}\n", .{ expected, actual });
    return error.TestExpectedEqual;
}

Because on master it explicitly specifies the format, it is no longer ambiguous for enums that have a format function like std.Target.Os.WindowsVersion.


Here is another reproduction:

const std = @import("std");

const PlainEnum = enum {
    one,
    two,

    pub fn format(
        self: @This(),
        writer: *std.Io.Writer,
    ) std.Io.Writer.Error!void {
        try writer.print("Plain {t}", .{self});
    }
};

test "expectEqualDeep error" {
    try std.testing.expectEqualDeep(PlainEnum.one, PlainEnum.two);
}

If you remove the format function then it works.
Basically it is fixed in master, so I don’t think there is a reason to create an issue for this.

5 Likes

Thank you for your explication, it’s clear now.

1 Like