noreturn error union type will allow try and catch
Zig Version
0.12.0-dev.3397+43edd53c3
Steps to Reproduce and Observed Behavior
fn foo() !noreturn {
while (true) {}
}
pub fn main() !void {
try foo() catch |err| {
return err;
};
}
This will compile without an error.
Expected Behavior
I expect this code to produce a compiler error like this because the code in the catch block is never reached:
repro.zig:745:15: error: expected error union type, found 'noreturn'
try foo() catch |err| {
~~~~~~~~~~^~~~~
Note that the current bug isn't around error unions but with catching noreturn.
See:
fn foo() noreturn {
while (true) {}
}
pub fn main() !void {
foo() catch |err| {
return err;
};
}
which also compiles.
This is being ambiguous in this example.
try foo() catch |err| {
return err;
};
If you are going to use catch, it does not require the try keyword.
Reference
https://ziglang.org/documentation/master/#try
its not ambiguous, catch takes precedence and the compile error is there if the return type is !u32 for example. in @dylan-conway's example the compile error should be error: expected error union type, found 'noreturn' as stated, in @Rexicon226 examples it should error with error: unreachable code.
edit: try does appear to be the one to actually take precedence in the !u32 example so it should perhaps be error: unreachable code in both cases. this is consistent with how you can try foo() orelse xyz on a !?T
Doesn't a !noreturn return type still make sense? It either never returns or it returns an error. A use case that comes to mind is execve. It does seem like a very niche use case though.
Zig currently types execve with an error set return type (ie. error { ... }, as opposed to an error union error { ... }!T).
I do agree that !noreturn would be more communicative though
yes it does make sense, i was not trying to say !noreturn should be disallowed. only agreeing that attempting to try and catch it should be a compile error like with other non-noreturn types. and my edit was only clarifying my opinion on which compile error it should throw.
this reminds me that anything that is noreturn (without error) will stop analyzing. which i think is an intentional design choice, but it is odd because it prevents stuff shown above from erroring.
note that something like this also compiles
const std = @import("std");
pub fn main() void {
std.process.exit(0);
std.debug.print("Hello, world!\n", .{});
@compileError("did not analyze me");
}
or, for more silliness:
const std = @import("std");
pub fn main() void {
// all analysis stops after the noreturn is seen
// so any nonsense that astgen does not silence
// can compile successfully
const a: void = std.process.exit(0) + 1;
_ = a;
4 + 2;
try 0 catch {};
return 1;
}
so for try and catch at the same time, noReturnOrError() catch ... gets seen, but then since the result of this is just noreturn, the try (...) part is ignored.
oh good observation, since this isn't caught by AstGen it may be a duplicate of #11686 and #18504. implementers of those should also include this case in the tests