mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 05:44:20 +00:00
This implement trace "popping" for correctly handled errors within
`catch { ... }` and `else { ... }` blocks.
When breaking from these blocks with any non-error, we pop the error
trace frames corresponding to the operand. When breaking with an error,
we preserve the frames so that error traces "chain" together as usual.
```zig
fn foo(cond1: bool, cond2: bool) !void {
bar() catch {
if (cond1) {
// If baz() result is a non-error, pop the error trace frames from bar()
// If baz() result is an error, leave the bar() frames on the error trace
return baz();
} else if (cond2) {
// If we break/return an error, then leave the error frames from bar() on the error trace
return error.Foo;
}
};
// An error returned from here does not include bar()'s error frames in the trace
return error.Bar;
}
```
Notice that if foo() does not return an error it, it leaves no extra
frames on the error trace.
This is piece (1/3) of https://github.com/ziglang/zig/issues/1923#issuecomment-1218495574
390 lines
10 KiB
Zig
390 lines
10 KiB
Zig
const std = @import("std");
|
|
const os = std.os;
|
|
const tests = @import("tests.zig");
|
|
|
|
pub fn addCases(cases: *tests.StackTracesContext) void {
|
|
cases.addCase(.{
|
|
.name = "return",
|
|
.source =
|
|
\\pub fn main() !void {
|
|
\\ return error.TheSkyIsFalling;
|
|
\\}
|
|
,
|
|
.Debug = .{
|
|
.expect =
|
|
\\error: TheSkyIsFalling
|
|
\\source.zig:2:5: [address] in main (test)
|
|
\\ return error.TheSkyIsFalling;
|
|
\\ ^
|
|
\\
|
|
,
|
|
},
|
|
.ReleaseSafe = .{
|
|
.exclude_os = .{
|
|
.windows, // TODO
|
|
.linux, // defeated by aggressive inlining
|
|
},
|
|
.expect =
|
|
\\error: TheSkyIsFalling
|
|
\\source.zig:2:5: [address] in [function]
|
|
\\ return error.TheSkyIsFalling;
|
|
\\ ^
|
|
\\
|
|
,
|
|
},
|
|
.ReleaseFast = .{
|
|
.expect =
|
|
\\error: TheSkyIsFalling
|
|
\\
|
|
,
|
|
},
|
|
.ReleaseSmall = .{
|
|
.expect =
|
|
\\error: TheSkyIsFalling
|
|
\\
|
|
,
|
|
},
|
|
});
|
|
|
|
cases.addCase(.{
|
|
.name = "try return",
|
|
.source =
|
|
\\fn foo() !void {
|
|
\\ return error.TheSkyIsFalling;
|
|
\\}
|
|
\\
|
|
\\pub fn main() !void {
|
|
\\ try foo();
|
|
\\}
|
|
,
|
|
.Debug = .{
|
|
.expect =
|
|
\\error: TheSkyIsFalling
|
|
\\source.zig:2:5: [address] in foo (test)
|
|
\\ return error.TheSkyIsFalling;
|
|
\\ ^
|
|
\\source.zig:6:5: [address] in main (test)
|
|
\\ try foo();
|
|
\\ ^
|
|
\\
|
|
,
|
|
},
|
|
.ReleaseSafe = .{
|
|
.exclude_os = .{
|
|
.windows, // TODO
|
|
},
|
|
.expect =
|
|
\\error: TheSkyIsFalling
|
|
\\source.zig:2:5: [address] in [function]
|
|
\\ return error.TheSkyIsFalling;
|
|
\\ ^
|
|
\\source.zig:6:5: [address] in [function]
|
|
\\ try foo();
|
|
\\ ^
|
|
\\
|
|
,
|
|
},
|
|
.ReleaseFast = .{
|
|
.expect =
|
|
\\error: TheSkyIsFalling
|
|
\\
|
|
,
|
|
},
|
|
.ReleaseSmall = .{
|
|
.expect =
|
|
\\error: TheSkyIsFalling
|
|
\\
|
|
,
|
|
},
|
|
});
|
|
|
|
cases.addCase(.{
|
|
.name = "try return + handled catch/if-else",
|
|
.source =
|
|
\\fn foo() !void {
|
|
\\ return error.TheSkyIsFalling;
|
|
\\}
|
|
\\
|
|
\\pub fn main() !void {
|
|
\\ foo() catch {}; // should not affect error trace
|
|
\\ if (foo()) |_| {} else |_| {
|
|
\\ // should also not affect error trace
|
|
\\ }
|
|
\\ try foo();
|
|
\\}
|
|
,
|
|
.Debug = .{
|
|
.expect =
|
|
\\error: TheSkyIsFalling
|
|
\\source.zig:2:5: [address] in foo (test)
|
|
\\ return error.TheSkyIsFalling;
|
|
\\ ^
|
|
\\source.zig:10:5: [address] in main (test)
|
|
\\ try foo();
|
|
\\ ^
|
|
\\
|
|
,
|
|
},
|
|
.ReleaseSafe = .{
|
|
.exclude_os = .{
|
|
.windows, // TODO
|
|
.linux, // defeated by aggressive inlining
|
|
},
|
|
.expect =
|
|
\\error: TheSkyIsFalling
|
|
\\source.zig:2:5: [address] in [function]
|
|
\\ return error.TheSkyIsFalling;
|
|
\\ ^
|
|
\\source.zig:10:5: [address] in [function]
|
|
\\ try foo();
|
|
\\ ^
|
|
\\
|
|
,
|
|
},
|
|
.ReleaseFast = .{
|
|
.expect =
|
|
\\error: TheSkyIsFalling
|
|
\\
|
|
,
|
|
},
|
|
.ReleaseSmall = .{
|
|
.expect =
|
|
\\error: TheSkyIsFalling
|
|
\\
|
|
,
|
|
},
|
|
});
|
|
|
|
cases.addCase(.{
|
|
.name = "try return from within catch",
|
|
.source =
|
|
\\fn foo() !void {
|
|
\\ return error.TheSkyIsFalling;
|
|
\\}
|
|
\\
|
|
\\fn bar() !void {
|
|
\\ return error.AndMyCarIsOutOfGas;
|
|
\\}
|
|
\\
|
|
\\pub fn main() !void {
|
|
\\ foo() catch { // error trace should include foo()
|
|
\\ try bar();
|
|
\\ };
|
|
\\}
|
|
,
|
|
.Debug = .{
|
|
.expect =
|
|
\\error: AndMyCarIsOutOfGas
|
|
\\source.zig:2:5: [address] in foo (test)
|
|
\\ return error.TheSkyIsFalling;
|
|
\\ ^
|
|
\\source.zig:6:5: [address] in bar (test)
|
|
\\ return error.AndMyCarIsOutOfGas;
|
|
\\ ^
|
|
\\source.zig:11:9: [address] in main (test)
|
|
\\ try bar();
|
|
\\ ^
|
|
\\
|
|
,
|
|
},
|
|
.ReleaseSafe = .{
|
|
.exclude_os = .{
|
|
.windows, // TODO
|
|
},
|
|
.expect =
|
|
\\error: AndMyCarIsOutOfGas
|
|
\\source.zig:2:5: [address] in [function]
|
|
\\ return error.TheSkyIsFalling;
|
|
\\ ^
|
|
\\source.zig:6:5: [address] in [function]
|
|
\\ return error.AndMyCarIsOutOfGas;
|
|
\\ ^
|
|
\\source.zig:11:9: [address] in [function]
|
|
\\ try bar();
|
|
\\ ^
|
|
\\
|
|
,
|
|
},
|
|
.ReleaseFast = .{
|
|
.expect =
|
|
\\error: AndMyCarIsOutOfGas
|
|
\\
|
|
,
|
|
},
|
|
.ReleaseSmall = .{
|
|
.expect =
|
|
\\error: AndMyCarIsOutOfGas
|
|
\\
|
|
,
|
|
},
|
|
});
|
|
|
|
cases.addCase(.{
|
|
.name = "try return from within if-else",
|
|
.source =
|
|
\\fn foo() !void {
|
|
\\ return error.TheSkyIsFalling;
|
|
\\}
|
|
\\
|
|
\\fn bar() !void {
|
|
\\ return error.AndMyCarIsOutOfGas;
|
|
\\}
|
|
\\
|
|
\\pub fn main() !void {
|
|
\\ if (foo()) |_| {} else |_| { // error trace should include foo()
|
|
\\ try bar();
|
|
\\ }
|
|
\\}
|
|
,
|
|
.Debug = .{
|
|
.expect =
|
|
\\error: AndMyCarIsOutOfGas
|
|
\\source.zig:2:5: [address] in foo (test)
|
|
\\ return error.TheSkyIsFalling;
|
|
\\ ^
|
|
\\source.zig:6:5: [address] in bar (test)
|
|
\\ return error.AndMyCarIsOutOfGas;
|
|
\\ ^
|
|
\\source.zig:11:9: [address] in main (test)
|
|
\\ try bar();
|
|
\\ ^
|
|
\\
|
|
,
|
|
},
|
|
.ReleaseSafe = .{
|
|
.exclude_os = .{
|
|
.windows, // TODO
|
|
},
|
|
.expect =
|
|
\\error: AndMyCarIsOutOfGas
|
|
\\source.zig:2:5: [address] in [function]
|
|
\\ return error.TheSkyIsFalling;
|
|
\\ ^
|
|
\\source.zig:6:5: [address] in [function]
|
|
\\ return error.AndMyCarIsOutOfGas;
|
|
\\ ^
|
|
\\source.zig:11:9: [address] in [function]
|
|
\\ try bar();
|
|
\\ ^
|
|
\\
|
|
,
|
|
},
|
|
.ReleaseFast = .{
|
|
.expect =
|
|
\\error: AndMyCarIsOutOfGas
|
|
\\
|
|
,
|
|
},
|
|
.ReleaseSmall = .{
|
|
.expect =
|
|
\\error: AndMyCarIsOutOfGas
|
|
\\
|
|
,
|
|
},
|
|
});
|
|
|
|
cases.addCase(.{
|
|
.name = "try try return return",
|
|
.source =
|
|
\\fn foo() !void {
|
|
\\ try bar();
|
|
\\}
|
|
\\
|
|
\\fn bar() !void {
|
|
\\ return make_error();
|
|
\\}
|
|
\\
|
|
\\fn make_error() !void {
|
|
\\ return error.TheSkyIsFalling;
|
|
\\}
|
|
\\
|
|
\\pub fn main() !void {
|
|
\\ try foo();
|
|
\\}
|
|
,
|
|
.Debug = .{
|
|
.expect =
|
|
\\error: TheSkyIsFalling
|
|
\\source.zig:10:5: [address] in make_error (test)
|
|
\\ return error.TheSkyIsFalling;
|
|
\\ ^
|
|
\\source.zig:6:5: [address] in bar (test)
|
|
\\ return make_error();
|
|
\\ ^
|
|
\\source.zig:2:5: [address] in foo (test)
|
|
\\ try bar();
|
|
\\ ^
|
|
\\source.zig:14:5: [address] in main (test)
|
|
\\ try foo();
|
|
\\ ^
|
|
\\
|
|
,
|
|
},
|
|
.ReleaseSafe = .{
|
|
.exclude_os = .{
|
|
.windows, // TODO
|
|
},
|
|
.expect =
|
|
\\error: TheSkyIsFalling
|
|
\\source.zig:10:5: [address] in [function]
|
|
\\ return error.TheSkyIsFalling;
|
|
\\ ^
|
|
\\source.zig:6:5: [address] in [function]
|
|
\\ return make_error();
|
|
\\ ^
|
|
\\source.zig:2:5: [address] in [function]
|
|
\\ try bar();
|
|
\\ ^
|
|
\\source.zig:14:5: [address] in [function]
|
|
\\ try foo();
|
|
\\ ^
|
|
\\
|
|
,
|
|
},
|
|
.ReleaseFast = .{
|
|
.expect =
|
|
\\error: TheSkyIsFalling
|
|
\\
|
|
,
|
|
},
|
|
.ReleaseSmall = .{
|
|
.expect =
|
|
\\error: TheSkyIsFalling
|
|
\\
|
|
,
|
|
},
|
|
});
|
|
|
|
cases.addCase(.{
|
|
.exclude_os = .{
|
|
.openbsd, // integer overflow
|
|
.windows, // TODO intermittent failures
|
|
},
|
|
.name = "dumpCurrentStackTrace",
|
|
.source =
|
|
\\const std = @import("std");
|
|
\\
|
|
\\fn bar() void {
|
|
\\ std.debug.dumpCurrentStackTrace(@returnAddress());
|
|
\\}
|
|
\\fn foo() void {
|
|
\\ bar();
|
|
\\}
|
|
\\pub fn main() u8 {
|
|
\\ foo();
|
|
\\ return 1;
|
|
\\}
|
|
,
|
|
.Debug = .{
|
|
.expect =
|
|
\\source.zig:7:8: [address] in foo (test)
|
|
\\ bar();
|
|
\\ ^
|
|
\\source.zig:10:8: [address] in main (test)
|
|
\\ foo();
|
|
\\ ^
|
|
\\
|
|
,
|
|
},
|
|
});
|
|
}
|