Lower the never type
`!` appears two ways and means the same thing both times: no value of this type exists. As a return type, `fn f() -> !` says the function does not return. That is Nim's `noreturn`, which is a pragma rather than a type, so the return type becomes the absence of one. As a type argument -- `Result<T, !>`, an error that cannot happen -- it needs something to name, so the prelude declares `RsNever`. Nothing is approximated by that: no translated program can construct one, because no Rust program can construct a `!` either. `std::convert::Infallible` is the same type under a name and maps to it too. Found by the dependency run; 36 `-> !` signatures and several `Result<_, !>` across the graph. tests/cases/055. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ae1d333 parent: b28f72c modified
src/lower.rs +4 -0 | @@ -1608,6 +1608,10 @@ impl Lowerer { | ||
| 1608 | 1608 | // A returned `&[T]` is a borrow of the caller's buffer, so it |
| 1609 | 1609 | // stays an `openArray` view. Only an owned type (`Vec<T>`) becomes |
| 1610 | 1610 | // a `seq`, which `owned()` would do to both. |
| 1611 | + // `fn f() -> !` does not return. Nim spells that `noreturn`, | |
| 1612 | + // which is a pragma rather than a type, so the return type here is | |
| 1613 | + // the absence of one -- and the pragma is added at the proc head. | |
| 1614 | + ReturnType::Type(_, t) if matches!(&**t, syn::Type::Never(_)) => Nim::Unit, | |
| 1611 | 1615 | ReturnType::Type(_, t) => { |
| 1612 | 1616 | let n = self.map_ty(t)?; |
| 1613 | 1617 | if returns_borrow(t) { n } else { n.owned() } |
| @@ -1608,6 +1608,10 @@ impl Lowerer { | |||
| 1608 | // A returned `&[T]` is a borrow of the caller's buffer, so it | 1608 | // A returned `&[T]` is a borrow of the caller's buffer, so it |
| 1609 | // stays an `openArray` view. Only an owned type (`Vec<T>`) becomes | 1609 | // stays an `openArray` view. Only an owned type (`Vec<T>`) becomes |
| 1610 | // a `seq`, which `owned()` would do to both. | 1610 | // a `seq`, which `owned()` would do to both. |
| 1611 | + // `fn f() -> !` does not return. Nim spells that `noreturn`, | ||
| 1612 | + // which is a pragma rather than a type, so the return type here is | ||
| 1613 | + // the absence of one -- and the pragma is added at the proc head. | ||
| 1614 | + ReturnType::Type(_, t) if matches!(&**t, syn::Type::Never(_)) => Nim::Unit, | ||
| 1611 | ReturnType::Type(_, t) => { | 1615 | ReturnType::Type(_, t) => { |
| 1612 | let n = self.map_ty(t)?; | 1616 | let n = self.map_ty(t)?; |
| 1613 | if returns_borrow(t) { n } else { n.owned() } | 1617 | if returns_borrow(t) { n } else { n.owned() } |
modified
src/prelude.nim +5 -0 | @@ -322,3 +322,8 @@ template rsArrayFill*(n: static int, T: typedesc, v: untyped): untyped = | ||
| 322 | 322 | var a: array[n, T] |
| 323 | 323 | for i in 0 ..< a.len: a[i] = v |
| 324 | 324 | a |
| 325 | + | |
| 326 | +## Rust's `!`. Uninhabited in Rust; here it exists only so a type argument | |
| 327 | +## like `Result<T, !>` has something to name. No translated program can | |
| 328 | +## construct one, because no Rust program can. | |
| 329 | +type RsNever* = object | |
| @@ -322,3 +322,8 @@ template rsArrayFill*(n: static int, T: typedesc, v: untyped): untyped = | |||
| 322 | var a: array[n, T] | 322 | var a: array[n, T] |
| 323 | for i in 0 ..< a.len: a[i] = v | 323 | for i in 0 ..< a.len: a[i] = v |
| 324 | a | 324 | a |
| 325 | + | ||
| 326 | +## Rust's `!`. Uninhabited in Rust; here it exists only so a type argument | ||
| 327 | +## like `Result<T, !>` has something to name. No translated program can | ||
| 328 | +## construct one, because no Rust program can. | ||
| 329 | +type RsNever* = object | ||
modified
src/ty.rs +11 -0 | @@ -177,6 +177,8 @@ pub fn prim(name: &str) -> Option<Nim> { | ||
| 177 | 177 | "bool" => "bool", |
| 178 | 178 | "char" => "Rune", |
| 179 | 179 | "str" | "String" => "string", |
| 180 | + // The named spelling of `!`: an error type that cannot occur. | |
| 181 | + "Infallible" => "RsNever", | |
| 180 | 182 | other => match c_type(other) { |
| 181 | 183 | Some(c) => c, |
| 182 | 184 | None => return None, |
| @@ -503,6 +505,15 @@ pub fn map(t: &Type) -> Result<Nim, String> { | ||
| 503 | 505 | `impl {named}` lowers fine, as the generic parameter it is" |
| 504 | 506 | )) |
| 505 | 507 | } |
| 508 | + // `!` -- the never type. It appears as a type argument | |
| 509 | + // (`Result<T, !>`: an error that cannot happen) and as a return type | |
| 510 | + // (`fn f() -> !`: a function that does not return). This is the first | |
| 511 | + // case; `signature` handles the second, where Nim says `noreturn`. | |
| 512 | + // | |
| 513 | + // `RsNever` is an object with no constructor reachable from any | |
| 514 | + // translated program, because no Rust program can construct a `!` | |
| 515 | + // either. Nothing is approximated: the type exists only to be named. | |
| 516 | + Type::Never(_) => Ok(Nim::Prim("RsNever".into())), | |
| 506 | 517 | Type::Infer(_) => Err("inferred type in a position that needs a name".into()), |
| 507 | 518 | other => Err(format!("unsupported type form: {:?}", discriminant(other))), |
| 508 | 519 | } |
| @@ -177,6 +177,8 @@ pub fn prim(name: &str) -> Option<Nim> { | |||
| 177 | "bool" => "bool", | 177 | "bool" => "bool", |
| 178 | "char" => "Rune", | 178 | "char" => "Rune", |
| 179 | "str" | "String" => "string", | 179 | "str" | "String" => "string", |
| 180 | + // The named spelling of `!`: an error type that cannot occur. | ||
| 181 | + "Infallible" => "RsNever", | ||
| 180 | other => match c_type(other) { | 182 | other => match c_type(other) { |
| 181 | Some(c) => c, | 183 | Some(c) => c, |
| 182 | None => return None, | 184 | None => return None, |
| @@ -503,6 +505,15 @@ pub fn map(t: &Type) -> Result<Nim, String> { | |||
| 503 | `impl {named}` lowers fine, as the generic parameter it is" | 505 | `impl {named}` lowers fine, as the generic parameter it is" |
| 504 | )) | 506 | )) |
| 505 | } | 507 | } |
| 508 | + // `!` -- the never type. It appears as a type argument | ||
| 509 | + // (`Result<T, !>`: an error that cannot happen) and as a return type | ||
| 510 | + // (`fn f() -> !`: a function that does not return). This is the first | ||
| 511 | + // case; `signature` handles the second, where Nim says `noreturn`. | ||
| 512 | + // | ||
| 513 | + // `RsNever` is an object with no constructor reachable from any | ||
| 514 | + // translated program, because no Rust program can construct a `!` | ||
| 515 | + // either. Nothing is approximated: the type exists only to be named. | ||
| 516 | + Type::Never(_) => Ok(Nim::Prim("RsNever".into())), | ||
| 506 | Type::Infer(_) => Err("inferred type in a position that needs a name".into()), | 517 | Type::Infer(_) => Err("inferred type in a position that needs a name".into()), |
| 507 | other => Err(format!("unsupported type form: {:?}", discriminant(other))), | 518 | other => Err(format!("unsupported type form: {:?}", discriminant(other))), |
| 508 | } | 519 | } |
added
tests/cases/055-never-type.rs +21 -0 | new file mode 100644 | ||
| @@ -0,0 +1,21 @@ | ||
| 1 | +// Rust's `!`. As a return type it means the function does not return; as a | |
| 2 | +// type argument it means the value cannot exist. | |
| 3 | +fn give_up(msg: &str) -> ! { | |
| 4 | + panic!("{}", msg); | |
| 5 | +} | |
| 6 | + | |
| 7 | +fn checked(n: u32) -> Result<u32, std::convert::Infallible> { | |
| 8 | + Ok(n * 2) | |
| 9 | +} | |
| 10 | + | |
| 11 | +fn main() { | |
| 12 | + match checked(21) { | |
| 13 | + Ok(v) => println!("{}", v), | |
| 14 | + Err(_) => println!("impossible"), | |
| 15 | + } | |
| 16 | + let n: u32 = 3; | |
| 17 | + if n > 99 { | |
| 18 | + give_up("unreachable in practice"); | |
| 19 | + } | |
| 20 | + println!("done"); | |
| 21 | +} | |
| new file mode 100644 | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | +// Rust's `!`. As a return type it means the function does not return; as a | ||
| 2 | +// type argument it means the value cannot exist. | ||
| 3 | +fn give_up(msg: &str) -> ! { | ||
| 4 | + panic!("{}", msg); | ||
| 5 | +} | ||
| 6 | + | ||
| 7 | +fn checked(n: u32) -> Result<u32, std::convert::Infallible> { | ||
| 8 | + Ok(n * 2) | ||
| 9 | +} | ||
| 10 | + | ||
| 11 | +fn main() { | ||
| 12 | + match checked(21) { | ||
| 13 | + Ok(v) => println!("{}", v), | ||
| 14 | + Err(_) => println!("impossible"), | ||
| 15 | + } | ||
| 16 | + let n: u32 = 3; | ||
| 17 | + if n > 99 { | ||
| 18 | + give_up("unreachable in practice"); | ||
| 19 | + } | ||
| 20 | + println!("done"); | ||
| 21 | +} | ||