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

Read an opaque return type through `match`, `if` and `return`

`anstyle`'s `fn render_fg(self) -> impl Display + Copy` is a tail `match`, and
`match` in value position is not lowered, so the concrete type could not be
read off the body.

It does not need to be lowered to be read. Rust gives every arm of a `match`
the same type, so any single arm that rustnim can type answers for all of
them -- that is a consequence of Rust's own rule, not a sampling heuristic.
Same for `if`/`else`, blocks, and an explicit `return`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nandithebull committed 2026-09-19T10:18:17-07:00 Browse files
6c57571 parent: 8594af4
modified src/lower.rs +51 -2
@@ -1782,8 +1782,15 @@ impl Lowerer {
17821782 fn opaque_of(&mut self, sig: &syn::Signature, block: &syn::Block) -> Option<Nim> {
17831783 let tail = block.stmts.iter().rev().find_map(|st| match st {
17841784 Stmt::Expr(e, None) => Some(e.clone()),
1785+ // `return e;` answers the same question.
1786+ Stmt::Expr(Expr::Return(r), _) => r.expr.as_deref().cloned(),
17851787 _ => None,
17861788 })?;
1789+ // A tail `match` or `if` produces one type across all its arms -- Rust
1790+ // requires that -- so any arm that types is the answer. This is what
1791+ // makes a body like `match self { A(c) => c.buf(), .. }` usable here
1792+ // even though `match` in value position is not lowered.
1793+ let cands = result_exprs(&tail);
17871794 // Parameters have to be in scope, or every mention of one is an
17881795 // unknown name and the result types as nothing.
17891796 let (ptys, _) = self.signature(sig).ok()?;
@@ -1802,12 +1809,18 @@ impl Lowerer {
18021809 let saved_out = self.out.clone();
18031810 let saved_indent = self.indent;
18041811 let saved_hoisted = self.hoisted.clone();
1805- let r = self.expr(&tail);
1812+ let mut found = None;
1813+ for c in &cands {
1814+ if let Some(t) = self.expr(c).ok().and_then(|v| v.ty) {
1815+ found = Some(t);
1816+ break;
1817+ }
1818+ }
18061819 self.out = saved_out;
18071820 self.indent = saved_indent;
18081821 self.hoisted = saved_hoisted;
18091822 self.pop_scope();
1810- r.ok().and_then(|v| v.ty)
1823+ found
18111824 }
18121825
18131826 fn signature(&self, sig: &syn::Signature) -> Result<(Vec<Nim>, Nim), String> {
@@ -7606,3 +7619,39 @@ fn opaque_key(sig: &syn::Signature, block: &syn::Block) -> String {
76067619 quote::quote!(#block)
76077620 )
76087621 }
7622+
7623+/// The expressions a body can produce its result from, flattened through
7624+/// `match`, `if` and blocks. Rust gives every branch the same type, so any one
7625+/// of them that rustnim can type answers for all of them.
7626+fn result_exprs(e: &Expr) -> Vec<Expr> {
7627+ match e {
7628+ Expr::Match(m) => m.arms.iter().flat_map(|a| result_exprs(&a.body)).collect(),
7629+ Expr::If(i) => {
7630+ let mut v: Vec<Expr> = i
7631+ .then_branch
7632+ .stmts
7633+ .last()
7634+ .and_then(|s| match s {
7635+ Stmt::Expr(e, None) => Some(result_exprs(e)),
7636+ _ => None,
7637+ })
7638+ .unwrap_or_default();
7639+ if let Some((_, els)) = &i.else_branch {
7640+ v.extend(result_exprs(els));
7641+ }
7642+ v
7643+ }
7644+ Expr::Block(b) => b
7645+ .block
7646+ .stmts
7647+ .last()
7648+ .and_then(|s| match s {
7649+ Stmt::Expr(e, None) => Some(result_exprs(e)),
7650+ _ => None,
7651+ })
7652+ .unwrap_or_default(),
7653+ Expr::Paren(p) => result_exprs(&p.expr),
7654+ Expr::Group(g) => result_exprs(&g.expr),
7655+ other => vec![other.clone()],
7656+ }
7657+}
@@ -1782,8 +1782,15 @@ impl Lowerer {
1782 fn opaque_of(&mut self, sig: &syn::Signature, block: &syn::Block) -> Option<Nim> {1782 fn opaque_of(&mut self, sig: &syn::Signature, block: &syn::Block) -> Option<Nim> {
1783 let tail = block.stmts.iter().rev().find_map(|st| match st {1783 let tail = block.stmts.iter().rev().find_map(|st| match st {
1784 Stmt::Expr(e, None) => Some(e.clone()),1784 Stmt::Expr(e, None) => Some(e.clone()),
1785+ // `return e;` answers the same question.
1786+ Stmt::Expr(Expr::Return(r), _) => r.expr.as_deref().cloned(),
1785 _ => None,1787 _ => None,
1786 })?;1788 })?;
1789+ // A tail `match` or `if` produces one type across all its arms -- Rust
1790+ // requires that -- so any arm that types is the answer. This is what
1791+ // makes a body like `match self { A(c) => c.buf(), .. }` usable here
1792+ // even though `match` in value position is not lowered.
1793+ let cands = result_exprs(&tail);
1787 // Parameters have to be in scope, or every mention of one is an1794 // Parameters have to be in scope, or every mention of one is an
1788 // unknown name and the result types as nothing.1795 // unknown name and the result types as nothing.
1789 let (ptys, _) = self.signature(sig).ok()?;1796 let (ptys, _) = self.signature(sig).ok()?;
@@ -1802,12 +1809,18 @@ impl Lowerer {
1802 let saved_out = self.out.clone();1809 let saved_out = self.out.clone();
1803 let saved_indent = self.indent;1810 let saved_indent = self.indent;
1804 let saved_hoisted = self.hoisted.clone();1811 let saved_hoisted = self.hoisted.clone();
1805- let r = self.expr(&tail);1812+ let mut found = None;
1813+ for c in &cands {
1814+ if let Some(t) = self.expr(c).ok().and_then(|v| v.ty) {
1815+ found = Some(t);
1816+ break;
1817+ }
1818+ }
1806 self.out = saved_out;1819 self.out = saved_out;
1807 self.indent = saved_indent;1820 self.indent = saved_indent;
1808 self.hoisted = saved_hoisted;1821 self.hoisted = saved_hoisted;
1809 self.pop_scope();1822 self.pop_scope();
1810- r.ok().and_then(|v| v.ty)1823+ found
1811 }1824 }
1812 1825
1813 fn signature(&self, sig: &syn::Signature) -> Result<(Vec<Nim>, Nim), String> {1826 fn signature(&self, sig: &syn::Signature) -> Result<(Vec<Nim>, Nim), String> {
@@ -7606,3 +7619,39 @@ fn opaque_key(sig: &syn::Signature, block: &syn::Block) -> String {
7606 quote::quote!(#block)7619 quote::quote!(#block)
7607 )7620 )
7608 }7621 }
7622+
7623+/// The expressions a body can produce its result from, flattened through
7624+/// `match`, `if` and blocks. Rust gives every branch the same type, so any one
7625+/// of them that rustnim can type answers for all of them.
7626+fn result_exprs(e: &Expr) -> Vec<Expr> {
7627+ match e {
7628+ Expr::Match(m) => m.arms.iter().flat_map(|a| result_exprs(&a.body)).collect(),
7629+ Expr::If(i) => {
7630+ let mut v: Vec<Expr> = i
7631+ .then_branch
7632+ .stmts
7633+ .last()
7634+ .and_then(|s| match s {
7635+ Stmt::Expr(e, None) => Some(result_exprs(e)),
7636+ _ => None,
7637+ })
7638+ .unwrap_or_default();
7639+ if let Some((_, els)) = &i.else_branch {
7640+ v.extend(result_exprs(els));
7641+ }
7642+ v
7643+ }
7644+ Expr::Block(b) => b
7645+ .block
7646+ .stmts
7647+ .last()
7648+ .and_then(|s| match s {
7649+ Stmt::Expr(e, None) => Some(result_exprs(e)),
7650+ _ => None,
7651+ })
7652+ .unwrap_or_default(),
7653+ Expr::Paren(p) => result_exprs(&p.expr),
7654+ Expr::Group(g) => result_exprs(&g.expr),
7655+ other => vec![other.clone()],
7656+ }
7657+}
modified tests/cases/056-return-impl-trait.rs +11 -0
@@ -28,8 +28,19 @@ impl Wrap {
2828 }
2929 }
3030
31+// The body's result reached through a `match`: Rust gives every arm the same
32+// type, so any arm answers for all of them.
33+fn pick(n: u32) -> impl Display {
34+ match n {
35+ 0 => Tag { n: 100 },
36+ 1 => Tag { n: 200 },
37+ _ => Tag { n: 300 },
38+ }
39+}
40+
3141 fn main() {
3242 println!("{}", make(3));
3343 println!("{}", relay(3));
3444 println!("{}", Wrap.build(9));
45+ println!("{} {}", pick(0), pick(7));
3546 }
@@ -28,8 +28,19 @@ impl Wrap {
28 }28 }
29 }29 }
30 30
31+// The body's result reached through a `match`: Rust gives every arm the same
32+// type, so any arm answers for all of them.
33+fn pick(n: u32) -> impl Display {
34+ match n {
35+ 0 => Tag { n: 100 },
36+ 1 => Tag { n: 200 },
37+ _ => Tag { n: 300 },
38+ }
39+}
40+
31 fn main() {41 fn main() {
32 println!("{}", make(3));42 println!("{}", make(3));
33 println!("{}", relay(3));43 println!("{}", relay(3));
34 println!("{}", Wrap.build(9));44 println!("{}", Wrap.build(9));
45+ println!("{} {}", pick(0), pick(7));
35 }46 }