The expression return smth is of type noreturn because even if it returns from the function, it doesn’t return from the expression.
Now noreturn can coerce into any type because it doesn’t ever actually holds a value. A good example of that is unreachable or @panic whose type is noreturn. You can put unreachable as a “value” into any type because it doesn’t ever actually goes in the location.
So here, return ... coerces into a boolean. You can also boolean or return ... for example.
noreturn coerces to anything with the promise that whatever type is there isn’t actually compared to the other values in the expression, so it doesn’t matter what it is. That’s what allows it to slip in basically anywhere to return error values from try.
Maybe it should be some sort of compile diagnostics to let you know this is legal but probably wrong, but I don’t think you can remove it entirely without breaking a lot of things.
I think it might be worth considering forbidding noreturn to coerce because of an operator that doesn’t affect control flow. Otherwise it’s a very useful feature.
It is not the whole story. boolean or return ... is only valid when boolean is a compile-time known false. And boolean and return ... is only valid when boolean is a compile-time known true. Otherwise, they must be assigned to a value.
It looks, when boolean is compile-time known, boolean or return ... and boolean and return ... are just translated to return ... at a phase during compiling.
Similarly, if (b) !return b2; is also translated to if (b) return b2; at a phase. If the translation is not made, then the line must be written as _ = if (b) !return b2 else false; etc.
I mean why if (b) !return b2; compiles is not only because noreturn values can coerce into any type, but also because it is translated to `if (b) return b2 at a phase. If the translation is not made, then the line must be written as _ = if (b) !return b2 else false; etc.