nandi/rustnimpublic Fork
⑂ ab0405b
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

Fold a conditional array length

`[T; if Self::COMPRESS { 0 } else { Self::NUM_POW10S * 2 }]` -- Nim's `if` is
an expression as Rust's is, and const-folds the same way when the condition
is a constant.

Both branches are converted to `int`: Rust lengths are `usize`, and a bare
literal in one arm against a `usize` const in the other is a type mismatch in
Nim. The conversion const-folds away.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nandithebull committed 2026-09-19T12:57:49-07:00 Browse files
ab0405b parent: 51929ac
modified src/ty.rs +30 -0
@@ -677,6 +677,36 @@ pub fn const_expr(e: &syn::Expr) -> Result<String, String> {
677677 // spelling that covers all three -- identity on an integer, 0/1 on a
678678 // bool, the ordinal on an enum -- and is exactly what Rust's `as` does
679679 // here.
680+ // A conditional length. Nim's `if` is an expression as Rust's is, and
681+ // const-folds the same way when the condition is a constant.
682+ Expr::If(i) => {
683+ let cond = const_expr(&i.cond)?;
684+ let then = i
685+ .then_branch
686+ .stmts
687+ .last()
688+ .and_then(|s| match s {
689+ syn::Stmt::Expr(e, None) => Some(const_expr(e)),
690+ _ => None,
691+ })
692+ .ok_or_else(|| "empty `if` branch in an array length".to_string())??;
693+ let (_, els) = i
694+ .else_branch
695+ .as_ref()
696+ .ok_or_else(|| "an `if` array length needs an `else`".to_string())?;
697+ // Both branches are made `int`: Rust lengths are `usize`, and a
698+ // bare literal in one arm against a `usize` const in the other is
699+ // a type mismatch in Nim. The conversion const-folds.
700+ Ok(format!(
701+ "(if {}: int({}) else: int({}))",
702+ cond,
703+ then,
704+ const_expr(els)?
705+ ))
706+ }
707+ Expr::Unary(u) if matches!(u.op, syn::UnOp::Not(_)) => {
708+ Ok(format!("(not {})", const_expr(&u.expr)?))
709+ }
680710 Expr::Cast(c) => Ok(format!("ord({})", const_expr(&c.expr)?)),
681711 Expr::Lit(syn::ExprLit { lit: syn::Lit::Bool(b), .. }) => Ok(b.value().to_string()),
682712 other => Err(format!(
@@ -677,6 +677,36 @@ pub fn const_expr(e: &syn::Expr) -> Result<String, String> {
677 // spelling that covers all three -- identity on an integer, 0/1 on a677 // spelling that covers all three -- identity on an integer, 0/1 on a
678 // bool, the ordinal on an enum -- and is exactly what Rust's `as` does678 // bool, the ordinal on an enum -- and is exactly what Rust's `as` does
679 // here.679 // here.
680+ // A conditional length. Nim's `if` is an expression as Rust's is, and
681+ // const-folds the same way when the condition is a constant.
682+ Expr::If(i) => {
683+ let cond = const_expr(&i.cond)?;
684+ let then = i
685+ .then_branch
686+ .stmts
687+ .last()
688+ .and_then(|s| match s {
689+ syn::Stmt::Expr(e, None) => Some(const_expr(e)),
690+ _ => None,
691+ })
692+ .ok_or_else(|| "empty `if` branch in an array length".to_string())??;
693+ let (_, els) = i
694+ .else_branch
695+ .as_ref()
696+ .ok_or_else(|| "an `if` array length needs an `else`".to_string())?;
697+ // Both branches are made `int`: Rust lengths are `usize`, and a
698+ // bare literal in one arm against a `usize` const in the other is
699+ // a type mismatch in Nim. The conversion const-folds.
700+ Ok(format!(
701+ "(if {}: int({}) else: int({}))",
702+ cond,
703+ then,
704+ const_expr(els)?
705+ ))
706+ }
707+ Expr::Unary(u) if matches!(u.op, syn::UnOp::Not(_)) => {
708+ Ok(format!("(not {})", const_expr(&u.expr)?))
709+ }
680 Expr::Cast(c) => Ok(format!("ord({})", const_expr(&c.expr)?)),710 Expr::Cast(c) => Ok(format!("ord({})", const_expr(&c.expr)?)),
681 Expr::Lit(syn::ExprLit { lit: syn::Lit::Bool(b), .. }) => Ok(b.value().to_string()),711 Expr::Lit(syn::ExprLit { lit: syn::Lit::Bool(b), .. }) => Ok(b.value().to_string()),
682 other => Err(format!(712 other => Err(format!(
modified tests/cases/052-const-array-len.rs +9 -0
@@ -16,6 +16,12 @@ struct Oid;
1616
1717 impl Oid {
1818 const MAX_SIZE: usize = 5;
19+ const COMPRESS: bool = false;
20+
21+ // A conditional length. Nim's `if` is an expression too.
22+ fn table() -> [u8; if Self::COMPRESS { 1 } else { Self::MAX_SIZE * 2 }] {
23+ [1u8; if Self::COMPRESS { 1 } else { Self::MAX_SIZE * 2 }]
24+ }
1925
2026 // `Self::` in a length, resolving against the impl being walked.
2127 fn buffer() -> [u8; Self::MAX_SIZE] {
@@ -56,6 +62,9 @@ fn main() {
5662
5763 // An associated const as a length, and in the braces rustc's expanded
5864 // output writes it with.
65+ let t2 = Oid::table();
66+ println!("{} {}", t2.len(), sum(&t2));
67+
5968 let b2 = Oid::buffer();
6069 println!("{} {}", b2.len(), sum(&b2));
6170
@@ -16,6 +16,12 @@ struct Oid;
16 16
17 impl Oid {17 impl Oid {
18 const MAX_SIZE: usize = 5;18 const MAX_SIZE: usize = 5;
19+ const COMPRESS: bool = false;
20+
21+ // A conditional length. Nim's `if` is an expression too.
22+ fn table() -> [u8; if Self::COMPRESS { 1 } else { Self::MAX_SIZE * 2 }] {
23+ [1u8; if Self::COMPRESS { 1 } else { Self::MAX_SIZE * 2 }]
24+ }
19 25
20 // `Self::` in a length, resolving against the impl being walked.26 // `Self::` in a length, resolving against the impl being walked.
21 fn buffer() -> [u8; Self::MAX_SIZE] {27 fn buffer() -> [u8; Self::MAX_SIZE] {
@@ -56,6 +62,9 @@ fn main() {
56 62
57 // An associated const as a length, and in the braces rustc's expanded63 // An associated const as a length, and in the braces rustc's expanded
58 // output writes it with.64 // output writes it with.
65+ let t2 = Oid::table();
66+ println!("{} {}", t2.len(), sum(&t2));
67+
59 let b2 = Oid::buffer();68 let b2 = Oid::buffer();
60 println!("{} {}", b2.len(), sum(&b2));69 println!("{} {}", b2.len(), sum(&b2));
61 70