Should the ending two return statements be both unreachable?

Now the first is allowed, the second is disallowed.

const std = @import("std");

const T = enum {
    a,
    b,
};

var t: T = .a;

pub fn main() void {
    switch (t) {
        .a => return,
        .b => return,
    }
    return; // compiles okay (v0.14)
    return; // fails to compile
}

Yes, both should be unreachable. I believe the reason why this compiles is because nobody programmed the analyzer to search for this pattern.

3 Likes

There is a simpler example:

pub fn main() void {
    if (true) return;
    return;
}
2 Likes

Note that this sort of thing is useful. Consider something like:

fn foo() void {
    if (builtin.os.tag == .windows) {
        // some windows specific stuff
        return;
    }
    // some non-windows specific stuff
    return;
}

That if (builtin.os.tag == .windows) is effectively if (true) when targeting Windows.


Note also that if you want to get a compile error if you e.g. forget a return, you can do something like:

pub fn main() void {
    switch (t) {
        .a => return,
        .b => return,
    }
    @compileError("switch is expected to return in each case");
}

or

pub fn main() void {
    switch (t) {
        .a => return,
        .b => return,
    }
    comptime unreachable;
}

(see also this somewhat related concept)

11 Likes

or (using the switch expression)

pub fn main() void {
    return switch (t) {
        .a => {},
        .b => {},
    };
}
6 Likes

Right; note that anything more complicated (i.e. multiline case bodies) would need a labeled switch to continue using that pattern.

fn foo() u32 {
    return blk: switch (t) {
        .a => {
            // stuff
            break :blk 1;
        },
        .b => {
            // stuff
            break :blk 2;
        },
    };
}

Here’s a real example of the @compileError option for what it’s worth.

2 Likes

The compileError or comptime unreachable only tell that you made a mistake, not where. The pattern @Sze showed would tell you where you made the mistake. Though, the compile error will be a little more cryptic (being a type mismatch, instead of unreachable).

I think more information is well worth it, especially in a large piece of code like the real example you linked.

Also, will explicitly state these patterns apply not just to switch’s but any time you have multiple branches that all must return.

2 Likes

I don’t think this applies to the example OP gave, since it was concrete, and every switch prong returned unconditionally. It would be perfectly safe to consider the final return statements as unreachable, as it wouldn’t limit any kind of generic programming or conditional compilation. I think if they could implement this detection and flag it as a compiler error, they would.

1 Like

It explains why the behaviour exists at all, ‘to allow you to write code that specialises at comptime’.

But this is certainly an uncaught edge case. I don’t see any reason it couldn’t be caught, but it is also a bug that is annoying not breaking so it is not a high priority.