nandi/rustnimpublic Fork
⑂ ae1d333
Commits
⬇ Clone ▾
git clone https://git.rickub.com/nandi/rustnim.git
git clone ssh://git@rickub.com/nandi/rustnim.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP

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>
nandithebull committed 2026-09-19T09:54:57-07:00 Browse files
ae1d333 parent: b28f72c
modified src/lower.rs +4 -0
@@ -1608,6 +1608,10 @@ impl Lowerer {
16081608 // A returned `&[T]` is a borrow of the caller's buffer, so it
16091609 // stays an `openArray` view. Only an owned type (`Vec<T>`) becomes
16101610 // 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,
16111615 ReturnType::Type(_, t) => {
16121616 let n = self.map_ty(t)?;
16131617 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 it1608 // A returned `&[T]` is a borrow of the caller's buffer, so it
1609 // stays an `openArray` view. Only an owned type (`Vec<T>`) becomes1609 // 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 =
322322 var a: array[n, T]
323323 for i in 0 ..< a.len: a[i] = v
324324 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] = v323 for i in 0 ..< a.len: a[i] = v
324 a324 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> {
177177 "bool" => "bool",
178178 "char" => "Rune",
179179 "str" | "String" => "string",
180+ // The named spelling of `!`: an error type that cannot occur.
181+ "Infallible" => "RsNever",
180182 other => match c_type(other) {
181183 Some(c) => c,
182184 None => return None,
@@ -503,6 +505,15 @@ pub fn map(t: &Type) -> Result<Nim, String> {
503505 `impl {named}` lowers fine, as the generic parameter it is"
504506 ))
505507 }
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())),
506517 Type::Infer(_) => Err("inferred type in a position that needs a name".into()),
507518 other => Err(format!("unsupported type form: {:?}", discriminant(other))),
508519 }
@@ -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+}