! currently only represents an unreachable point in control flow, but if it were a proper type it could potentially represent an unreachable enum variant as well. A small tweak could be made to the language to make enum literals for generic enums where not all generics can be inferred to have the type ! for all un-inferrable generics (e.g., an expression such as Ok(1) would have the type Result<i32, !> by default, which could then be coerced into Result<i32, T> for any T). As an example of what this would allow, the following code would be valid:
let x: Result<i32, !> = Ok(1);
let y: Result<i32, i32> = x;
The existing coercion forall T. ! => T would have to be transitively extended through enums and structs for this to work, which could be a little difficult to define and might make it too easy to create breaking changes in libraries.
This coercion can be modeled today in @canndrew’s system:
// ...insert code defining Empty from above here...
// this code hasn't been tested so might not be quite right
let x: Result<i32, Empty> = Ok (1);
let y: Result<i32, i32> = x.map_err(|x| x.elim());
Although that is pretty unergonomic.
The more I think about this, the more I realise how much of this applies to all uninhabited types; e.g., @glaebhoerl’s comment about the infectious sizing of ! could apply to all uninhabited types. Maybe we should remove ! like @canndrew proposes, but instead of adding elim, just add a new coercion from any uninhabited type to any other type (effectively letting any uninhabited type behave like today’s !).