zig icon indicating copy to clipboard operation
zig copied to clipboard

noreturn error union type will allow try and catch

Open dylan-conway opened this issue 1 year ago • 9 comments

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| {
    ~~~~~~~~~~^~~~~

dylan-conway avatar Mar 21 '24 20:03 dylan-conway

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.

Rexicon226 avatar Mar 21 '24 21:03 Rexicon226

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

kassane avatar Mar 21 '24 21:03 kassane

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.

nektro avatar Mar 21 '24 21:03 nektro

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

nektro avatar Mar 21 '24 21:03 nektro

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.

lacc97 avatar Mar 21 '24 21:03 lacc97

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

silversquirl avatar Mar 21 '24 21:03 silversquirl

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.

nektro avatar Mar 21 '24 21:03 nektro

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.

paperclover avatar Mar 21 '24 22:03 paperclover

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

nektro avatar Mar 21 '24 22:03 nektro