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

Make opaque-type resolution finish, and read arms correctly

The 357-crate run stopped failing and started never finishing. Three costs,
all introduced with the opaque-return-type work and all invisible on one
crate:

  * `refill_const_names` rebuilt a map over every enum and every associated
    const on *every* `map_ty` call. This was almost all of it -- thirty crates
    went from over two minutes to twenty-three seconds once it rebuilds only
    when those tables have actually grown.
  * `opaque_of` cloned the whole output buffer -- megabytes of translated
    program -- to save and restore it around each candidate expression. It
    truncates back instead.
  * `resolve_opaque` ran up to `pending.len()` rounds, each re-lowering every
    unresolved body. The bound is a chain depth, not a count: `a` returning
    `b`'s result returning `c`'s is three rounds. Capped at eight, with
    anything deeper reported rather than waited for.

And a correctness bug the speed work exposed. Arm bodies were being typed
without binding their patterns, so `Self::Ansi(c) => c.as_fg_buffer()` could
not resolve -- `c` was not in scope. It looked like it worked because some
other arm happened to type. Arms are now entered through `pat_bind`, the same
machinery the real lowering uses, and the receiver is bound too: `match self`
is the whole body of several of these, and `self` is not a `FnArg::Typed`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nandithebull committed 2026-09-19T12:02:52-07:00 Browse files
abf2c13 parent: e534fb9
modified src/lower.rs +104 -61
@@ -344,10 +344,12 @@ pub struct Lowerer {
344344 /// Const generic parameters, by name, with their Nim type. Nim's `static`
345345 /// parameter is the same thing: a generic parameter that is a value.
346346 const_gens: HashMap<String, String>,
347+ /// Combined table size when `CONST_NAMES` was last rebuilt.
348+ const_names_at: std::cell::Cell<Option<usize>>,
347349 /// Bodies of functions whose return type is a return-position `impl
348350 /// Trait`, keyed the same way as `fns`/`methods`, so `resolve_opaque` can
349351 /// read the concrete type back off the body.
350- opaque_fns: Vec<(OpaqueKey, syn::Signature, syn::Block)>,
352+ opaque_fns: Vec<(OpaqueKey, syn::Signature, syn::Block, Option<Nim>)>,
351353 /// Resolved opaque return types, keyed by the signature's source text.
352354 /// `signature` is called again on the emission pass and has only the
353355 /// signature to go on, so that is what the key is made of.
@@ -431,6 +433,7 @@ impl Lowerer {
431433 local_items: HashMap::new(),
432434 emitted_local: std::collections::HashSet::new(),
433435 const_gens: HashMap::new(),
436+ const_names_at: std::cell::Cell::new(None),
434437 opaque_fns: Vec::new(),
435438 opaque_ret: HashMap::new(),
436439 opaque_hint: None,
@@ -817,6 +820,7 @@ impl Lowerer {
817820 OpaqueKey::Free(self.cur_mod.clone(), name),
818821 f.sig.clone(),
819822 (*f.block).clone(),
823+ None,
820824 ));
821825 }
822826 }
@@ -1091,6 +1095,7 @@ impl Lowerer {
10911095 OpaqueKey::Method(tyname.clone(), mname),
10921096 m.sig.clone(),
10931097 m.block.clone(),
1098+ Some(self_ty.clone()),
10941099 ));
10951100 }
10961101 }
@@ -1126,6 +1131,7 @@ impl Lowerer {
11261131 OpaqueKey::Method(tyname.clone(), m.sig.ident.to_string()),
11271132 m.sig.clone(),
11281133 m.block.clone(),
1134+ Some(self_ty.clone()),
11291135 ));
11301136 }
11311137 }
@@ -1274,6 +1280,16 @@ impl Lowerer {
12741280 /// Publish the `Qualifier::NAME` constants an array length may refer to.
12751281 /// See `ty::CONST_NAMES` for why this is a side channel.
12761282 fn refill_const_names(&self) {
1283+ // `map_ty` is called constantly, and this walks every enum and every
1284+ // associated const in the program. Both tables only ever grow, so
1285+ // their combined size is enough to tell whether anything has been
1286+ // added since the last refill -- without it, a 357-crate run spends
1287+ // essentially all of its time rebuilding this map.
1288+ let size = self.enums.len() + self.assoc_consts.len();
1289+ if self.const_names_at.get() == Some(size) {
1290+ return;
1291+ }
1292+ self.const_names_at.set(Some(size));
12771293 ty::CONST_NAMES.with(|m| {
12781294 let mut m = m.borrow_mut();
12791295 m.clear();
@@ -1824,6 +1840,54 @@ impl Lowerer {
18241840 Ok(())
18251841 }
18261842
1843+ /// The type a result expression produces, looking through `match`, `if`
1844+ /// and blocks.
1845+ ///
1846+ /// Rust gives every branch of these the same type, so the first branch
1847+ /// that types answers for all of them. A `match` arm has to be entered
1848+ /// properly rather than typed on its own: `Self::Ansi(c) => c.buf()`
1849+ /// means nothing until `c` is bound, which is what `pat_bind` does for
1850+ /// the real lowering too.
1851+ fn result_ty(&mut self, e: &Expr) -> Option<Nim> {
1852+ match e {
1853+ Expr::Match(m) => {
1854+ let scrut = self.expr(&m.expr).ok()?;
1855+ let st = scrut.ty.clone()?;
1856+ for arm in &m.arms {
1857+ self.push_scope();
1858+ let bound = self.pat_bind(&arm.pat, &scrut.code, &st).is_ok();
1859+ let t = if bound { self.result_ty(&arm.body) } else { None };
1860+ self.pop_scope();
1861+ if t.is_some() {
1862+ return t;
1863+ }
1864+ }
1865+ None
1866+ }
1867+ Expr::If(i) => {
1868+ let then = i.then_branch.stmts.last().and_then(|st| match st {
1869+ Stmt::Expr(e, None) => Some(e.clone()),
1870+ _ => None,
1871+ });
1872+ if let Some(t) = then.and_then(|e| self.result_ty(&e)) {
1873+ return Some(t);
1874+ }
1875+ let (_, els) = i.else_branch.as_ref()?;
1876+ self.result_ty(els)
1877+ }
1878+ Expr::Block(b) => {
1879+ let tail = b.block.stmts.last().and_then(|st| match st {
1880+ Stmt::Expr(e, None) => Some(e.clone()),
1881+ _ => None,
1882+ })?;
1883+ self.result_ty(&tail)
1884+ }
1885+ Expr::Paren(p) => self.result_ty(&p.expr),
1886+ Expr::Group(g) => self.result_ty(&g.expr),
1887+ other => self.expr(other).ok().and_then(|v| v.ty),
1888+ }
1889+ }
1890+
18271891 /// Give every return-position `impl Trait` its concrete type, taken from
18281892 /// the body that produces it.
18291893 ///
@@ -1835,13 +1899,21 @@ impl Lowerer {
18351899 let opaque = Nim::Named(OPAQUE.into(), Vec::new());
18361900 let pending = std::mem::take(&mut self.opaque_fns);
18371901 let mut left = pending.clone();
1838- // One function's opaque type may be another's; each round resolves at
1839- // least one or nothing more can be learned.
1840- for _ in 0..pending.len().max(1) {
1902+ // One function's opaque type may be another's, so this iterates -- but
1903+ // it is bounded rather than run to exhaustion. Each round lowers every
1904+ // still-unresolved body, so a bound of `pending.len()` is quadratic,
1905+ // and with several hundred opaque functions across a dependency graph
1906+ // that is the difference between seconds and never finishing.
1907+ //
1908+ // The bound is a chain depth, not a count: `a` returning `b`'s result
1909+ // returning `c`'s is three rounds. Chains that long are already
1910+ // unusual, and anything deeper is reported rather than waited for.
1911+ const MAX_ROUNDS: usize = 8;
1912+ for _ in 0..MAX_ROUNDS.min(pending.len().max(1)) {
18411913 let mut progressed = false;
18421914 let mut still = Vec::new();
1843- for (key, sig, block) in left {
1844- match self.opaque_of(&sig, &block) {
1915+ for (key, sig, block, self_ty) in left {
1916+ match self.opaque_of(&sig, &block, self_ty.as_ref()) {
18451917 Some(t) if t != opaque => {
18461918 self.opaque_ret.insert(opaque_key(&sig, &block), t.clone());
18471919 match &key {
@@ -1858,7 +1930,7 @@ impl Lowerer {
18581930 }
18591931 progressed = true;
18601932 }
1861- _ => still.push((key, sig, block)),
1933+ _ => still.push((key, sig, block, self_ty)),
18621934 }
18631935 }
18641936 left = still;
@@ -1866,7 +1938,7 @@ impl Lowerer {
18661938 break;
18671939 }
18681940 }
1869- if let Some((key, sig, _)) = left.first() {
1941+ if let Some((key, sig, _, _)) = left.first() {
18701942 let what = match key {
18711943 OpaqueKey::Free(_, n) => n.clone(),
18721944 OpaqueKey::Method(t, n) => format!("{t}::{n}"),
@@ -1886,22 +1958,30 @@ impl Lowerer {
18861958 }
18871959
18881960 /// The type of what a body returns, lowered for real and then discarded.
1889- fn opaque_of(&mut self, sig: &syn::Signature, block: &syn::Block) -> Option<Nim> {
1961+ fn opaque_of(
1962+ &mut self,
1963+ sig: &syn::Signature,
1964+ block: &syn::Block,
1965+ self_ty: Option<&Nim>,
1966+ ) -> Option<Nim> {
18901967 let tail = block.stmts.iter().rev().find_map(|st| match st {
18911968 Stmt::Expr(e, None) => Some(e.clone()),
18921969 // `return e;` answers the same question.
18931970 Stmt::Expr(Expr::Return(r), _) => r.expr.as_deref().cloned(),
18941971 _ => None,
18951972 })?;
1896- // A tail `match` or `if` produces one type across all its arms -- Rust
1897- // requires that -- so any arm that types is the answer. This is what
1898- // makes a body like `match self { A(c) => c.buf(), .. }` usable here
1899- // even though `match` in value position is not lowered.
1900- let cands = result_exprs(&tail);
1973+
19011974 // Parameters have to be in scope, or every mention of one is an
19021975 // unknown name and the result types as nothing.
19031976 let (ptys, _) = self.signature(sig).ok()?;
19041977 self.push_scope();
1978+ // The receiver too: `match self { .. }` is the whole body of several
1979+ // of these, and `self` is not a `FnArg::Typed`.
1980+ if let Some(t) = self_ty {
1981+ self.bind("self", t.clone());
1982+ }
1983+ let outer_self = self.self_ty.clone();
1984+ self.self_ty = self_ty.cloned().or(outer_self.clone());
19051985 let mut i = 0usize;
19061986 for a in &sig.inputs {
19071987 if let FnArg::Typed(t) = a {
@@ -1913,20 +1993,19 @@ impl Lowerer {
19131993 i += 1;
19141994 }
19151995 }
1916- let saved_out = self.out.clone();
1996+ // Truncated back rather than cloned and restored: `out` is the whole
1997+ // translated program, megabytes of it by the time this runs, and
1998+ // copying it once per candidate expression per round was the cost
1999+ // that made a 357-crate run never finish.
2000+ let saved_out = self.out.len();
19172001 let saved_indent = self.indent;
1918- let saved_hoisted = self.hoisted.clone();
1919- let mut found = None;
1920- for c in &cands {
1921- if let Some(t) = self.expr(c).ok().and_then(|v| v.ty) {
1922- found = Some(t);
1923- break;
1924- }
1925- }
1926- self.out = saved_out;
2002+ let saved_hoisted = self.hoisted.len();
2003+ let found = self.result_ty(&tail);
2004+ self.out.truncate(saved_out);
19272005 self.indent = saved_indent;
1928- self.hoisted = saved_hoisted;
2006+ self.hoisted.truncate(saved_hoisted);
19292007 self.pop_scope();
2008+ self.self_ty = outer_self;
19302009 found
19312010 }
19322011
@@ -7728,42 +7807,6 @@ fn opaque_key(sig: &syn::Signature, block: &syn::Block) -> String {
77287807 )
77297808 }
77307809
7731-/// The expressions a body can produce its result from, flattened through
7732-/// `match`, `if` and blocks. Rust gives every branch the same type, so any one
7733-/// of them that rustnim can type answers for all of them.
7734-fn result_exprs(e: &Expr) -> Vec<Expr> {
7735- match e {
7736- Expr::Match(m) => m.arms.iter().flat_map(|a| result_exprs(&a.body)).collect(),
7737- Expr::If(i) => {
7738- let mut v: Vec<Expr> = i
7739- .then_branch
7740- .stmts
7741- .last()
7742- .and_then(|s| match s {
7743- Stmt::Expr(e, None) => Some(result_exprs(e)),
7744- _ => None,
7745- })
7746- .unwrap_or_default();
7747- if let Some((_, els)) = &i.else_branch {
7748- v.extend(result_exprs(els));
7749- }
7750- v
7751- }
7752- Expr::Block(b) => b
7753- .block
7754- .stmts
7755- .last()
7756- .and_then(|s| match s {
7757- Stmt::Expr(e, None) => Some(result_exprs(e)),
7758- _ => None,
7759- })
7760- .unwrap_or_default(),
7761- Expr::Paren(p) => result_exprs(&p.expr),
7762- Expr::Group(g) => result_exprs(&g.expr),
7763- other => vec![other.clone()],
7764- }
7765-}
7766-
77677810 /// Whether a type contains an `impl Trait` anywhere inside it.
77687811 fn contains_impl_trait(t: &syn::Type) -> bool {
77697812 use syn::Type;
@@ -344,10 +344,12 @@ pub struct Lowerer {
344 /// Const generic parameters, by name, with their Nim type. Nim's `static`344 /// Const generic parameters, by name, with their Nim type. Nim's `static`
345 /// parameter is the same thing: a generic parameter that is a value.345 /// parameter is the same thing: a generic parameter that is a value.
346 const_gens: HashMap<String, String>,346 const_gens: HashMap<String, String>,
347+ /// Combined table size when `CONST_NAMES` was last rebuilt.
348+ const_names_at: std::cell::Cell<Option<usize>>,
347 /// Bodies of functions whose return type is a return-position `impl349 /// Bodies of functions whose return type is a return-position `impl
348 /// Trait`, keyed the same way as `fns`/`methods`, so `resolve_opaque` can350 /// Trait`, keyed the same way as `fns`/`methods`, so `resolve_opaque` can
349 /// read the concrete type back off the body.351 /// read the concrete type back off the body.
350- opaque_fns: Vec<(OpaqueKey, syn::Signature, syn::Block)>,352+ opaque_fns: Vec<(OpaqueKey, syn::Signature, syn::Block, Option<Nim>)>,
351 /// Resolved opaque return types, keyed by the signature's source text.353 /// Resolved opaque return types, keyed by the signature's source text.
352 /// `signature` is called again on the emission pass and has only the354 /// `signature` is called again on the emission pass and has only the
353 /// signature to go on, so that is what the key is made of.355 /// signature to go on, so that is what the key is made of.
@@ -431,6 +433,7 @@ impl Lowerer {
431 local_items: HashMap::new(),433 local_items: HashMap::new(),
432 emitted_local: std::collections::HashSet::new(),434 emitted_local: std::collections::HashSet::new(),
433 const_gens: HashMap::new(),435 const_gens: HashMap::new(),
436+ const_names_at: std::cell::Cell::new(None),
434 opaque_fns: Vec::new(),437 opaque_fns: Vec::new(),
435 opaque_ret: HashMap::new(),438 opaque_ret: HashMap::new(),
436 opaque_hint: None,439 opaque_hint: None,
@@ -817,6 +820,7 @@ impl Lowerer {
817 OpaqueKey::Free(self.cur_mod.clone(), name),820 OpaqueKey::Free(self.cur_mod.clone(), name),
818 f.sig.clone(),821 f.sig.clone(),
819 (*f.block).clone(),822 (*f.block).clone(),
823+ None,
820 ));824 ));
821 }825 }
822 }826 }
@@ -1091,6 +1095,7 @@ impl Lowerer {
1091 OpaqueKey::Method(tyname.clone(), mname),1095 OpaqueKey::Method(tyname.clone(), mname),
1092 m.sig.clone(),1096 m.sig.clone(),
1093 m.block.clone(),1097 m.block.clone(),
1098+ Some(self_ty.clone()),
1094 ));1099 ));
1095 }1100 }
1096 }1101 }
@@ -1126,6 +1131,7 @@ impl Lowerer {
1126 OpaqueKey::Method(tyname.clone(), m.sig.ident.to_string()),1131 OpaqueKey::Method(tyname.clone(), m.sig.ident.to_string()),
1127 m.sig.clone(),1132 m.sig.clone(),
1128 m.block.clone(),1133 m.block.clone(),
1134+ Some(self_ty.clone()),
1129 ));1135 ));
1130 }1136 }
1131 }1137 }
@@ -1274,6 +1280,16 @@ impl Lowerer {
1274 /// Publish the `Qualifier::NAME` constants an array length may refer to.1280 /// Publish the `Qualifier::NAME` constants an array length may refer to.
1275 /// See `ty::CONST_NAMES` for why this is a side channel.1281 /// See `ty::CONST_NAMES` for why this is a side channel.
1276 fn refill_const_names(&self) {1282 fn refill_const_names(&self) {
1283+ // `map_ty` is called constantly, and this walks every enum and every
1284+ // associated const in the program. Both tables only ever grow, so
1285+ // their combined size is enough to tell whether anything has been
1286+ // added since the last refill -- without it, a 357-crate run spends
1287+ // essentially all of its time rebuilding this map.
1288+ let size = self.enums.len() + self.assoc_consts.len();
1289+ if self.const_names_at.get() == Some(size) {
1290+ return;
1291+ }
1292+ self.const_names_at.set(Some(size));
1277 ty::CONST_NAMES.with(|m| {1293 ty::CONST_NAMES.with(|m| {
1278 let mut m = m.borrow_mut();1294 let mut m = m.borrow_mut();
1279 m.clear();1295 m.clear();
@@ -1824,6 +1840,54 @@ impl Lowerer {
1824 Ok(())1840 Ok(())
1825 }1841 }
1826 1842
1843+ /// The type a result expression produces, looking through `match`, `if`
1844+ /// and blocks.
1845+ ///
1846+ /// Rust gives every branch of these the same type, so the first branch
1847+ /// that types answers for all of them. A `match` arm has to be entered
1848+ /// properly rather than typed on its own: `Self::Ansi(c) => c.buf()`
1849+ /// means nothing until `c` is bound, which is what `pat_bind` does for
1850+ /// the real lowering too.
1851+ fn result_ty(&mut self, e: &Expr) -> Option<Nim> {
1852+ match e {
1853+ Expr::Match(m) => {
1854+ let scrut = self.expr(&m.expr).ok()?;
1855+ let st = scrut.ty.clone()?;
1856+ for arm in &m.arms {
1857+ self.push_scope();
1858+ let bound = self.pat_bind(&arm.pat, &scrut.code, &st).is_ok();
1859+ let t = if bound { self.result_ty(&arm.body) } else { None };
1860+ self.pop_scope();
1861+ if t.is_some() {
1862+ return t;
1863+ }
1864+ }
1865+ None
1866+ }
1867+ Expr::If(i) => {
1868+ let then = i.then_branch.stmts.last().and_then(|st| match st {
1869+ Stmt::Expr(e, None) => Some(e.clone()),
1870+ _ => None,
1871+ });
1872+ if let Some(t) = then.and_then(|e| self.result_ty(&e)) {
1873+ return Some(t);
1874+ }
1875+ let (_, els) = i.else_branch.as_ref()?;
1876+ self.result_ty(els)
1877+ }
1878+ Expr::Block(b) => {
1879+ let tail = b.block.stmts.last().and_then(|st| match st {
1880+ Stmt::Expr(e, None) => Some(e.clone()),
1881+ _ => None,
1882+ })?;
1883+ self.result_ty(&tail)
1884+ }
1885+ Expr::Paren(p) => self.result_ty(&p.expr),
1886+ Expr::Group(g) => self.result_ty(&g.expr),
1887+ other => self.expr(other).ok().and_then(|v| v.ty),
1888+ }
1889+ }
1890+
1827 /// Give every return-position `impl Trait` its concrete type, taken from1891 /// Give every return-position `impl Trait` its concrete type, taken from
1828 /// the body that produces it.1892 /// the body that produces it.
1829 ///1893 ///
@@ -1835,13 +1899,21 @@ impl Lowerer {
1835 let opaque = Nim::Named(OPAQUE.into(), Vec::new());1899 let opaque = Nim::Named(OPAQUE.into(), Vec::new());
1836 let pending = std::mem::take(&mut self.opaque_fns);1900 let pending = std::mem::take(&mut self.opaque_fns);
1837 let mut left = pending.clone();1901 let mut left = pending.clone();
1838- // One function's opaque type may be another's; each round resolves at1902+ // One function's opaque type may be another's, so this iterates -- but
1839- // least one or nothing more can be learned.1903+ // it is bounded rather than run to exhaustion. Each round lowers every
1840- for _ in 0..pending.len().max(1) {1904+ // still-unresolved body, so a bound of `pending.len()` is quadratic,
1905+ // and with several hundred opaque functions across a dependency graph
1906+ // that is the difference between seconds and never finishing.
1907+ //
1908+ // The bound is a chain depth, not a count: `a` returning `b`'s result
1909+ // returning `c`'s is three rounds. Chains that long are already
1910+ // unusual, and anything deeper is reported rather than waited for.
1911+ const MAX_ROUNDS: usize = 8;
1912+ for _ in 0..MAX_ROUNDS.min(pending.len().max(1)) {
1841 let mut progressed = false;1913 let mut progressed = false;
1842 let mut still = Vec::new();1914 let mut still = Vec::new();
1843- for (key, sig, block) in left {1915+ for (key, sig, block, self_ty) in left {
1844- match self.opaque_of(&sig, &block) {1916+ match self.opaque_of(&sig, &block, self_ty.as_ref()) {
1845 Some(t) if t != opaque => {1917 Some(t) if t != opaque => {
1846 self.opaque_ret.insert(opaque_key(&sig, &block), t.clone());1918 self.opaque_ret.insert(opaque_key(&sig, &block), t.clone());
1847 match &key {1919 match &key {
@@ -1858,7 +1930,7 @@ impl Lowerer {
1858 }1930 }
1859 progressed = true;1931 progressed = true;
1860 }1932 }
1861- _ => still.push((key, sig, block)),1933+ _ => still.push((key, sig, block, self_ty)),
1862 }1934 }
1863 }1935 }
1864 left = still;1936 left = still;
@@ -1866,7 +1938,7 @@ impl Lowerer {
1866 break;1938 break;
1867 }1939 }
1868 }1940 }
1869- if let Some((key, sig, _)) = left.first() {1941+ if let Some((key, sig, _, _)) = left.first() {
1870 let what = match key {1942 let what = match key {
1871 OpaqueKey::Free(_, n) => n.clone(),1943 OpaqueKey::Free(_, n) => n.clone(),
1872 OpaqueKey::Method(t, n) => format!("{t}::{n}"),1944 OpaqueKey::Method(t, n) => format!("{t}::{n}"),
@@ -1886,22 +1958,30 @@ impl Lowerer {
1886 }1958 }
1887 1959
1888 /// The type of what a body returns, lowered for real and then discarded.1960 /// The type of what a body returns, lowered for real and then discarded.
1889- fn opaque_of(&mut self, sig: &syn::Signature, block: &syn::Block) -> Option<Nim> {1961+ fn opaque_of(
1962+ &mut self,
1963+ sig: &syn::Signature,
1964+ block: &syn::Block,
1965+ self_ty: Option<&Nim>,
1966+ ) -> Option<Nim> {
1890 let tail = block.stmts.iter().rev().find_map(|st| match st {1967 let tail = block.stmts.iter().rev().find_map(|st| match st {
1891 Stmt::Expr(e, None) => Some(e.clone()),1968 Stmt::Expr(e, None) => Some(e.clone()),
1892 // `return e;` answers the same question.1969 // `return e;` answers the same question.
1893 Stmt::Expr(Expr::Return(r), _) => r.expr.as_deref().cloned(),1970 Stmt::Expr(Expr::Return(r), _) => r.expr.as_deref().cloned(),
1894 _ => None,1971 _ => None,
1895 })?;1972 })?;
1896- // A tail `match` or `if` produces one type across all its arms -- Rust1973+
1897- // requires that -- so any arm that types is the answer. This is what
1898- // makes a body like `match self { A(c) => c.buf(), .. }` usable here
1899- // even though `match` in value position is not lowered.
1900- let cands = result_exprs(&tail);
1901 // Parameters have to be in scope, or every mention of one is an1974 // Parameters have to be in scope, or every mention of one is an
1902 // unknown name and the result types as nothing.1975 // unknown name and the result types as nothing.
1903 let (ptys, _) = self.signature(sig).ok()?;1976 let (ptys, _) = self.signature(sig).ok()?;
1904 self.push_scope();1977 self.push_scope();
1978+ // The receiver too: `match self { .. }` is the whole body of several
1979+ // of these, and `self` is not a `FnArg::Typed`.
1980+ if let Some(t) = self_ty {
1981+ self.bind("self", t.clone());
1982+ }
1983+ let outer_self = self.self_ty.clone();
1984+ self.self_ty = self_ty.cloned().or(outer_self.clone());
1905 let mut i = 0usize;1985 let mut i = 0usize;
1906 for a in &sig.inputs {1986 for a in &sig.inputs {
1907 if let FnArg::Typed(t) = a {1987 if let FnArg::Typed(t) = a {
@@ -1913,20 +1993,19 @@ impl Lowerer {
1913 i += 1;1993 i += 1;
1914 }1994 }
1915 }1995 }
1916- let saved_out = self.out.clone();1996+ // Truncated back rather than cloned and restored: `out` is the whole
1997+ // translated program, megabytes of it by the time this runs, and
1998+ // copying it once per candidate expression per round was the cost
1999+ // that made a 357-crate run never finish.
2000+ let saved_out = self.out.len();
1917 let saved_indent = self.indent;2001 let saved_indent = self.indent;
1918- let saved_hoisted = self.hoisted.clone();2002+ let saved_hoisted = self.hoisted.len();
1919- let mut found = None;2003+ let found = self.result_ty(&tail);
1920- for c in &cands {2004+ self.out.truncate(saved_out);
1921- if let Some(t) = self.expr(c).ok().and_then(|v| v.ty) {
1922- found = Some(t);
1923- break;
1924- }
1925- }
1926- self.out = saved_out;
1927 self.indent = saved_indent;2005 self.indent = saved_indent;
1928- self.hoisted = saved_hoisted;2006+ self.hoisted.truncate(saved_hoisted);
1929 self.pop_scope();2007 self.pop_scope();
2008+ self.self_ty = outer_self;
1930 found2009 found
1931 }2010 }
1932 2011
@@ -7728,42 +7807,6 @@ fn opaque_key(sig: &syn::Signature, block: &syn::Block) -> String {
7728 )7807 )
7729 }7808 }
7730 7809
7731-/// The expressions a body can produce its result from, flattened through
7732-/// `match`, `if` and blocks. Rust gives every branch the same type, so any one
7733-/// of them that rustnim can type answers for all of them.
7734-fn result_exprs(e: &Expr) -> Vec<Expr> {
7735- match e {
7736- Expr::Match(m) => m.arms.iter().flat_map(|a| result_exprs(&a.body)).collect(),
7737- Expr::If(i) => {
7738- let mut v: Vec<Expr> = i
7739- .then_branch
7740- .stmts
7741- .last()
7742- .and_then(|s| match s {
7743- Stmt::Expr(e, None) => Some(result_exprs(e)),
7744- _ => None,
7745- })
7746- .unwrap_or_default();
7747- if let Some((_, els)) = &i.else_branch {
7748- v.extend(result_exprs(els));
7749- }
7750- v
7751- }
7752- Expr::Block(b) => b
7753- .block
7754- .stmts
7755- .last()
7756- .and_then(|s| match s {
7757- Stmt::Expr(e, None) => Some(result_exprs(e)),
7758- _ => None,
7759- })
7760- .unwrap_or_default(),
7761- Expr::Paren(p) => result_exprs(&p.expr),
7762- Expr::Group(g) => result_exprs(&g.expr),
7763- other => vec![other.clone()],
7764- }
7765-}
7766-
7767 /// Whether a type contains an `impl Trait` anywhere inside it.7810 /// Whether a type contains an `impl Trait` anywhere inside it.
7768 fn contains_impl_trait(t: &syn::Type) -> bool {7811 fn contains_impl_trait(t: &syn::Type) -> bool {
7769 use syn::Type;7812 use syn::Type;