Description
This is in a sense a continuation of #3257. That issue laid some of the groundwork for the current status of noreturn
and similar types, but there are a few things missing. Also, thanks to @ifreund for helping me figure out some of the details of this.
Lots of types can be constructed which are similar to noreturn
. In type theory terms, they are "empty types": types for which no values exist. I'll call these types "noreturn-like". Here's an exhaustive list of types which should be considered noreturn-like (note the special cases around pointers mentioned at the end):
noreturn
enum {}
enum(T) {}
for anyT
error {}
union
/extern union
where all fields are noreturn-like (including the case where the union has no fields)- tagged unions where the tag type is noreturn-like or all fields are noreturn-like
- structs containing any noreturn-like field
E!T
whereE
andT
are both noreturn-like (so,error{}!T
)[n]T
/@Vector(n, T)
whereT
is noreturn-like andn != 0
*T
/[*]T
(and any qualifiers) whereT
is noreturn-like[]T
has the value&.{}
so is not noreturn-like, but rather zero-bit OPV[*c]T
has the valuenull
so is not noreturn-like. In principle it's zero-bit OPV, but this is a footgun, so instead is disallowed.
Currently, most of these aren't "truly" considered noreturn-like. Such types should terminate semantic analysis when found (akin to unreachable
). Additionally, undefined
should not coerce to noreturn-like types, to avoid being able to actually "construct" them without terminating analysis.
We currently have the restriction that (at function scope) var x: T = ...
, where T
is noreturn-like, is a compile error. This, however, is inconsistent - const x: T = ...
is allowed, and moreover, so is var x = @as(T, ...)
. I propose that we remove this needless restriction. Allocs for noreturn-like types can be special-cased to emit no AIR instructions, and analysis will terminate before we attempt to store to one, as by that point we will have had an instruction whose result type is the child type.
Note, however, that global var
s may not be of a noreturn-like type, nor may a value of a noreturn-like type be exported or marked extern. This is a compile error.
One last notable change in semantics here is that switching on a noreturn-like enum is no longer supported in the same way that it is in status quo. The idea currently is that you turn such an enum into the true noreturn
type using an empty switch, switch (e) {}
. This code will still be accepted under this proposal, but for a different reason: it's never analyzed! The moment e
is created, semantic analysis terminates, since we reached a noreturn instruction.