Lower shadowing, `T::Assoc` on a generic, and type the string macros
Three things `almost` needed, each a gap that had been sitting in plain sight. A `let` may shadow another in the same scope in Rust and may not in Nim. `let scale = ..; let scale = if scale > ..` is ordinary Rust; the second binding now gets a name of its own and every later use follows it, which is what shadowing means. The name is chosen *after* the initialiser is lowered, so `let x = x + 1` still reads the old `x`. `T::Float` where `T` is a generic parameter becomes a generic parameter of its own, inferred from the argument at each call. Nim has no way to say "the Float of T", and this is a weaker static check than Rust's -- Nim will not verify the argument really is `T::Float`. It is not a weaker *program*, since rustc checked that before any of this ran, but the check does not survive the translation and the comment says so. And a macro in value position was typed as nothing at all unless it was `write!`. `format!` produces a `String` and always has, so `let s = format!(..)` failed for want of an annotation. That one was not mine: it predates this session's work, which I checked by rebuilding at HEAD before assuming. tests/cases/073. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
c9a5d39 parent: 4d9142c modified
src/lower.rs +145 -6 | @@ -389,6 +389,15 @@ pub struct Lowerer { | ||
| 389 | 389 | /// Return type to use instead of the signature's, for a default body |
| 390 | 390 | /// whose return type Nim cannot infer. |
| 391 | 391 | ret_override: Option<Nim>, |
| 392 | + /// Names invented for `T::Assoc` where `T` is a generic parameter. | |
| 393 | + /// They become generic parameters of whatever signature mentions them. | |
| 394 | + assoc_params: std::cell::RefCell<std::collections::HashSet<String>>, | |
| 395 | + /// Generic parameters of the signature currently being mapped. `signature` | |
| 396 | + /// takes `&self`, and a `T::Assoc` in it needs to know `T` is one. | |
| 397 | + cur_sig_generics: std::cell::RefCell<Vec<String>>, | |
| 398 | + /// Nim names for locals whose Rust name is shadowed. | |
| 399 | + shadow: Vec<HashMap<String, String>>, | |
| 400 | + shadow_n: usize, | |
| 392 | 401 | /// Procs that cannot be translated, by Nim name, with the reason. |
| 393 | 402 | /// |
| 394 | 403 | /// Not an error on its own. A dependency graph carries far more code than |
| @@ -515,6 +524,10 @@ impl Lowerer { | ||
| 515 | 524 | fn_bounds: HashMap::new(), |
| 516 | 525 | type_traits: HashMap::new(), |
| 517 | 526 | ret_override: None, |
| 527 | + assoc_params: std::cell::RefCell::new(std::collections::HashSet::new()), | |
| 528 | + cur_sig_generics: std::cell::RefCell::new(Vec::new()), | |
| 529 | + shadow: vec![HashMap::new()], | |
| 530 | + shadow_n: 0, | |
| 518 | 531 | untranslatable: HashMap::new(), |
| 519 | 532 | enum_module: HashMap::new(), |
| 520 | 533 | report: false, |
| @@ -564,10 +577,45 @@ impl Lowerer { | ||
| 564 | 577 | fn push_scope(&mut self) { |
| 565 | 578 | self.scopes.push(HashMap::new()); |
| 566 | 579 | self.alias_scopes.push(HashMap::new()); |
| 580 | + self.shadow.push(HashMap::new()); | |
| 567 | 581 | } |
| 568 | 582 | fn pop_scope(&mut self) { |
| 569 | 583 | self.scopes.pop(); |
| 570 | 584 | self.alias_scopes.pop(); |
| 585 | + self.shadow.pop(); | |
| 586 | + } | |
| 587 | + | |
| 588 | + /// The Nim name a local goes by. | |
| 589 | + /// | |
| 590 | + /// Rust lets a `let` shadow another in the same scope -- `let scale = ..; | |
| 591 | + /// let scale = if scale > ..` is ordinary Rust -- and Nim does not. The | |
| 592 | + /// second binding gets a name of its own and every later use follows it, | |
| 593 | + /// which is what shadowing means. | |
| 594 | + fn local_name(&self, rust: &str) -> String { | |
| 595 | + for m in self.shadow.iter().rev() { | |
| 596 | + if let Some(n) = m.get(rust) { | |
| 597 | + return n.clone(); | |
| 598 | + } | |
| 599 | + } | |
| 600 | + ident(rust) | |
| 601 | + } | |
| 602 | + | |
| 603 | + /// Introduce a binding, giving it a fresh name if it shadows one that is | |
| 604 | + /// still live. | |
| 605 | + fn bind_local(&mut self, rust: &str) -> String { | |
| 606 | + let shadowed = self.lookup(rust).is_some() || self.lookup_alias(rust).is_some(); | |
| 607 | + let nim = if shadowed { | |
| 608 | + let n = self.shadow_n; | |
| 609 | + self.shadow_n += 1; | |
| 610 | + format!("{}_s{}", ident(rust), n) | |
| 611 | + } else { | |
| 612 | + ident(rust) | |
| 613 | + }; | |
| 614 | + self.shadow | |
| 615 | + .last_mut() | |
| 616 | + .unwrap() | |
| 617 | + .insert(rust.to_string(), nim.clone()); | |
| 618 | + nim | |
| 571 | 619 | } |
| 572 | 620 | fn bind_alias(&mut self, name: &str, a: Alias) { |
| 573 | 621 | self.alias_scopes |
| @@ -1615,6 +1663,33 @@ impl Lowerer { | ||
| 1615 | 1663 | )); |
| 1616 | 1664 | } |
| 1617 | 1665 | } |
| 1666 | + // `T::Float` where `T` is a generic parameter. Which type that is | |
| 1667 | + // depends on the instantiation, and Nim has no way to say "the Float | |
| 1668 | + // of T" -- so it becomes a parameter of its own, inferred from the | |
| 1669 | + // argument at each call. | |
| 1670 | + // | |
| 1671 | + // That is a weaker static check than Rust's: Nim will not verify the | |
| 1672 | + // argument really is `T::Float`. It is not a weaker *program* -- rustc | |
| 1673 | + // checked that before this ran, and nothing about what the code does | |
| 1674 | + // changes -- but the check does not survive the translation, and that | |
| 1675 | + // is worth knowing. | |
| 1676 | + if let syn::Type::Path(p) = t { | |
| 1677 | + let segs: Vec<String> = | |
| 1678 | + p.path.segments.iter().map(|s| s.ident.to_string()).collect(); | |
| 1679 | + if segs.len() == 2 | |
| 1680 | + && (self | |
| 1681 | + .fn_generics | |
| 1682 | + .iter() | |
| 1683 | + .chain(self.impl_generics.iter()) | |
| 1684 | + .any(|g| *g == segs[0]) | |
| 1685 | + || self.cur_sig_generics.borrow().iter().any(|g| *g == segs[0])) | |
| 1686 | + && self.assoc.get(&(segs[0].clone(), segs[1].clone())).is_none() | |
| 1687 | + { | |
| 1688 | + let n = format!("{}_{}", segs[0], segs[1]); | |
| 1689 | + self.assoc_params.borrow_mut().insert(n.clone()); | |
| 1690 | + return Ok(Nim::Named(n, Vec::new())); | |
| 1691 | + } | |
| 1692 | + } | |
| 1618 | 1693 | // `Self::Item` names an associated type of the enclosing `impl`. |
| 1619 | 1694 | if let syn::Type::Path(p) = t { |
| 1620 | 1695 | let segs: Vec<String> = |
| @@ -2169,6 +2244,34 @@ impl Lowerer { | ||
| 2169 | 2244 | format!("[{}]", out.join(", ")) |
| 2170 | 2245 | } |
| 2171 | 2246 | |
| 2247 | + /// Add any invented `T::Assoc` names this signature mentions to its | |
| 2248 | + /// generic parameters. They are inferred from the arguments at each call, | |
| 2249 | + /// which is the only way Nim can express "the Assoc of T". | |
| 2250 | + fn add_assoc_params( | |
| 2251 | + &self, | |
| 2252 | + gparams: &mut Vec<String>, | |
| 2253 | + ptys: &[Nim], | |
| 2254 | + ret: &Nim, | |
| 2255 | + recv: Option<&Nim>, | |
| 2256 | + ) { | |
| 2257 | + let invented = self.assoc_params.borrow().clone(); | |
| 2258 | + let mut mentioned: Vec<String> = invented | |
| 2259 | + .into_iter() | |
| 2260 | + .filter(|n| { | |
| 2261 | + let probe = [n.clone()]; | |
| 2262 | + ptys.iter().any(|p| Self::mentions_any(p, &probe)) | |
| 2263 | + || Self::mentions_any(ret, &probe) | |
| 2264 | + || recv.is_some_and(|r| Self::mentions_any(r, &probe)) | |
| 2265 | + }) | |
| 2266 | + .collect(); | |
| 2267 | + mentioned.sort(); | |
| 2268 | + for n in mentioned { | |
| 2269 | + if !gparams.contains(&n) { | |
| 2270 | + gparams.push(n); | |
| 2271 | + } | |
| 2272 | + } | |
| 2273 | + } | |
| 2274 | + | |
| 2172 | 2275 | fn gen_list(&self, params: &[String]) -> String { |
| 2173 | 2276 | if params.is_empty() { |
| 2174 | 2277 | return String::new(); |
| @@ -2249,6 +2352,7 @@ impl Lowerer { | ||
| 2249 | 2352 | params.push(g); |
| 2250 | 2353 | } |
| 2251 | 2354 | } |
| 2355 | + self.add_assoc_params(&mut params, &ptys, &ret, recv); | |
| 2252 | 2356 | let gens = self.gen_list(¶ms); |
| 2253 | 2357 | let mut parts = Vec::new(); |
| 2254 | 2358 | if let Some(self_ty) = recv { |
| @@ -2679,6 +2783,16 @@ impl Lowerer { | ||
| 2679 | 2783 | } |
| 2680 | 2784 | |
| 2681 | 2785 | fn signature(&self, sig: &syn::Signature) -> Result<(Vec<Nim>, Nim), String> { |
| 2786 | + let outer = std::mem::replace( | |
| 2787 | + &mut *self.cur_sig_generics.borrow_mut(), | |
| 2788 | + Self::generics_of(&sig.generics), | |
| 2789 | + ); | |
| 2790 | + let r = self.signature_inner(sig); | |
| 2791 | + *self.cur_sig_generics.borrow_mut() = outer; | |
| 2792 | + r | |
| 2793 | + } | |
| 2794 | + | |
| 2795 | + fn signature_inner(&self, sig: &syn::Signature) -> Result<(Vec<Nim>, Nim), String> { | |
| 2682 | 2796 | // `unsafe fn` marks a contract for callers; it does not change what |
| 2683 | 2797 | // the body means, so it lowers like any other proc. |
| 2684 | 2798 | // Lifetime parameters carry no runtime meaning and Nim is GC'd, so |
| @@ -3851,6 +3965,8 @@ impl Lowerer { | ||
| 3851 | 3965 | gparams.push(g); |
| 3852 | 3966 | } |
| 3853 | 3967 | } |
| 3968 | + self.add_assoc_params(&mut gparams, &ptys, &ret, recv_ty.as_ref()); | |
| 3969 | + | |
| 3854 | 3970 | // A parameter that appears in no argument cannot be bound by a call. |
| 3855 | 3971 | // With an `auto` return there is nothing else to bind it either, so it |
| 3856 | 3972 | // is not a parameter at all -- it was only ever the return type's name. |
| @@ -4509,9 +4625,16 @@ impl Lowerer { | ||
| 4509 | 4625 | if let Expr::Cast(c) = e { |
| 4510 | 4626 | return self.map_ty(&c.ty).ok(); |
| 4511 | 4627 | } |
| 4628 | + // Everything the probe can touch is restored, not just `out`: a | |
| 4629 | + // `format!` hoists, and a hoist left behind turns up as a binding the | |
| 4630 | + // real lowering never made. | |
| 4512 | 4631 | let save = self.out.len(); |
| 4632 | + let saved_hoist = self.hoisted.clone(); | |
| 4633 | + let saved_tmp = self.tmp; | |
| 4513 | 4634 | let t = self.expr(e).ok().and_then(|v| v.ty); |
| 4514 | 4635 | self.out.truncate(save); |
| 4636 | + self.hoisted = saved_hoist; | |
| 4637 | + self.tmp = saved_tmp; | |
| 4515 | 4638 | t.map(|t| t.owned()) |
| 4516 | 4639 | } |
| 4517 | 4640 | |
| @@ -4675,10 +4798,13 @@ impl Lowerer { | ||
| 4675 | 4798 | let kw = if mutable { "var" } else { "let" }; |
| 4676 | 4799 | // Inside a generic proc the binding's type may mention a parameter Nim |
| 4677 | 4800 | // will infer; naming it in an annotation would not resolve. |
| 4801 | + // The binding's own name comes last: `let x = x + 1` reads the old | |
| 4802 | + // `x` on the right, so the new name must not be in scope until after. | |
| 4803 | + let nim = self.bind_local(&name); | |
| 4678 | 4804 | let line = if self.mentions_type_param(&t) { |
| 4679 | - format!("{} {} = {}", kw, ident(&name), v.code) | |
| 4805 | + format!("{} {} = {}", kw, nim, v.code) | |
| 4680 | 4806 | } else { |
| 4681 | - format!("{} {}: {} = {}", kw, ident(&name), t.render(), v.code) | |
| 4807 | + format!("{} {}: {} = {}", kw, nim, t.render(), v.code) | |
| 4682 | 4808 | }; |
| 4683 | 4809 | self.line(&line); |
| 4684 | 4810 | self.bind(&name, t); |
| @@ -6174,7 +6300,7 @@ impl Lowerer { | ||
| 6174 | 6300 | }); |
| 6175 | 6301 | } |
| 6176 | 6302 | if let Some(t) = self.lookup(&name) { |
| 6177 | - return Ok(Val::new(ident(&name), Some(t))); | |
| 6303 | + return Ok(Val::new(self.local_name(&name), Some(t))); | |
| 6178 | 6304 | } |
| 6179 | 6305 | // A top-level function used as a value, e.g. passed to a |
| 6180 | 6306 | // parameter of `impl Fn(..)` type. |
| @@ -6332,10 +6458,23 @@ impl Lowerer { | ||
| 6332 | 6458 | Ok(Val::new(code, ty)) |
| 6333 | 6459 | } |
| 6334 | 6460 | Expr::Macro(m) => { |
| 6335 | - let is_write = matches!(path_name(&m.mac.path).as_str(), "write" | "writeln"); | |
| 6461 | + let which = path_name(&m.mac.path); | |
| 6336 | 6462 | let code = self.macro_call(&m.mac)?; |
| 6337 | - // A formatter write is a statement that appends, not a value. | |
| 6338 | - let ty = if is_write { Some(Nim::Unit) } else { None }; | |
| 6463 | + let ty = match which.as_str() { | |
| 6464 | + // A formatter write is a statement that appends, not a | |
| 6465 | + // value. | |
| 6466 | + "write" | "writeln" => Some(Nim::Unit), | |
| 6467 | + // These produce a string and always did; saying so is what | |
| 6468 | + // lets `let s = format!(..)` be typed without an | |
| 6469 | + // annotation. | |
| 6470 | + "format" | "format_args" | "format_args_nl" | "concat" | |
| 6471 | + | "stringify" | "env" | "include_str" => { | |
| 6472 | + Some(Nim::Prim("string".into())) | |
| 6473 | + } | |
| 6474 | + "panic" | "unreachable" | "assert" | "assert_eq" | "assert_ne" | |
| 6475 | + | "debug_assert" | "todo" | "unimplemented" => Some(Nim::Unit), | |
| 6476 | + _ => None, | |
| 6477 | + }; | |
| 6339 | 6478 | Ok(Val::new(code, ty)) |
| 6340 | 6479 | } |
| 6341 | 6480 | Expr::Struct(s) => { |
| @@ -389,6 +389,15 @@ pub struct Lowerer { | |||
| 389 | /// Return type to use instead of the signature's, for a default body | 389 | /// Return type to use instead of the signature's, for a default body |
| 390 | /// whose return type Nim cannot infer. | 390 | /// whose return type Nim cannot infer. |
| 391 | ret_override: Option<Nim>, | 391 | ret_override: Option<Nim>, |
| 392 | + /// Names invented for `T::Assoc` where `T` is a generic parameter. | ||
| 393 | + /// They become generic parameters of whatever signature mentions them. | ||
| 394 | + assoc_params: std::cell::RefCell<std::collections::HashSet<String>>, | ||
| 395 | + /// Generic parameters of the signature currently being mapped. `signature` | ||
| 396 | + /// takes `&self`, and a `T::Assoc` in it needs to know `T` is one. | ||
| 397 | + cur_sig_generics: std::cell::RefCell<Vec<String>>, | ||
| 398 | + /// Nim names for locals whose Rust name is shadowed. | ||
| 399 | + shadow: Vec<HashMap<String, String>>, | ||
| 400 | + shadow_n: usize, | ||
| 392 | /// Procs that cannot be translated, by Nim name, with the reason. | 401 | /// Procs that cannot be translated, by Nim name, with the reason. |
| 393 | /// | 402 | /// |
| 394 | /// Not an error on its own. A dependency graph carries far more code than | 403 | /// Not an error on its own. A dependency graph carries far more code than |
| @@ -515,6 +524,10 @@ impl Lowerer { | |||
| 515 | fn_bounds: HashMap::new(), | 524 | fn_bounds: HashMap::new(), |
| 516 | type_traits: HashMap::new(), | 525 | type_traits: HashMap::new(), |
| 517 | ret_override: None, | 526 | ret_override: None, |
| 527 | + assoc_params: std::cell::RefCell::new(std::collections::HashSet::new()), | ||
| 528 | + cur_sig_generics: std::cell::RefCell::new(Vec::new()), | ||
| 529 | + shadow: vec![HashMap::new()], | ||
| 530 | + shadow_n: 0, | ||
| 518 | untranslatable: HashMap::new(), | 531 | untranslatable: HashMap::new(), |
| 519 | enum_module: HashMap::new(), | 532 | enum_module: HashMap::new(), |
| 520 | report: false, | 533 | report: false, |
| @@ -564,10 +577,45 @@ impl Lowerer { | |||
| 564 | fn push_scope(&mut self) { | 577 | fn push_scope(&mut self) { |
| 565 | self.scopes.push(HashMap::new()); | 578 | self.scopes.push(HashMap::new()); |
| 566 | self.alias_scopes.push(HashMap::new()); | 579 | self.alias_scopes.push(HashMap::new()); |
| 580 | + self.shadow.push(HashMap::new()); | ||
| 567 | } | 581 | } |
| 568 | fn pop_scope(&mut self) { | 582 | fn pop_scope(&mut self) { |
| 569 | self.scopes.pop(); | 583 | self.scopes.pop(); |
| 570 | self.alias_scopes.pop(); | 584 | self.alias_scopes.pop(); |
| 585 | + self.shadow.pop(); | ||
| 586 | + } | ||
| 587 | + | ||
| 588 | + /// The Nim name a local goes by. | ||
| 589 | + /// | ||
| 590 | + /// Rust lets a `let` shadow another in the same scope -- `let scale = ..; | ||
| 591 | + /// let scale = if scale > ..` is ordinary Rust -- and Nim does not. The | ||
| 592 | + /// second binding gets a name of its own and every later use follows it, | ||
| 593 | + /// which is what shadowing means. | ||
| 594 | + fn local_name(&self, rust: &str) -> String { | ||
| 595 | + for m in self.shadow.iter().rev() { | ||
| 596 | + if let Some(n) = m.get(rust) { | ||
| 597 | + return n.clone(); | ||
| 598 | + } | ||
| 599 | + } | ||
| 600 | + ident(rust) | ||
| 601 | + } | ||
| 602 | + | ||
| 603 | + /// Introduce a binding, giving it a fresh name if it shadows one that is | ||
| 604 | + /// still live. | ||
| 605 | + fn bind_local(&mut self, rust: &str) -> String { | ||
| 606 | + let shadowed = self.lookup(rust).is_some() || self.lookup_alias(rust).is_some(); | ||
| 607 | + let nim = if shadowed { | ||
| 608 | + let n = self.shadow_n; | ||
| 609 | + self.shadow_n += 1; | ||
| 610 | + format!("{}_s{}", ident(rust), n) | ||
| 611 | + } else { | ||
| 612 | + ident(rust) | ||
| 613 | + }; | ||
| 614 | + self.shadow | ||
| 615 | + .last_mut() | ||
| 616 | + .unwrap() | ||
| 617 | + .insert(rust.to_string(), nim.clone()); | ||
| 618 | + nim | ||
| 571 | } | 619 | } |
| 572 | fn bind_alias(&mut self, name: &str, a: Alias) { | 620 | fn bind_alias(&mut self, name: &str, a: Alias) { |
| 573 | self.alias_scopes | 621 | self.alias_scopes |
| @@ -1615,6 +1663,33 @@ impl Lowerer { | |||
| 1615 | )); | 1663 | )); |
| 1616 | } | 1664 | } |
| 1617 | } | 1665 | } |
| 1666 | + // `T::Float` where `T` is a generic parameter. Which type that is | ||
| 1667 | + // depends on the instantiation, and Nim has no way to say "the Float | ||
| 1668 | + // of T" -- so it becomes a parameter of its own, inferred from the | ||
| 1669 | + // argument at each call. | ||
| 1670 | + // | ||
| 1671 | + // That is a weaker static check than Rust's: Nim will not verify the | ||
| 1672 | + // argument really is `T::Float`. It is not a weaker *program* -- rustc | ||
| 1673 | + // checked that before this ran, and nothing about what the code does | ||
| 1674 | + // changes -- but the check does not survive the translation, and that | ||
| 1675 | + // is worth knowing. | ||
| 1676 | + if let syn::Type::Path(p) = t { | ||
| 1677 | + let segs: Vec<String> = | ||
| 1678 | + p.path.segments.iter().map(|s| s.ident.to_string()).collect(); | ||
| 1679 | + if segs.len() == 2 | ||
| 1680 | + && (self | ||
| 1681 | + .fn_generics | ||
| 1682 | + .iter() | ||
| 1683 | + .chain(self.impl_generics.iter()) | ||
| 1684 | + .any(|g| *g == segs[0]) | ||
| 1685 | + || self.cur_sig_generics.borrow().iter().any(|g| *g == segs[0])) | ||
| 1686 | + && self.assoc.get(&(segs[0].clone(), segs[1].clone())).is_none() | ||
| 1687 | + { | ||
| 1688 | + let n = format!("{}_{}", segs[0], segs[1]); | ||
| 1689 | + self.assoc_params.borrow_mut().insert(n.clone()); | ||
| 1690 | + return Ok(Nim::Named(n, Vec::new())); | ||
| 1691 | + } | ||
| 1692 | + } | ||
| 1618 | // `Self::Item` names an associated type of the enclosing `impl`. | 1693 | // `Self::Item` names an associated type of the enclosing `impl`. |
| 1619 | if let syn::Type::Path(p) = t { | 1694 | if let syn::Type::Path(p) = t { |
| 1620 | let segs: Vec<String> = | 1695 | let segs: Vec<String> = |
| @@ -2169,6 +2244,34 @@ impl Lowerer { | |||
| 2169 | format!("[{}]", out.join(", ")) | 2244 | format!("[{}]", out.join(", ")) |
| 2170 | } | 2245 | } |
| 2171 | 2246 | ||
| 2247 | + /// Add any invented `T::Assoc` names this signature mentions to its | ||
| 2248 | + /// generic parameters. They are inferred from the arguments at each call, | ||
| 2249 | + /// which is the only way Nim can express "the Assoc of T". | ||
| 2250 | + fn add_assoc_params( | ||
| 2251 | + &self, | ||
| 2252 | + gparams: &mut Vec<String>, | ||
| 2253 | + ptys: &[Nim], | ||
| 2254 | + ret: &Nim, | ||
| 2255 | + recv: Option<&Nim>, | ||
| 2256 | + ) { | ||
| 2257 | + let invented = self.assoc_params.borrow().clone(); | ||
| 2258 | + let mut mentioned: Vec<String> = invented | ||
| 2259 | + .into_iter() | ||
| 2260 | + .filter(|n| { | ||
| 2261 | + let probe = [n.clone()]; | ||
| 2262 | + ptys.iter().any(|p| Self::mentions_any(p, &probe)) | ||
| 2263 | + || Self::mentions_any(ret, &probe) | ||
| 2264 | + || recv.is_some_and(|r| Self::mentions_any(r, &probe)) | ||
| 2265 | + }) | ||
| 2266 | + .collect(); | ||
| 2267 | + mentioned.sort(); | ||
| 2268 | + for n in mentioned { | ||
| 2269 | + if !gparams.contains(&n) { | ||
| 2270 | + gparams.push(n); | ||
| 2271 | + } | ||
| 2272 | + } | ||
| 2273 | + } | ||
| 2274 | + | ||
| 2172 | fn gen_list(&self, params: &[String]) -> String { | 2275 | fn gen_list(&self, params: &[String]) -> String { |
| 2173 | if params.is_empty() { | 2276 | if params.is_empty() { |
| 2174 | return String::new(); | 2277 | return String::new(); |
| @@ -2249,6 +2352,7 @@ impl Lowerer { | |||
| 2249 | params.push(g); | 2352 | params.push(g); |
| 2250 | } | 2353 | } |
| 2251 | } | 2354 | } |
| 2355 | + self.add_assoc_params(&mut params, &ptys, &ret, recv); | ||
| 2252 | let gens = self.gen_list(¶ms); | 2356 | let gens = self.gen_list(¶ms); |
| 2253 | let mut parts = Vec::new(); | 2357 | let mut parts = Vec::new(); |
| 2254 | if let Some(self_ty) = recv { | 2358 | if let Some(self_ty) = recv { |
| @@ -2679,6 +2783,16 @@ impl Lowerer { | |||
| 2679 | } | 2783 | } |
| 2680 | 2784 | ||
| 2681 | fn signature(&self, sig: &syn::Signature) -> Result<(Vec<Nim>, Nim), String> { | 2785 | fn signature(&self, sig: &syn::Signature) -> Result<(Vec<Nim>, Nim), String> { |
| 2786 | + let outer = std::mem::replace( | ||
| 2787 | + &mut *self.cur_sig_generics.borrow_mut(), | ||
| 2788 | + Self::generics_of(&sig.generics), | ||
| 2789 | + ); | ||
| 2790 | + let r = self.signature_inner(sig); | ||
| 2791 | + *self.cur_sig_generics.borrow_mut() = outer; | ||
| 2792 | + r | ||
| 2793 | + } | ||
| 2794 | + | ||
| 2795 | + fn signature_inner(&self, sig: &syn::Signature) -> Result<(Vec<Nim>, Nim), String> { | ||
| 2682 | // `unsafe fn` marks a contract for callers; it does not change what | 2796 | // `unsafe fn` marks a contract for callers; it does not change what |
| 2683 | // the body means, so it lowers like any other proc. | 2797 | // the body means, so it lowers like any other proc. |
| 2684 | // Lifetime parameters carry no runtime meaning and Nim is GC'd, so | 2798 | // Lifetime parameters carry no runtime meaning and Nim is GC'd, so |
| @@ -3851,6 +3965,8 @@ impl Lowerer { | |||
| 3851 | gparams.push(g); | 3965 | gparams.push(g); |
| 3852 | } | 3966 | } |
| 3853 | } | 3967 | } |
| 3968 | + self.add_assoc_params(&mut gparams, &ptys, &ret, recv_ty.as_ref()); | ||
| 3969 | + | ||
| 3854 | // A parameter that appears in no argument cannot be bound by a call. | 3970 | // A parameter that appears in no argument cannot be bound by a call. |
| 3855 | // With an `auto` return there is nothing else to bind it either, so it | 3971 | // With an `auto` return there is nothing else to bind it either, so it |
| 3856 | // is not a parameter at all -- it was only ever the return type's name. | 3972 | // is not a parameter at all -- it was only ever the return type's name. |
| @@ -4509,9 +4625,16 @@ impl Lowerer { | |||
| 4509 | if let Expr::Cast(c) = e { | 4625 | if let Expr::Cast(c) = e { |
| 4510 | return self.map_ty(&c.ty).ok(); | 4626 | return self.map_ty(&c.ty).ok(); |
| 4511 | } | 4627 | } |
| 4628 | + // Everything the probe can touch is restored, not just `out`: a | ||
| 4629 | + // `format!` hoists, and a hoist left behind turns up as a binding the | ||
| 4630 | + // real lowering never made. | ||
| 4512 | let save = self.out.len(); | 4631 | let save = self.out.len(); |
| 4632 | + let saved_hoist = self.hoisted.clone(); | ||
| 4633 | + let saved_tmp = self.tmp; | ||
| 4513 | let t = self.expr(e).ok().and_then(|v| v.ty); | 4634 | let t = self.expr(e).ok().and_then(|v| v.ty); |
| 4514 | self.out.truncate(save); | 4635 | self.out.truncate(save); |
| 4636 | + self.hoisted = saved_hoist; | ||
| 4637 | + self.tmp = saved_tmp; | ||
| 4515 | t.map(|t| t.owned()) | 4638 | t.map(|t| t.owned()) |
| 4516 | } | 4639 | } |
| 4517 | 4640 | ||
| @@ -4675,10 +4798,13 @@ impl Lowerer { | |||
| 4675 | let kw = if mutable { "var" } else { "let" }; | 4798 | let kw = if mutable { "var" } else { "let" }; |
| 4676 | // Inside a generic proc the binding's type may mention a parameter Nim | 4799 | // Inside a generic proc the binding's type may mention a parameter Nim |
| 4677 | // will infer; naming it in an annotation would not resolve. | 4800 | // will infer; naming it in an annotation would not resolve. |
| 4801 | + // The binding's own name comes last: `let x = x + 1` reads the old | ||
| 4802 | + // `x` on the right, so the new name must not be in scope until after. | ||
| 4803 | + let nim = self.bind_local(&name); | ||
| 4678 | let line = if self.mentions_type_param(&t) { | 4804 | let line = if self.mentions_type_param(&t) { |
| 4679 | - format!("{} {} = {}", kw, ident(&name), v.code) | 4805 | + format!("{} {} = {}", kw, nim, v.code) |
| 4680 | } else { | 4806 | } else { |
| 4681 | - format!("{} {}: {} = {}", kw, ident(&name), t.render(), v.code) | 4807 | + format!("{} {}: {} = {}", kw, nim, t.render(), v.code) |
| 4682 | }; | 4808 | }; |
| 4683 | self.line(&line); | 4809 | self.line(&line); |
| 4684 | self.bind(&name, t); | 4810 | self.bind(&name, t); |
| @@ -6174,7 +6300,7 @@ impl Lowerer { | |||
| 6174 | }); | 6300 | }); |
| 6175 | } | 6301 | } |
| 6176 | if let Some(t) = self.lookup(&name) { | 6302 | if let Some(t) = self.lookup(&name) { |
| 6177 | - return Ok(Val::new(ident(&name), Some(t))); | 6303 | + return Ok(Val::new(self.local_name(&name), Some(t))); |
| 6178 | } | 6304 | } |
| 6179 | // A top-level function used as a value, e.g. passed to a | 6305 | // A top-level function used as a value, e.g. passed to a |
| 6180 | // parameter of `impl Fn(..)` type. | 6306 | // parameter of `impl Fn(..)` type. |
| @@ -6332,10 +6458,23 @@ impl Lowerer { | |||
| 6332 | Ok(Val::new(code, ty)) | 6458 | Ok(Val::new(code, ty)) |
| 6333 | } | 6459 | } |
| 6334 | Expr::Macro(m) => { | 6460 | Expr::Macro(m) => { |
| 6335 | - let is_write = matches!(path_name(&m.mac.path).as_str(), "write" | "writeln"); | 6461 | + let which = path_name(&m.mac.path); |
| 6336 | let code = self.macro_call(&m.mac)?; | 6462 | let code = self.macro_call(&m.mac)?; |
| 6337 | - // A formatter write is a statement that appends, not a value. | 6463 | + let ty = match which.as_str() { |
| 6338 | - let ty = if is_write { Some(Nim::Unit) } else { None }; | 6464 | + // A formatter write is a statement that appends, not a |
| 6465 | + // value. | ||
| 6466 | + "write" | "writeln" => Some(Nim::Unit), | ||
| 6467 | + // These produce a string and always did; saying so is what | ||
| 6468 | + // lets `let s = format!(..)` be typed without an | ||
| 6469 | + // annotation. | ||
| 6470 | + "format" | "format_args" | "format_args_nl" | "concat" | ||
| 6471 | + | "stringify" | "env" | "include_str" => { | ||
| 6472 | + Some(Nim::Prim("string".into())) | ||
| 6473 | + } | ||
| 6474 | + "panic" | "unreachable" | "assert" | "assert_eq" | "assert_ne" | ||
| 6475 | + | "debug_assert" | "todo" | "unimplemented" => Some(Nim::Unit), | ||
| 6476 | + _ => None, | ||
| 6477 | + }; | ||
| 6339 | Ok(Val::new(code, ty)) | 6478 | Ok(Val::new(code, ty)) |
| 6340 | } | 6479 | } |
| 6341 | Expr::Struct(s) => { | 6480 | Expr::Struct(s) => { |
added
tests/cases/073-shadowing.rs +30 -0 | new file mode 100644 | ||
| @@ -0,0 +1,30 @@ | ||
| 1 | +// Rust lets a `let` shadow another in the same scope; Nim does not. The second | |
| 2 | +// binding gets a name of its own and every later use follows it — which is | |
| 3 | +// what shadowing means. | |
| 4 | +fn scale(a: f64, b: f64) -> f64 { | |
| 5 | + let scale = if a > b { a } else { b }; | |
| 6 | + let scale = if scale > 1.0 { scale } else { 1.0 }; | |
| 7 | + scale * 2.0 | |
| 8 | +} | |
| 9 | + | |
| 10 | +fn steps(n: u32) -> String { | |
| 11 | + let v = n; | |
| 12 | + let v = v + 1; | |
| 13 | + let v = v * 2; | |
| 14 | + // A shadow of a different type entirely. | |
| 15 | + let v = format!("{}", v); | |
| 16 | + v | |
| 17 | +} | |
| 18 | + | |
| 19 | +fn main() { | |
| 20 | + println!("{} {}", scale(3.0, 7.0), scale(0.1, 0.2)); | |
| 21 | + println!("{}", steps(5)); | |
| 22 | + | |
| 23 | + // Shadowing in a nested scope leaves the outer one alone. | |
| 24 | + let x = 1u32; | |
| 25 | + { | |
| 26 | + let x = x + 10; | |
| 27 | + println!("inner {}", x); | |
| 28 | + } | |
| 29 | + println!("outer {}", x); | |
| 30 | +} | |
| new file mode 100644 | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | +// Rust lets a `let` shadow another in the same scope; Nim does not. The second | ||
| 2 | +// binding gets a name of its own and every later use follows it — which is | ||
| 3 | +// what shadowing means. | ||
| 4 | +fn scale(a: f64, b: f64) -> f64 { | ||
| 5 | + let scale = if a > b { a } else { b }; | ||
| 6 | + let scale = if scale > 1.0 { scale } else { 1.0 }; | ||
| 7 | + scale * 2.0 | ||
| 8 | +} | ||
| 9 | + | ||
| 10 | +fn steps(n: u32) -> String { | ||
| 11 | + let v = n; | ||
| 12 | + let v = v + 1; | ||
| 13 | + let v = v * 2; | ||
| 14 | + // A shadow of a different type entirely. | ||
| 15 | + let v = format!("{}", v); | ||
| 16 | + v | ||
| 17 | +} | ||
| 18 | + | ||
| 19 | +fn main() { | ||
| 20 | + println!("{} {}", scale(3.0, 7.0), scale(0.1, 0.2)); | ||
| 21 | + println!("{}", steps(5)); | ||
| 22 | + | ||
| 23 | + // Shadowing in a nested scope leaves the outer one alone. | ||
| 24 | + let x = 1u32; | ||
| 25 | + { | ||
| 26 | + let x = x + 10; | ||
| 27 | + println!("inner {}", x); | ||
| 28 | + } | ||
| 29 | + println!("outer {}", x); | ||
| 30 | +} | ||