Take a range's type from the end that has one, and scan every branch
`for i in 0..self.netsize` was typed from the literal `0`, so the loop variable came out `i32` against a `usize` bound. A bare bound says only what Rust defaults to; the other end decides, and the literal is re-lowered to match -- without which Nim compares an `int32` against a `uint`. `startpos = i` is a peer as plainly as a comparison is, and the scan for peers now looks in `else` branches and `match` arms as well as `then`. That last omission was the whole of why `best` stayed `i32`. With these, `color_quant` compiles: Rust through rustnim to Nim that `nim c` accepts, 685 lines and 84 items. It is the first of libcosmic's dependencies to go the whole way. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
e28de7f parent: 2693452 modified
src/lower.rs +46 -2 | @@ -4082,8 +4082,35 @@ impl Lowerer { | ||
| 4082 | 4082 | Expr::If(i) => { |
| 4083 | 4083 | self.find_peer_expr(name, &i.cond, out); |
| 4084 | 4084 | self.find_peer(name, &i.then_branch.stmts, out); |
| 4085 | + if let Some((_, els)) = &i.else_branch { | |
| 4086 | + self.find_peer_expr(name, els, out); | |
| 4087 | + } | |
| 4088 | + } | |
| 4089 | + Expr::Match(mt) => { | |
| 4090 | + for arm in &mt.arms { | |
| 4091 | + self.find_peer_expr(name, &arm.body, out); | |
| 4092 | + } | |
| 4093 | + } | |
| 4094 | + Expr::Assign(a) => { | |
| 4095 | + // `startpos = i` says what `startpos` is as plainly as a | |
| 4096 | + // comparison does. | |
| 4097 | + if names(&a.left) && !is_bare_literal(&a.right) { | |
| 4098 | + if let Some(t) = self.pushed_ty(&a.right) { | |
| 4099 | + *out = Some(t); | |
| 4100 | + return; | |
| 4101 | + } | |
| 4102 | + if let Expr::Path(p) = strip_parens(&a.right) { | |
| 4103 | + if p.path.segments.len() == 1 { | |
| 4104 | + let n = p.path.segments[0].ident.to_string(); | |
| 4105 | + if let Some(t) = self.pending_lets.get(&n).cloned() { | |
| 4106 | + *out = Some(t); | |
| 4107 | + return; | |
| 4108 | + } | |
| 4109 | + } | |
| 4110 | + } | |
| 4111 | + } | |
| 4112 | + self.find_peer_expr(name, &a.right, out) | |
| 4085 | 4113 | } |
| 4086 | - Expr::Assign(a) => self.find_peer_expr(name, &a.right, out), | |
| 4087 | 4114 | Expr::Index(ix) => { |
| 4088 | 4115 | // `xs[i]` makes `i` an index, which says nothing about width. |
| 4089 | 4116 | self.find_peer_expr(name, &ix.expr, out); |
| @@ -4815,7 +4842,24 @@ impl Lowerer { | ||
| 4815 | 4842 | return Err("a `for` over an unbounded range would not terminate".into()) |
| 4816 | 4843 | } |
| 4817 | 4844 | }; |
| 4818 | - let ty = lo.ty.clone().or(hi.ty.clone()); | |
| 4845 | + // `0..n` is `n`'s type, not the literal's. A bare bound says | |
| 4846 | + // only what Rust defaults to, so the other end decides -- and | |
| 4847 | + // the literal is then re-lowered to match, or Nim compares an | |
| 4848 | + // `int32` against a `uint`. | |
| 4849 | + let lo_bare = r.start.as_deref().is_some_and(is_bare_literal); | |
| 4850 | + let hi_bare = r.end.as_deref().is_some_and(is_bare_literal); | |
| 4851 | + let ty = match (lo_bare, hi_bare) { | |
| 4852 | + (true, false) => hi.ty.clone().or(lo.ty.clone()), | |
| 4853 | + _ => lo.ty.clone().or(hi.ty.clone()), | |
| 4854 | + }; | |
| 4855 | + let lo = match (&ty, r.start.as_deref()) { | |
| 4856 | + (Some(t), Some(e)) if lo_bare => self.expr_at(e, Some(t))?, | |
| 4857 | + _ => lo, | |
| 4858 | + }; | |
| 4859 | + let hi = match (&ty, r.end.as_deref()) { | |
| 4860 | + (Some(t), Some(e)) if hi_bare && !lo_bare => self.expr_at(e, Some(t))?, | |
| 4861 | + _ => hi, | |
| 4862 | + }; | |
| 4819 | 4863 | Ok(Iter::Range { |
| 4820 | 4864 | lo: lo.code, |
| 4821 | 4865 | hi: hi.code, |
| @@ -4082,8 +4082,35 @@ impl Lowerer { | |||
| 4082 | Expr::If(i) => { | 4082 | Expr::If(i) => { |
| 4083 | self.find_peer_expr(name, &i.cond, out); | 4083 | self.find_peer_expr(name, &i.cond, out); |
| 4084 | self.find_peer(name, &i.then_branch.stmts, out); | 4084 | self.find_peer(name, &i.then_branch.stmts, out); |
| 4085 | + if let Some((_, els)) = &i.else_branch { | ||
| 4086 | + self.find_peer_expr(name, els, out); | ||
| 4087 | + } | ||
| 4088 | + } | ||
| 4089 | + Expr::Match(mt) => { | ||
| 4090 | + for arm in &mt.arms { | ||
| 4091 | + self.find_peer_expr(name, &arm.body, out); | ||
| 4092 | + } | ||
| 4093 | + } | ||
| 4094 | + Expr::Assign(a) => { | ||
| 4095 | + // `startpos = i` says what `startpos` is as plainly as a | ||
| 4096 | + // comparison does. | ||
| 4097 | + if names(&a.left) && !is_bare_literal(&a.right) { | ||
| 4098 | + if let Some(t) = self.pushed_ty(&a.right) { | ||
| 4099 | + *out = Some(t); | ||
| 4100 | + return; | ||
| 4101 | + } | ||
| 4102 | + if let Expr::Path(p) = strip_parens(&a.right) { | ||
| 4103 | + if p.path.segments.len() == 1 { | ||
| 4104 | + let n = p.path.segments[0].ident.to_string(); | ||
| 4105 | + if let Some(t) = self.pending_lets.get(&n).cloned() { | ||
| 4106 | + *out = Some(t); | ||
| 4107 | + return; | ||
| 4108 | + } | ||
| 4109 | + } | ||
| 4110 | + } | ||
| 4111 | + } | ||
| 4112 | + self.find_peer_expr(name, &a.right, out) | ||
| 4085 | } | 4113 | } |
| 4086 | - Expr::Assign(a) => self.find_peer_expr(name, &a.right, out), | ||
| 4087 | Expr::Index(ix) => { | 4114 | Expr::Index(ix) => { |
| 4088 | // `xs[i]` makes `i` an index, which says nothing about width. | 4115 | // `xs[i]` makes `i` an index, which says nothing about width. |
| 4089 | self.find_peer_expr(name, &ix.expr, out); | 4116 | self.find_peer_expr(name, &ix.expr, out); |
| @@ -4815,7 +4842,24 @@ impl Lowerer { | |||
| 4815 | return Err("a `for` over an unbounded range would not terminate".into()) | 4842 | return Err("a `for` over an unbounded range would not terminate".into()) |
| 4816 | } | 4843 | } |
| 4817 | }; | 4844 | }; |
| 4818 | - let ty = lo.ty.clone().or(hi.ty.clone()); | 4845 | + // `0..n` is `n`'s type, not the literal's. A bare bound says |
| 4846 | + // only what Rust defaults to, so the other end decides -- and | ||
| 4847 | + // the literal is then re-lowered to match, or Nim compares an | ||
| 4848 | + // `int32` against a `uint`. | ||
| 4849 | + let lo_bare = r.start.as_deref().is_some_and(is_bare_literal); | ||
| 4850 | + let hi_bare = r.end.as_deref().is_some_and(is_bare_literal); | ||
| 4851 | + let ty = match (lo_bare, hi_bare) { | ||
| 4852 | + (true, false) => hi.ty.clone().or(lo.ty.clone()), | ||
| 4853 | + _ => lo.ty.clone().or(hi.ty.clone()), | ||
| 4854 | + }; | ||
| 4855 | + let lo = match (&ty, r.start.as_deref()) { | ||
| 4856 | + (Some(t), Some(e)) if lo_bare => self.expr_at(e, Some(t))?, | ||
| 4857 | + _ => lo, | ||
| 4858 | + }; | ||
| 4859 | + let hi = match (&ty, r.end.as_deref()) { | ||
| 4860 | + (Some(t), Some(e)) if hi_bare && !lo_bare => self.expr_at(e, Some(t))?, | ||
| 4861 | + _ => hi, | ||
| 4862 | + }; | ||
| 4819 | Ok(Iter::Range { | 4863 | Ok(Iter::Range { |
| 4820 | lo: lo.code, | 4864 | lo: lo.code, |
| 4821 | hi: hi.code, | 4865 | hi: hi.code, |