-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Description
Zig Version
0.11.0-dev.1836+28364166e
Steps to Reproduce and Observed Output
This suggestion is similar to #12807 but goes a bit further: While the example from that pull requests yields an error return trace these days, the following code snippet does not:
const std = @import("std");
fn foo() !void {
return error.Foo;
}
fn bar() !void {
try foo();
}
fn baz() void {
bar() catch |err| {
std.debug.print("\nSome additional debug information: [...] Error: {any}\n", .{err});
unreachable;
};
}
test {
baz();
}
This yields:
$ zig test test.zig
Test [1/1] test_0...
Some additional debug information: [...] Error: error.Foo
thread 205313 panic: reached unreachable code
/home/user/zig-test/test.zig:12:9: 0x20bef3 in baz (test)
unreachable;
^
/home/user/zig-test/test.zig:16:8: 0x20be78 in test_0 (test)
baz();
^
/home/user/opt/zig/lib/test_runner.zig:66:28: 0x20d85e in main (test)
} else test_fn.func();
^
/home/user/opt/zig/lib/std/start.zig:607:22: 0x20c7b5 in posixCallMainAndExit (test)
root.main();
^
/home/user/opt/zig/lib/std/start.zig:376:5: 0x20c261 in _start (test)
@call(.never_inline, posixCallMainAndExit, .{});
^
error: the following test command crashed:
/home/user/zig-test/zig-cache/o/2fb76e24211cebd8013e87fd8c71dd2f/test
i.e. no error return trace.
Expected Output
I would expect the output to contain an error return trace, like so:
$ zig test test.zig
Test [1/1] test_0... thread 209915 panic: attempt to unwrap error: Foo
/home/user/zig-test/test.zig:4:5: 0x20c6c8 in foo (test)
return error.Foo;
^
/home/user/zig-test/test.zig:7:5: 0x20c7d3 in bar (test)
try foo();
^
/home/user/zig-test/test.zig:12:9: 0x20bdd0 in baz (test)
unreachable;
^
/home/user/zig-test/test.zig:13:8: 0x20bd78 in test_0 (test)
baz();
^
/home/user/opt/zig/lib/test_runner.zig:66:28: 0x20d6ce in main (test)
} else test_fn.func();
^
/home/user/opt/zig/lib/std/start.zig:607:22: 0x20c695 in posixCallMainAndExit (test)
root.main();
^
/home/user/opt/zig/lib/std/start.zig:376:5: 0x20c141 in _start (test)
@call(.never_inline, posixCallMainAndExit, .{});
^
error: the following test command crashed:
/home/user/zig-test/zig-cache/o/2fb76e24211cebd8013e87fd8c71dd2f/test
I would expect a similar output if I don't even capture the error (i.e. catch { … } instead of catch |err| { … }).
Interestingly, if in the code snippet I replace unreachable; with switch (err) { else => unreachable, } I do get an error return trace, which is probably thanks to the aforementioned pull request.
[Add-on] What about if unreachable is invoked only indirectly?
Ideally, outputting an error return trace would also work if I outsource the catch block to a noreturn function which then calls unreachable: E.g.
const std = @import("std");
fn foo() !void {
return error.Foo;
}
fn bar() !void {
try foo();
}
fn baz() void {
bar() catch |err| failWithDebugInfo(err);
}
test {
baz();
}
fn failWithDebugInfo(err: anyerror) noreturn {
std.debug.print("\nSome additional debug information: [...]\nError: {any}", .{err});
unreachable; // or @panic("…")
}
This would be really helpful if one wants to wrap unreachable or @panic in a custom function that, like above, outputs additional information. I'm not sure if this possible, though, so please consider this an optional add-on to my feature request. FWIW, I tried using the aforementioned workaround of employing a switch statement:
const std = @import("std");
fn foo() !void {
return error.Foo;
}
fn bar() !void {
try foo();
}
fn baz() void {
bar() catch |err| failWithDebugInfo(err);
}
test {
baz();
}
fn failWithDebugInfo(err: anyerror) noreturn {
std.debug.print("\nSome additional debug information: [...]\nError: {any}", .{err});
switch (err) {
else => unreachable,
}
}
But this yields an incomplete error return trace (it doesn't tell me where error.Foo came from):
$ zig test test.zig
Test [1/1] test_0...
Some additional debug information: [...]
Error: error.Foothread 239739 panic: attempt to unwrap error: Foo
/home/user/zigfish-test/test.zig:18:17: 0x20c92f in failWithDebugInfo (test)
else => unreachable,
^
/home/user/zigfish-test/test.zig:10:40: 0x20bedc in baz (test)
bar() catch |err| failWithDebugInfo(err);
^
/home/user/zigfish-test/test.zig:13:8: 0x20be88 in test_0 (test)
baz();
^
/home/user/opt/zig/lib/test_runner.zig:66:28: 0x20d7be in main (test)
} else test_fn.func();
^
/home/user/opt/zig/lib/std/start.zig:607:22: 0x20c7a5 in posixCallMainAndExit (test)
root.main();
^
/home/user/opt/zig/lib/std/start.zig:376:5: 0x20c251 in _start (test)
@call(.never_inline, posixCallMainAndExit, .{});
^
error: the following test command crashed:
/home/user/zigfish-test/zig-cache/o/2fb76e24211cebd8013e87fd8c71dd2f/test
[Add-on 2] Allow for @panic instead of unreachable
I.e. all the code snippets from above should also yield an error return trace when unreachable is replaced with @panic("Foo").
Again, I'm not familiar with Zig internals, so I can't speak to the feasibility of implementing this behavior but it's at least what I would expect from a user perspective.