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

Infer integer widths from use, as Rust does

Five inferences, each reading from where Rust reads it, found by translating
`color_quant` end to end:

  * `let mut i = 0;` takes its type from the first typed value the binding is
    compared or combined with. A bare literal says only what Rust *defaults*
    to, and `while i < samplepixels` is what makes it a `usize`.
  * That scan sees bindings declared later and inside loops, which is where
    the peer usually is.
  * `vec![0; 256]` into a `Vec<usize>` is a `usize` zero: the element takes
    its type from the expected `Vec<T>`, and lowering the literal first made
    it an `i32`.
  * `type Neuron = Quad<f64>;` used as `Neuron { .. }`. An alias is expanded
    rather than emitted, so the name does not exist in Nim -- but the type it
    stands for does, and it carries the generic arguments too.
  * `match n { 0 => 1, n => n }` is `n`'s type. Rust gives every arm the same
    one, but an arm that is a bare literal only reports a default, so an arm
    carrying a real type is preferred.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nandithebull committed 2026-09-20T10:39:56-07:00 Browse files
2693452 parent: 71ee2a9
modified src/lower.rs +239 -7
@@ -402,6 +402,8 @@ pub struct Lowerer {
402402 vec_hint: Option<Nim>,
403403 /// Type for a `let x;` with no initialiser, from its first assignment.
404404 deferred_hint: Option<Nim>,
405+ /// Types of bindings a hint scan will meet before they are lowered.
406+ pending_lets: HashMap<String, Nim>,
405407 /// Bounds discovered while rewriting `impl Trait` parameters. `rewrite_apit`
406408 /// runs from `&self`, so they are parked here and folded into `bounds` by
407409 /// whoever called it.
@@ -508,6 +510,7 @@ impl Lowerer {
508510 errors: Vec::new(),
509511 vec_hint: None,
510512 deferred_hint: None,
513+ pending_lets: HashMap::new(),
511514 pending_apit_bounds: std::cell::RefCell::new(Vec::new()),
512515 bounds: HashMap::new(),
513516 const_names_at: std::cell::Cell::new(None),
@@ -2327,16 +2330,25 @@ impl Lowerer {
23272330 Expr::Match(m) => {
23282331 let scrut = self.expr(&m.expr).ok()?;
23292332 let st = scrut.ty.clone()?;
2333+ // Rust gives every arm the same type, but an arm that is a
2334+ // bare literal only says what that literal *defaults* to. So
2335+ // an arm that carries a real type is preferred: in
2336+ // `match n { 0 => 1, n => n }` the answer is `n`'s type, and
2337+ // taking the first arm would make it `i32`.
2338+ let mut fallback = None;
23302339 for arm in &m.arms {
23312340 self.push_scope();
23322341 let bound = self.pat_bind(&arm.pat, &scrut.code, &st).is_ok();
23332342 let t = if bound { self.result_ty_inner(&arm.body) } else { None };
23342343 self.pop_scope();
2335- if t.is_some() {
2336- return t;
2344+ let Some(t) = t else { continue };
2345+ if is_bare_literal(&arm.body) {
2346+ fallback.get_or_insert(t);
2347+ } else {
2348+ return Some(t);
23372349 }
23382350 }
2339- None
2351+ fallback
23402352 }
23412353 Expr::If(i) => {
23422354 let then = i.then_branch.stmts.last().and_then(|st| match st {
@@ -3913,7 +3925,9 @@ impl Lowerer {
39133925 // else already says what it holds.
39143926 let hint = self.vec_elem_hint(l, rest);
39153927 let outer_hint = std::mem::replace(&mut self.vec_hint, hint);
3916- let deferred = self.deferred_let_hint(l, rest);
3928+ let deferred = self
3929+ .deferred_let_hint(l, rest)
3930+ .or_else(|| self.literal_let_hint(l, rest));
39173931 let outer_def = std::mem::replace(&mut self.deferred_hint, deferred);
39183932 let r = self.local_inner(l);
39193933 self.vec_hint = outer_hint;
@@ -3921,6 +3935,163 @@ impl Lowerer {
39213935 return r;
39223936 }
39233937
3938+ /// The type of `let x = 0;` -- an unsuffixed literal, which says only what
3939+ /// Rust *defaults* to -- read off the first place the binding meets a
3940+ /// typed value. `let mut i = 0; while i < samplepixels` makes `i` a
3941+ /// `usize` in Rust, and for the same reason here.
3942+ fn literal_let_hint(&mut self, l: &Local, rest: &[Stmt]) -> Option<Nim> {
3943+ if matches!(&l.pat, Pat::Type(_)) {
3944+ return None;
3945+ }
3946+ let Pat::Ident(id) = &l.pat else { return None };
3947+ let init = l.init.as_ref()?;
3948+ if !is_bare_literal(&init.expr) {
3949+ return None;
3950+ }
3951+ let name = id.ident.to_string();
3952+ // Types for bindings this scan will meet before they are lowered.
3953+ let outer = std::mem::take(&mut self.pending_lets);
3954+ self.collect_pending_lets(rest);
3955+ let mut found = None;
3956+ self.find_peer(&name, rest, &mut found);
3957+ self.pending_lets = outer;
3958+ found.filter(|t| t.is_integer() || matches!(t, Nim::Prim(p) if p.starts_with("float")))
3959+ }
3960+
3961+ /// Record what each later `let` in these statements binds, for peers that
3962+ /// are not in scope yet. Only the ones whose type is plain to see.
3963+ fn collect_pending_lets(&mut self, stmts: &[Stmt]) {
3964+ for st in stmts {
3965+ match st {
3966+ Stmt::Local(l) => {
3967+ let (n, ann) = match &l.pat {
3968+ Pat::Ident(i) => (i.ident.to_string(), None),
3969+ Pat::Type(t) => match &*t.pat {
3970+ Pat::Ident(i) => {
3971+ (i.ident.to_string(), self.map_ty(&t.ty).ok())
3972+ }
3973+ _ => continue,
3974+ },
3975+ _ => continue,
3976+ };
3977+ let t = ann.or_else(|| {
3978+ let i = l.init.as_ref()?;
3979+ if is_bare_literal(&i.expr) {
3980+ return None;
3981+ }
3982+ self.pushed_ty(&i.expr)
3983+ });
3984+ if let Some(t) = t {
3985+ self.pending_lets.entry(n).or_insert(t);
3986+ }
3987+ }
3988+ Stmt::Expr(e, _) => match e {
3989+ Expr::ForLoop(f) => {
3990+ // A `for i in 0..n` binding is typed by the range's
3991+ // bounds, and is a peer like any other.
3992+ if let (Pat::Ident(id), Expr::Range(r)) = (&*f.pat, strip_parens(&f.expr)) {
3993+ let b = r.end.as_deref().or(r.start.as_deref());
3994+ if let Some(b) = b {
3995+ if !is_bare_literal(b) {
3996+ if let Some(t) = self.pushed_ty(b) {
3997+ self.pending_lets
3998+ .entry(id.ident.to_string())
3999+ .or_insert(t);
4000+ }
4001+ }
4002+ }
4003+ }
4004+ self.collect_pending_lets(&f.body.stmts)
4005+ }
4006+ Expr::While(w) => self.collect_pending_lets(&w.body.stmts),
4007+ Expr::Loop(l) => self.collect_pending_lets(&l.body.stmts),
4008+ Expr::Block(b) => self.collect_pending_lets(&b.block.stmts),
4009+ _ => {}
4010+ },
4011+ _ => {}
4012+ }
4013+ }
4014+ }
4015+
4016+ /// The type of whatever `name` is first compared or combined with.
4017+ fn find_peer(&mut self, name: &str, stmts: &[Stmt], out: &mut Option<Nim>) {
4018+ for st in stmts {
4019+ if out.is_some() {
4020+ return;
4021+ }
4022+ match st {
4023+ Stmt::Expr(e, _) => self.find_peer_expr(name, e, out),
4024+ Stmt::Local(l) => {
4025+ if let Some(i) = &l.init {
4026+ self.find_peer_expr(name, &i.expr, out);
4027+ }
4028+ }
4029+ _ => {}
4030+ }
4031+ }
4032+ }
4033+
4034+ fn find_peer_expr(&mut self, name: &str, e: &Expr, out: &mut Option<Nim>) {
4035+ if out.is_some() {
4036+ return;
4037+ }
4038+ let names = |x: &Expr| {
4039+ matches!(x, Expr::Path(p)
4040+ if p.path.segments.len() == 1 && p.path.segments[0].ident == name)
4041+ };
4042+ match e {
4043+ Expr::Binary(b) => {
4044+ let other = if names(&b.left) {
4045+ Some(&*b.right)
4046+ } else if names(&b.right) {
4047+ Some(&*b.left)
4048+ } else {
4049+ None
4050+ };
4051+ if let Some(o) = other {
4052+ if !is_bare_literal(o) {
4053+ if let Some(t) = self.pushed_ty(o) {
4054+ *out = Some(t);
4055+ return;
4056+ }
4057+ // The peer may be declared *after* this binding and
4058+ // inside a loop, so it is not in scope yet. Its own
4059+ // `let` says what it is.
4060+ if let Expr::Path(p) = strip_parens(o) {
4061+ if p.path.segments.len() == 1 {
4062+ let n = p.path.segments[0].ident.to_string();
4063+ if let Some(t) = self.pending_lets.get(&n).cloned() {
4064+ *out = Some(t);
4065+ return;
4066+ }
4067+ }
4068+ }
4069+ }
4070+ }
4071+ self.find_peer_expr(name, &b.left, out);
4072+ self.find_peer_expr(name, &b.right, out);
4073+ }
4074+ Expr::While(w) => {
4075+ self.find_peer_expr(name, &w.cond, out);
4076+ self.find_peer(name, &w.body.stmts, out);
4077+ }
4078+ Expr::ForLoop(f) => self.find_peer(name, &f.body.stmts, out),
4079+ Expr::Loop(l) => self.find_peer(name, &l.body.stmts, out),
4080+ Expr::Block(b) => self.find_peer(name, &b.block.stmts, out),
4081+ Expr::Unsafe(u) => self.find_peer(name, &u.block.stmts, out),
4082+ Expr::If(i) => {
4083+ self.find_peer_expr(name, &i.cond, out);
4084+ self.find_peer(name, &i.then_branch.stmts, out);
4085+ }
4086+ Expr::Assign(a) => self.find_peer_expr(name, &a.right, out),
4087+ Expr::Index(ix) => {
4088+ // `xs[i]` makes `i` an index, which says nothing about width.
4089+ self.find_peer_expr(name, &ix.expr, out);
4090+ }
4091+ _ => {}
4092+ }
4093+ }
4094+
39244095 /// The type of a `let x;` binding, read off the first assignment to it.
39254096 fn deferred_let_hint(&mut self, l: &Local, rest: &[Stmt]) -> Option<Nim> {
39264097 if l.init.is_some() || matches!(&l.pat, Pat::Type(_)) {
@@ -4137,7 +4308,11 @@ impl Lowerer {
41374308 return Ok(());
41384309 }
41394310
4140- let v = self.expr_at(&init.expr, ann.as_ref())?;
4311+ // With no annotation, an unsuffixed literal initialiser takes the type
4312+ // of whatever the binding is first compared or combined with, which is
4313+ // where Rust gets it. `deferred_hint` carries that.
4314+ let want = ann.clone().or_else(|| self.deferred_hint.clone());
4315+ let v = self.expr_at(&init.expr, want.as_ref())?;
41414316
41424317 // `let s = &buf[..n]` binds a view of a place that is already in
41434318 // scope. Nim's borrow checker will not let a `let` borrow out of a
@@ -5876,6 +6051,28 @@ impl Lowerer {
58766051 .ok_or("`Self` outside an `impl` block")?,
58776052 other => other.to_string(),
58786053 };
6054+ // `type Neuron = Quad<f64>;` used as a literal's name. An
6055+ // alias is expanded rather than emitted, so the name does not
6056+ // exist in Nim -- but the type it stands for does, and it
6057+ // carries the generic arguments the literal would otherwise
6058+ // have to infer.
6059+ let mut alias_args: Option<Vec<Nim>> = None;
6060+ let name = {
6061+ let ty = syn::Type::Path(syn::TypePath {
6062+ attrs: Vec::new(),
6063+ qself: None,
6064+ path: s.path.clone(),
6065+ });
6066+ match self.map_ty(&ty) {
6067+ Ok(Nim::Named(n, a)) if n != name && self.structs.contains_key(&n) => {
6068+ if !a.is_empty() {
6069+ alias_args = Some(a);
6070+ }
6071+ n
6072+ }
6073+ _ => name,
6074+ }
6075+ };
58796076 let mut parts = Vec::new();
58806077 let mut field_tys: Vec<(Nim, Nim)> = Vec::new();
58816078 for f in &s.fields {
@@ -5901,6 +6098,9 @@ impl Lowerer {
59016098 Nim::Named(name.clone(), vec![])
59026099 } else {
59036100 match expect {
6101+ _ if alias_args.as_ref().is_some_and(|a| a.len() == gp.len()) => {
6102+ Nim::Named(name.clone(), alias_args.clone().unwrap())
6103+ }
59046104 Some(Nim::Named(n, a)) if *n == name && a.len() == gp.len() => {
59056105 Nim::Named(name.clone(), a.clone())
59066106 }
@@ -7309,9 +7509,20 @@ impl Lowerer {
73097509 }
73107510 // `vec![v; n]`, after expansion.
73117511 "from_elem" if codes.len() == 2 => {
7312- let t = args[0].ty.clone().map(|t| Nim::Seq(Box::new(t.owned())));
7512+ // The element takes its type from the expected `Vec<T>` where
7513+ // there is one: `vec![0; 256]` into a `Vec<usize>` is a `usize`
7514+ // zero, and lowering the literal before knowing that makes it
7515+ // an `i32`.
7516+ let want = match expect {
7517+ Some(Nim::Seq(e)) | Some(Nim::OpenArray(e)) => Some((**e).clone()),
7518+ _ => None,
7519+ };
7520+ let v = self.expr_at(&c.args[0], want.as_ref())?;
7521+ let t = want
7522+ .or_else(|| v.ty.clone().map(|t| t.owned()))
7523+ .map(|t| Nim::Seq(Box::new(t)));
73137524 return Ok(Val::new(
7314- format!("newSeqWith(int({}), {})", codes[1], codes[0]),
7525+ format!("newSeqWith(int({}), {})", codes[1], v.code),
73157526 t,
73167527 ));
73177528 }
@@ -9581,3 +9792,24 @@ fn strip_parens(e: &Expr) -> &Expr {
95819792 other => other,
95829793 }
95839794 }
9795+
9796+/// Whether an expression is an integer or float literal with no suffix, whose
9797+/// type is therefore Rust's default rather than anything the program said.
9798+fn is_bare_literal(e: &Expr) -> bool {
9799+ match e {
9800+ Expr::Lit(l) => match &l.lit {
9801+ syn::Lit::Int(i) => i.suffix().is_empty(),
9802+ syn::Lit::Float(f) => f.suffix().is_empty(),
9803+ _ => false,
9804+ },
9805+ Expr::Unary(u) => is_bare_literal(&u.expr),
9806+ Expr::Paren(p) => is_bare_literal(&p.expr),
9807+ Expr::Group(g) => is_bare_literal(&g.expr),
9808+ Expr::Block(b) => b
9809+ .block
9810+ .stmts
9811+ .last()
9812+ .is_some_and(|s| matches!(s, Stmt::Expr(e, None) if is_bare_literal(e))),
9813+ _ => false,
9814+ }
9815+}
@@ -402,6 +402,8 @@ pub struct Lowerer {
402 vec_hint: Option<Nim>,402 vec_hint: Option<Nim>,
403 /// Type for a `let x;` with no initialiser, from its first assignment.403 /// Type for a `let x;` with no initialiser, from its first assignment.
404 deferred_hint: Option<Nim>,404 deferred_hint: Option<Nim>,
405+ /// Types of bindings a hint scan will meet before they are lowered.
406+ pending_lets: HashMap<String, Nim>,
405 /// Bounds discovered while rewriting `impl Trait` parameters. `rewrite_apit`407 /// Bounds discovered while rewriting `impl Trait` parameters. `rewrite_apit`
406 /// runs from `&self`, so they are parked here and folded into `bounds` by408 /// runs from `&self`, so they are parked here and folded into `bounds` by
407 /// whoever called it.409 /// whoever called it.
@@ -508,6 +510,7 @@ impl Lowerer {
508 errors: Vec::new(),510 errors: Vec::new(),
509 vec_hint: None,511 vec_hint: None,
510 deferred_hint: None,512 deferred_hint: None,
513+ pending_lets: HashMap::new(),
511 pending_apit_bounds: std::cell::RefCell::new(Vec::new()),514 pending_apit_bounds: std::cell::RefCell::new(Vec::new()),
512 bounds: HashMap::new(),515 bounds: HashMap::new(),
513 const_names_at: std::cell::Cell::new(None),516 const_names_at: std::cell::Cell::new(None),
@@ -2327,16 +2330,25 @@ impl Lowerer {
2327 Expr::Match(m) => {2330 Expr::Match(m) => {
2328 let scrut = self.expr(&m.expr).ok()?;2331 let scrut = self.expr(&m.expr).ok()?;
2329 let st = scrut.ty.clone()?;2332 let st = scrut.ty.clone()?;
2333+ // Rust gives every arm the same type, but an arm that is a
2334+ // bare literal only says what that literal *defaults* to. So
2335+ // an arm that carries a real type is preferred: in
2336+ // `match n { 0 => 1, n => n }` the answer is `n`'s type, and
2337+ // taking the first arm would make it `i32`.
2338+ let mut fallback = None;
2330 for arm in &m.arms {2339 for arm in &m.arms {
2331 self.push_scope();2340 self.push_scope();
2332 let bound = self.pat_bind(&arm.pat, &scrut.code, &st).is_ok();2341 let bound = self.pat_bind(&arm.pat, &scrut.code, &st).is_ok();
2333 let t = if bound { self.result_ty_inner(&arm.body) } else { None };2342 let t = if bound { self.result_ty_inner(&arm.body) } else { None };
2334 self.pop_scope();2343 self.pop_scope();
2335- if t.is_some() {2344+ let Some(t) = t else { continue };
2336- return t;2345+ if is_bare_literal(&arm.body) {
2346+ fallback.get_or_insert(t);
2347+ } else {
2348+ return Some(t);
2337 }2349 }
2338 }2350 }
2339- None2351+ fallback
2340 }2352 }
2341 Expr::If(i) => {2353 Expr::If(i) => {
2342 let then = i.then_branch.stmts.last().and_then(|st| match st {2354 let then = i.then_branch.stmts.last().and_then(|st| match st {
@@ -3913,7 +3925,9 @@ impl Lowerer {
3913 // else already says what it holds.3925 // else already says what it holds.
3914 let hint = self.vec_elem_hint(l, rest);3926 let hint = self.vec_elem_hint(l, rest);
3915 let outer_hint = std::mem::replace(&mut self.vec_hint, hint);3927 let outer_hint = std::mem::replace(&mut self.vec_hint, hint);
3916- let deferred = self.deferred_let_hint(l, rest);3928+ let deferred = self
3929+ .deferred_let_hint(l, rest)
3930+ .or_else(|| self.literal_let_hint(l, rest));
3917 let outer_def = std::mem::replace(&mut self.deferred_hint, deferred);3931 let outer_def = std::mem::replace(&mut self.deferred_hint, deferred);
3918 let r = self.local_inner(l);3932 let r = self.local_inner(l);
3919 self.vec_hint = outer_hint;3933 self.vec_hint = outer_hint;
@@ -3921,6 +3935,163 @@ impl Lowerer {
3921 return r;3935 return r;
3922 }3936 }
3923 3937
3938+ /// The type of `let x = 0;` -- an unsuffixed literal, which says only what
3939+ /// Rust *defaults* to -- read off the first place the binding meets a
3940+ /// typed value. `let mut i = 0; while i < samplepixels` makes `i` a
3941+ /// `usize` in Rust, and for the same reason here.
3942+ fn literal_let_hint(&mut self, l: &Local, rest: &[Stmt]) -> Option<Nim> {
3943+ if matches!(&l.pat, Pat::Type(_)) {
3944+ return None;
3945+ }
3946+ let Pat::Ident(id) = &l.pat else { return None };
3947+ let init = l.init.as_ref()?;
3948+ if !is_bare_literal(&init.expr) {
3949+ return None;
3950+ }
3951+ let name = id.ident.to_string();
3952+ // Types for bindings this scan will meet before they are lowered.
3953+ let outer = std::mem::take(&mut self.pending_lets);
3954+ self.collect_pending_lets(rest);
3955+ let mut found = None;
3956+ self.find_peer(&name, rest, &mut found);
3957+ self.pending_lets = outer;
3958+ found.filter(|t| t.is_integer() || matches!(t, Nim::Prim(p) if p.starts_with("float")))
3959+ }
3960+
3961+ /// Record what each later `let` in these statements binds, for peers that
3962+ /// are not in scope yet. Only the ones whose type is plain to see.
3963+ fn collect_pending_lets(&mut self, stmts: &[Stmt]) {
3964+ for st in stmts {
3965+ match st {
3966+ Stmt::Local(l) => {
3967+ let (n, ann) = match &l.pat {
3968+ Pat::Ident(i) => (i.ident.to_string(), None),
3969+ Pat::Type(t) => match &*t.pat {
3970+ Pat::Ident(i) => {
3971+ (i.ident.to_string(), self.map_ty(&t.ty).ok())
3972+ }
3973+ _ => continue,
3974+ },
3975+ _ => continue,
3976+ };
3977+ let t = ann.or_else(|| {
3978+ let i = l.init.as_ref()?;
3979+ if is_bare_literal(&i.expr) {
3980+ return None;
3981+ }
3982+ self.pushed_ty(&i.expr)
3983+ });
3984+ if let Some(t) = t {
3985+ self.pending_lets.entry(n).or_insert(t);
3986+ }
3987+ }
3988+ Stmt::Expr(e, _) => match e {
3989+ Expr::ForLoop(f) => {
3990+ // A `for i in 0..n` binding is typed by the range's
3991+ // bounds, and is a peer like any other.
3992+ if let (Pat::Ident(id), Expr::Range(r)) = (&*f.pat, strip_parens(&f.expr)) {
3993+ let b = r.end.as_deref().or(r.start.as_deref());
3994+ if let Some(b) = b {
3995+ if !is_bare_literal(b) {
3996+ if let Some(t) = self.pushed_ty(b) {
3997+ self.pending_lets
3998+ .entry(id.ident.to_string())
3999+ .or_insert(t);
4000+ }
4001+ }
4002+ }
4003+ }
4004+ self.collect_pending_lets(&f.body.stmts)
4005+ }
4006+ Expr::While(w) => self.collect_pending_lets(&w.body.stmts),
4007+ Expr::Loop(l) => self.collect_pending_lets(&l.body.stmts),
4008+ Expr::Block(b) => self.collect_pending_lets(&b.block.stmts),
4009+ _ => {}
4010+ },
4011+ _ => {}
4012+ }
4013+ }
4014+ }
4015+
4016+ /// The type of whatever `name` is first compared or combined with.
4017+ fn find_peer(&mut self, name: &str, stmts: &[Stmt], out: &mut Option<Nim>) {
4018+ for st in stmts {
4019+ if out.is_some() {
4020+ return;
4021+ }
4022+ match st {
4023+ Stmt::Expr(e, _) => self.find_peer_expr(name, e, out),
4024+ Stmt::Local(l) => {
4025+ if let Some(i) = &l.init {
4026+ self.find_peer_expr(name, &i.expr, out);
4027+ }
4028+ }
4029+ _ => {}
4030+ }
4031+ }
4032+ }
4033+
4034+ fn find_peer_expr(&mut self, name: &str, e: &Expr, out: &mut Option<Nim>) {
4035+ if out.is_some() {
4036+ return;
4037+ }
4038+ let names = |x: &Expr| {
4039+ matches!(x, Expr::Path(p)
4040+ if p.path.segments.len() == 1 && p.path.segments[0].ident == name)
4041+ };
4042+ match e {
4043+ Expr::Binary(b) => {
4044+ let other = if names(&b.left) {
4045+ Some(&*b.right)
4046+ } else if names(&b.right) {
4047+ Some(&*b.left)
4048+ } else {
4049+ None
4050+ };
4051+ if let Some(o) = other {
4052+ if !is_bare_literal(o) {
4053+ if let Some(t) = self.pushed_ty(o) {
4054+ *out = Some(t);
4055+ return;
4056+ }
4057+ // The peer may be declared *after* this binding and
4058+ // inside a loop, so it is not in scope yet. Its own
4059+ // `let` says what it is.
4060+ if let Expr::Path(p) = strip_parens(o) {
4061+ if p.path.segments.len() == 1 {
4062+ let n = p.path.segments[0].ident.to_string();
4063+ if let Some(t) = self.pending_lets.get(&n).cloned() {
4064+ *out = Some(t);
4065+ return;
4066+ }
4067+ }
4068+ }
4069+ }
4070+ }
4071+ self.find_peer_expr(name, &b.left, out);
4072+ self.find_peer_expr(name, &b.right, out);
4073+ }
4074+ Expr::While(w) => {
4075+ self.find_peer_expr(name, &w.cond, out);
4076+ self.find_peer(name, &w.body.stmts, out);
4077+ }
4078+ Expr::ForLoop(f) => self.find_peer(name, &f.body.stmts, out),
4079+ Expr::Loop(l) => self.find_peer(name, &l.body.stmts, out),
4080+ Expr::Block(b) => self.find_peer(name, &b.block.stmts, out),
4081+ Expr::Unsafe(u) => self.find_peer(name, &u.block.stmts, out),
4082+ Expr::If(i) => {
4083+ self.find_peer_expr(name, &i.cond, out);
4084+ self.find_peer(name, &i.then_branch.stmts, out);
4085+ }
4086+ Expr::Assign(a) => self.find_peer_expr(name, &a.right, out),
4087+ Expr::Index(ix) => {
4088+ // `xs[i]` makes `i` an index, which says nothing about width.
4089+ self.find_peer_expr(name, &ix.expr, out);
4090+ }
4091+ _ => {}
4092+ }
4093+ }
4094+
3924 /// The type of a `let x;` binding, read off the first assignment to it.4095 /// The type of a `let x;` binding, read off the first assignment to it.
3925 fn deferred_let_hint(&mut self, l: &Local, rest: &[Stmt]) -> Option<Nim> {4096 fn deferred_let_hint(&mut self, l: &Local, rest: &[Stmt]) -> Option<Nim> {
3926 if l.init.is_some() || matches!(&l.pat, Pat::Type(_)) {4097 if l.init.is_some() || matches!(&l.pat, Pat::Type(_)) {
@@ -4137,7 +4308,11 @@ impl Lowerer {
4137 return Ok(());4308 return Ok(());
4138 }4309 }
4139 4310
4140- let v = self.expr_at(&init.expr, ann.as_ref())?;4311+ // With no annotation, an unsuffixed literal initialiser takes the type
4312+ // of whatever the binding is first compared or combined with, which is
4313+ // where Rust gets it. `deferred_hint` carries that.
4314+ let want = ann.clone().or_else(|| self.deferred_hint.clone());
4315+ let v = self.expr_at(&init.expr, want.as_ref())?;
4141 4316
4142 // `let s = &buf[..n]` binds a view of a place that is already in4317 // `let s = &buf[..n]` binds a view of a place that is already in
4143 // scope. Nim's borrow checker will not let a `let` borrow out of a4318 // scope. Nim's borrow checker will not let a `let` borrow out of a
@@ -5876,6 +6051,28 @@ impl Lowerer {
5876 .ok_or("`Self` outside an `impl` block")?,6051 .ok_or("`Self` outside an `impl` block")?,
5877 other => other.to_string(),6052 other => other.to_string(),
5878 };6053 };
6054+ // `type Neuron = Quad<f64>;` used as a literal's name. An
6055+ // alias is expanded rather than emitted, so the name does not
6056+ // exist in Nim -- but the type it stands for does, and it
6057+ // carries the generic arguments the literal would otherwise
6058+ // have to infer.
6059+ let mut alias_args: Option<Vec<Nim>> = None;
6060+ let name = {
6061+ let ty = syn::Type::Path(syn::TypePath {
6062+ attrs: Vec::new(),
6063+ qself: None,
6064+ path: s.path.clone(),
6065+ });
6066+ match self.map_ty(&ty) {
6067+ Ok(Nim::Named(n, a)) if n != name && self.structs.contains_key(&n) => {
6068+ if !a.is_empty() {
6069+ alias_args = Some(a);
6070+ }
6071+ n
6072+ }
6073+ _ => name,
6074+ }
6075+ };
5879 let mut parts = Vec::new();6076 let mut parts = Vec::new();
5880 let mut field_tys: Vec<(Nim, Nim)> = Vec::new();6077 let mut field_tys: Vec<(Nim, Nim)> = Vec::new();
5881 for f in &s.fields {6078 for f in &s.fields {
@@ -5901,6 +6098,9 @@ impl Lowerer {
5901 Nim::Named(name.clone(), vec![])6098 Nim::Named(name.clone(), vec![])
5902 } else {6099 } else {
5903 match expect {6100 match expect {
6101+ _ if alias_args.as_ref().is_some_and(|a| a.len() == gp.len()) => {
6102+ Nim::Named(name.clone(), alias_args.clone().unwrap())
6103+ }
5904 Some(Nim::Named(n, a)) if *n == name && a.len() == gp.len() => {6104 Some(Nim::Named(n, a)) if *n == name && a.len() == gp.len() => {
5905 Nim::Named(name.clone(), a.clone())6105 Nim::Named(name.clone(), a.clone())
5906 }6106 }
@@ -7309,9 +7509,20 @@ impl Lowerer {
7309 }7509 }
7310 // `vec![v; n]`, after expansion.7510 // `vec![v; n]`, after expansion.
7311 "from_elem" if codes.len() == 2 => {7511 "from_elem" if codes.len() == 2 => {
7312- let t = args[0].ty.clone().map(|t| Nim::Seq(Box::new(t.owned())));7512+ // The element takes its type from the expected `Vec<T>` where
7513+ // there is one: `vec![0; 256]` into a `Vec<usize>` is a `usize`
7514+ // zero, and lowering the literal before knowing that makes it
7515+ // an `i32`.
7516+ let want = match expect {
7517+ Some(Nim::Seq(e)) | Some(Nim::OpenArray(e)) => Some((**e).clone()),
7518+ _ => None,
7519+ };
7520+ let v = self.expr_at(&c.args[0], want.as_ref())?;
7521+ let t = want
7522+ .or_else(|| v.ty.clone().map(|t| t.owned()))
7523+ .map(|t| Nim::Seq(Box::new(t)));
7313 return Ok(Val::new(7524 return Ok(Val::new(
7314- format!("newSeqWith(int({}), {})", codes[1], codes[0]),7525+ format!("newSeqWith(int({}), {})", codes[1], v.code),
7315 t,7526 t,
7316 ));7527 ));
7317 }7528 }
@@ -9581,3 +9792,24 @@ fn strip_parens(e: &Expr) -> &Expr {
9581 other => other,9792 other => other,
9582 }9793 }
9583 }9794 }
9795+
9796+/// Whether an expression is an integer or float literal with no suffix, whose
9797+/// type is therefore Rust's default rather than anything the program said.
9798+fn is_bare_literal(e: &Expr) -> bool {
9799+ match e {
9800+ Expr::Lit(l) => match &l.lit {
9801+ syn::Lit::Int(i) => i.suffix().is_empty(),
9802+ syn::Lit::Float(f) => f.suffix().is_empty(),
9803+ _ => false,
9804+ },
9805+ Expr::Unary(u) => is_bare_literal(&u.expr),
9806+ Expr::Paren(p) => is_bare_literal(&p.expr),
9807+ Expr::Group(g) => is_bare_literal(&g.expr),
9808+ Expr::Block(b) => b
9809+ .block
9810+ .stmts
9811+ .last()
9812+ .is_some_and(|s| matches!(s, Stmt::Expr(e, None) if is_bare_literal(e))),
9813+ _ => false,
9814+ }
9815+}