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

Resolve return-position `impl Trait` from the body that produces it

Rust's opaque return type is a *single* concrete type -- the one the body
produces. The caller is not allowed to name it, but it is not ambiguous, so
reading it back off the body is exact rather than a guess. That is what this
does, and it is why `-> impl Display` no longer has to be refused.

The signature pass marks such a return type; a new pass between signatures and
bodies lowers each body's result expression into a scratch buffer, keeps the
*type*, and throws the text away. Using the real lowering rather than a
separate syntactic guess is the point: the answer is then the same one the
body will actually produce. Repeated to a fixpoint, since one such function
may return the result of another.

Forward declarations had to become deferred. They were rendered during
collection, which is before any of this is known, so they are now held as the
signature they came from -- along with the impl's generic parameters and
`Self`, which are no longer current at render time -- and rendered afterwards.

An unresolved one is an error, and so is two functions with identical
signature text whose opaque types differ, since the resolution is keyed on
that text.

Found by the dependency run. Return-position `impl Trait` is not rare: 60
`-> impl Parser` in the graph, plus `Display`, `Stream`, `PathData` and
`RangeBounds`.

tests/cases/056.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nandithebull committed 2026-09-19T10:02:02-07:00 Browse files
47de6c6 parent: ae1d333
modified src/lower.rs +216 -12
@@ -33,6 +33,26 @@ const NIM_KEYWORDS: &[&str] = &[
3333 /// inside `system`, which is a confusing place for it to surface.
3434 const NIM_SHADOWED: &[&str] = &["new"];
3535
36+/// Placeholder for a return-position `impl Trait` between the signature pass
37+/// and `resolve_opaque`. It never reaches the output: an unresolved one is an
38+/// error.
39+const OPAQUE: &str = "RsOpaque";
40+
41+/// Which table an opaque return type has to be written back into.
42+#[derive(Clone, Debug)]
43+enum Fwd {
44+ Text(String),
45+ /// Also carries the impl's generic parameters and `Self` type: rendering
46+ /// happens after collection, when that context is no longer current.
47+ Head(String, Box<syn::Signature>, Option<Nim>, Vec<String>, Option<Nim>),
48+}
49+
50+#[derive(Clone, Debug)]
51+enum OpaqueKey {
52+ Free(String, String),
53+ Method(String, String),
54+}
55+
3656 fn ident(name: &str) -> String {
3757 if NIM_KEYWORDS.contains(&name) || NIM_SHADOWED.contains(&name) {
3858 return format!("{name}_r");
@@ -277,7 +297,10 @@ pub struct Lowerer {
277297 /// bodies. Rust has no declaration-before-use rule and Nim does, so every
278298 /// proc is declared up front rather than the input being reordered --
279299 /// which would not work for mutual recursion anyway.
280- forwards: Vec<String>,
300+ /// Forward declarations. Held unrendered where they come from a
301+ /// signature, because a return-position `impl Trait` in one is not
302+ /// resolved until after every signature has been collected.
303+ forwards: Vec<Fwd>,
281304 /// Element type a `vec![..]` should build, from the binding's annotation.
282305 vec_expect: Option<Nim>,
283306 /// While lowering a formatting impl: the `Formatter` parameter's name.
@@ -317,6 +340,14 @@ pub struct Lowerer {
317340 /// Const generic parameters, by name, with their Nim type. Nim's `static`
318341 /// parameter is the same thing: a generic parameter that is a value.
319342 const_gens: HashMap<String, String>,
343+ /// Bodies of functions whose return type is a return-position `impl
344+ /// Trait`, keyed the same way as `fns`/`methods`, so `resolve_opaque` can
345+ /// read the concrete type back off the body.
346+ opaque_fns: Vec<(OpaqueKey, syn::Signature, syn::Block)>,
347+ /// Resolved opaque return types, keyed by the signature's source text.
348+ /// `signature` is called again on the emission pass and has only the
349+ /// signature to go on, so that is what the key is made of.
350+ opaque_ret: HashMap<String, Nim>,
320351 /// Module names supplied as separate input files. A `mod x;` naming one
321352 /// of these is satisfied by that file having been passed in.
322353 pub modules: Vec<String>,
@@ -391,6 +422,8 @@ impl Lowerer {
391422 local_items: HashMap::new(),
392423 emitted_local: std::collections::HashSet::new(),
393424 const_gens: HashMap::new(),
425+ opaque_fns: Vec::new(),
426+ opaque_ret: HashMap::new(),
394427 modules: Vec::new(),
395428 emitted: 0,
396429 features: Vec::new(),
@@ -489,6 +522,16 @@ impl Lowerer {
489522 self.collect(item)?;
490523 }
491524 }
525+
526+ // Pass 1.5: resolve return-position `impl Trait`.
527+ //
528+ // Rust's opaque return type is a single concrete type -- the one the
529+ // body produces. The caller is not allowed to name it, but it is not
530+ // ambiguous, so reading it off the body is exact rather than a guess.
531+ // Repeated to a fixpoint because one such function may return the
532+ // result of another.
533+ self.resolve_opaque()?;
534+
492535 // Pass 2: type definitions, which every signature may mention.
493536 for (m, f) in files {
494537 self.cur_mod = m.clone();
@@ -502,6 +545,17 @@ impl Lowerer {
502545 // reordering the input would not handle mutual recursion anyway.
503546 if !self.forwards.is_empty() {
504547 for f in self.forwards.clone() {
548+ let f = match f {
549+ Fwd::Text(t) => t,
550+ Fwd::Head(nim, sig, recv, gens, self_ty) => {
551+ let og = std::mem::replace(&mut self.impl_generics, gens);
552+ let os = std::mem::replace(&mut self.self_ty, self_ty);
553+ let r = self.head_of(&nim, &sig, recv.as_ref());
554+ self.impl_generics = og;
555+ self.self_ty = os;
556+ r?
557+ }
558+ };
505559 self.line(&f);
506560 }
507561 self.blank();
@@ -663,9 +717,19 @@ impl Lowerer {
663717 gen_names.extend(self.impl_trait_params(&f.sig));
664718 let name = f.sig.ident.to_string();
665719 let nim = self.fn_name(&self.cur_mod, &name);
666- self.forwards.push(self.head_of(&nim, &f.sig, None)?);
720+ self.forwards.push(Fwd::Head(nim.clone(), Box::new(f.sig.clone()), None, self.impl_generics.clone(), self.self_ty.clone()));
667721 self.fns
668- .insert((self.cur_mod.clone(), name), Sig { params, ret, generics: gen_names.clone() });
722+ .insert(
723+ (self.cur_mod.clone(), name.clone()),
724+ Sig { params, ret: ret.clone(), generics: gen_names.clone() },
725+ );
726+ if ret == Nim::Named(OPAQUE.into(), Vec::new()) {
727+ self.opaque_fns.push((
728+ OpaqueKey::Free(self.cur_mod.clone(), name),
729+ f.sig.clone(),
730+ (*f.block).clone(),
731+ ));
732+ }
669733 }
670734 Item::Struct(s) => {
671735 self.note_const_generics(&s.generics)?;
@@ -856,11 +920,11 @@ impl Lowerer {
856920 return Ok(());
857921 }
858922 if is_fmt_trait(&tr) {
859- self.forwards.push(format!(
923+ self.forwards.push(Fwd::Text(format!(
860924 "proc {}*(self: {}): string",
861925 fmt_proc(&tr),
862926 self_ty.render()
863- ));
927+ )));
864928 self.fmt_impls.insert((tyname, tr), ());
865929 return Ok(());
866930 }
@@ -876,7 +940,7 @@ impl Lowerer {
876940 // The source type may be a tuple, whose rendering has
877941 // parens and commas in it; a Nim identifier may not.
878942 let name = format!("rsFrom{}{}", mangle(&tyname), mangle(&type_name(&src)));
879- self.forwards.push(self.head_of(&name, &m.sig, None)?);
943+ self.forwards.push(Fwd::Head(name.clone(), Box::new(m.sig.clone()), None, self.impl_generics.clone(), self.self_ty.clone()));
880944 self.from_types
881945 .insert((type_name(&src), tyname.clone()), src.clone());
882946 self.from_impls
@@ -917,10 +981,17 @@ impl Lowerer {
917981 None
918982 };
919983 let nim = trait_method_name(&tyname, &tr, &mname, recv.is_some());
920- self.forwards.push(self.head_of(&nim, &m.sig, recv.as_ref())?);
984+ self.forwards.push(Fwd::Head(nim.clone(), Box::new(m.sig.clone()), recv.clone(), self.impl_generics.clone(), self.self_ty.clone()));
921985 self.methods
922- .insert((tyname.clone(), mname.clone()), Sig { params, ret, generics: gen_names.clone() });
923- self.statics.insert((tyname.clone(), mname), nim);
986+ .insert((tyname.clone(), mname.clone()), Sig { params, ret: ret.clone(), generics: gen_names.clone() });
987+ self.statics.insert((tyname.clone(), mname.clone()), nim);
988+ if ret == Nim::Named(OPAQUE.into(), Vec::new()) {
989+ self.opaque_fns.push((
990+ OpaqueKey::Method(tyname.clone(), mname),
991+ m.sig.clone(),
992+ m.block.clone(),
993+ ));
994+ }
924995 }
925996 return Ok(());
926997 }
@@ -937,12 +1008,24 @@ impl Lowerer {
9371008 }
9381009 let recv = if takes_self(&m.sig) { Some(self_ty.clone()) } else { None };
9391010 let nim = self.fn_name(&self.cur_mod, &m.sig.ident.to_string());
940- let head = self.head_of(&nim, &m.sig, recv.as_ref())?;
941- self.forwards.push(head);
1011+ self.forwards.push(Fwd::Head(
1012+ nim.clone(),
1013+ Box::new(m.sig.clone()),
1014+ recv.clone(),
1015+ self.impl_generics.clone(),
1016+ self.self_ty.clone(),
1017+ ));
9421018 self.methods
943- .insert((tyname.clone(), m.sig.ident.to_string()), Sig { params, ret, generics: gen_names.clone() });
1019+ .insert((tyname.clone(), m.sig.ident.to_string()), Sig { params, ret: ret.clone(), generics: gen_names.clone() });
9441020 self.statics
9451021 .insert((tyname.clone(), m.sig.ident.to_string()), nim);
1022+ if ret == Nim::Named(OPAQUE.into(), Vec::new()) {
1023+ self.opaque_fns.push((
1024+ OpaqueKey::Method(tyname.clone(), m.sig.ident.to_string()),
1025+ m.sig.clone(),
1026+ m.block.clone(),
1027+ ));
1028+ }
9461029 }
9471030 }
9481031 }
@@ -1588,6 +1671,115 @@ impl Lowerer {
15881671 Ok(())
15891672 }
15901673
1674+
1675+ /// Give every return-position `impl Trait` its concrete type, taken from
1676+ /// the body that produces it.
1677+ ///
1678+ /// The body is lowered into a scratch buffer that is then thrown away --
1679+ /// only the *type* of the result expression is kept. Doing it with the
1680+ /// real lowering rather than a separate syntactic guess is what makes the
1681+ /// answer the same one the body will actually produce.
1682+ fn resolve_opaque(&mut self) -> Result<(), String> {
1683+ let opaque = Nim::Named(OPAQUE.into(), Vec::new());
1684+ let pending = std::mem::take(&mut self.opaque_fns);
1685+ let mut left = pending.clone();
1686+ // One function's opaque type may be another's; each round resolves at
1687+ // least one or nothing more can be learned.
1688+ for _ in 0..pending.len().max(1) {
1689+ let mut progressed = false;
1690+ let mut still = Vec::new();
1691+ for (key, sig, block) in left {
1692+ match self.opaque_of(&sig, &block) {
1693+ Some(t) if t != opaque => {
1694+ let sk = quote::quote!(#sig).to_string();
1695+ if let Some(prev) = self.opaque_ret.get(&sk) {
1696+ if prev != &t {
1697+ return Err(format!(
1698+ "two functions share the signature `{}` but \
1699+ their opaque return types are different \
1700+ (`{}` and `{}`); rustnim keys the \
1701+ resolution on the signature text, so it \
1702+ cannot tell them apart",
1703+ sk,
1704+ prev.render(),
1705+ t.render()
1706+ ));
1707+ }
1708+ }
1709+ self.opaque_ret.insert(sk, t.clone());
1710+ match &key {
1711+ OpaqueKey::Free(m, n) => {
1712+ if let Some(s) = self.fns.get_mut(&(m.clone(), n.clone())) {
1713+ s.ret = t;
1714+ }
1715+ }
1716+ OpaqueKey::Method(ty, n) => {
1717+ if let Some(s) = self.methods.get_mut(&(ty.clone(), n.clone())) {
1718+ s.ret = t;
1719+ }
1720+ }
1721+ }
1722+ progressed = true;
1723+ }
1724+ _ => still.push((key, sig, block)),
1725+ }
1726+ }
1727+ left = still;
1728+ if left.is_empty() || !progressed {
1729+ break;
1730+ }
1731+ }
1732+ if let Some((key, sig, _)) = left.first() {
1733+ let what = match key {
1734+ OpaqueKey::Free(_, n) => n.clone(),
1735+ OpaqueKey::Method(t, n) => format!("{t}::{n}"),
1736+ };
1737+ let ret = match &sig.output {
1738+ ReturnType::Type(_, t) => quote::quote!(#t).to_string(),
1739+ ReturnType::Default => String::new(),
1740+ };
1741+ return Err(format!(
1742+ "`fn {what}() -> {ret}`: the concrete type behind the opaque \
1743+ return type could not be read off the body. Rust allows only \
1744+ one such type per function, so it is there -- rustnim could \
1745+ not work out what it is"
1746+ ));
1747+ }
1748+ Ok(())
1749+ }
1750+
1751+ /// The type of what a body returns, lowered for real and then discarded.
1752+ fn opaque_of(&mut self, sig: &syn::Signature, block: &syn::Block) -> Option<Nim> {
1753+ let tail = block.stmts.iter().rev().find_map(|st| match st {
1754+ Stmt::Expr(e, None) => Some(e.clone()),
1755+ _ => None,
1756+ })?;
1757+ // Parameters have to be in scope, or every mention of one is an
1758+ // unknown name and the result types as nothing.
1759+ let (ptys, _) = self.signature(sig).ok()?;
1760+ self.push_scope();
1761+ let mut i = 0usize;
1762+ for a in &sig.inputs {
1763+ if let FnArg::Typed(t) = a {
1764+ if let Pat::Ident(id) = &*t.pat {
1765+ if let Some(ty) = ptys.get(i) {
1766+ self.bind(&id.ident.to_string(), ty.clone().owned());
1767+ }
1768+ }
1769+ i += 1;
1770+ }
1771+ }
1772+ let saved_out = self.out.clone();
1773+ let saved_indent = self.indent;
1774+ let saved_hoisted = self.hoisted.clone();
1775+ let r = self.expr(&tail);
1776+ self.out = saved_out;
1777+ self.indent = saved_indent;
1778+ self.hoisted = saved_hoisted;
1779+ self.pop_scope();
1780+ r.ok().and_then(|v| v.ty)
1781+ }
1782+
15911783 fn signature(&self, sig: &syn::Signature) -> Result<(Vec<Nim>, Nim), String> {
15921784 // `unsafe fn` marks a contract for callers; it does not change what
15931785 // the body means, so it lowers like any other proc.
@@ -1612,6 +1804,18 @@ impl Lowerer {
16121804 // which is a pragma rather than a type, so the return type here is
16131805 // the absence of one -- and the pragma is added at the proc head.
16141806 ReturnType::Type(_, t) if matches!(&**t, syn::Type::Never(_)) => Nim::Unit,
1807+ // Return-position `impl Trait` names one concrete type -- the one
1808+ // the body produces. Rust hides which; it does not permit two. So
1809+ // the type is recoverable from the body, and `resolve_opaque`
1810+ // recovers it. Marked here, resolved between passes.
1811+ ReturnType::Type(_, t)
1812+ if matches!(&**t, syn::Type::ImplTrait(_)) && self.map_ty(t).is_err() =>
1813+ {
1814+ match self.opaque_ret.get(&quote::quote!(#sig).to_string()) {
1815+ Some(t) => t.clone(),
1816+ None => Nim::Named(OPAQUE.into(), Vec::new()),
1817+ }
1818+ }
16151819 ReturnType::Type(_, t) => {
16161820 let n = self.map_ty(t)?;
16171821 if returns_borrow(t) { n } else { n.owned() }
@@ -33,6 +33,26 @@ const NIM_KEYWORDS: &[&str] = &[
33 /// inside `system`, which is a confusing place for it to surface.33 /// inside `system`, which is a confusing place for it to surface.
34 const NIM_SHADOWED: &[&str] = &["new"];34 const NIM_SHADOWED: &[&str] = &["new"];
35 35
36+/// Placeholder for a return-position `impl Trait` between the signature pass
37+/// and `resolve_opaque`. It never reaches the output: an unresolved one is an
38+/// error.
39+const OPAQUE: &str = "RsOpaque";
40+
41+/// Which table an opaque return type has to be written back into.
42+#[derive(Clone, Debug)]
43+enum Fwd {
44+ Text(String),
45+ /// Also carries the impl's generic parameters and `Self` type: rendering
46+ /// happens after collection, when that context is no longer current.
47+ Head(String, Box<syn::Signature>, Option<Nim>, Vec<String>, Option<Nim>),
48+}
49+
50+#[derive(Clone, Debug)]
51+enum OpaqueKey {
52+ Free(String, String),
53+ Method(String, String),
54+}
55+
36 fn ident(name: &str) -> String {56 fn ident(name: &str) -> String {
37 if NIM_KEYWORDS.contains(&name) || NIM_SHADOWED.contains(&name) {57 if NIM_KEYWORDS.contains(&name) || NIM_SHADOWED.contains(&name) {
38 return format!("{name}_r");58 return format!("{name}_r");
@@ -277,7 +297,10 @@ pub struct Lowerer {
277 /// bodies. Rust has no declaration-before-use rule and Nim does, so every297 /// bodies. Rust has no declaration-before-use rule and Nim does, so every
278 /// proc is declared up front rather than the input being reordered --298 /// proc is declared up front rather than the input being reordered --
279 /// which would not work for mutual recursion anyway.299 /// which would not work for mutual recursion anyway.
280- forwards: Vec<String>,300+ /// Forward declarations. Held unrendered where they come from a
301+ /// signature, because a return-position `impl Trait` in one is not
302+ /// resolved until after every signature has been collected.
303+ forwards: Vec<Fwd>,
281 /// Element type a `vec![..]` should build, from the binding's annotation.304 /// Element type a `vec![..]` should build, from the binding's annotation.
282 vec_expect: Option<Nim>,305 vec_expect: Option<Nim>,
283 /// While lowering a formatting impl: the `Formatter` parameter's name.306 /// While lowering a formatting impl: the `Formatter` parameter's name.
@@ -317,6 +340,14 @@ pub struct Lowerer {
317 /// Const generic parameters, by name, with their Nim type. Nim's `static`340 /// Const generic parameters, by name, with their Nim type. Nim's `static`
318 /// parameter is the same thing: a generic parameter that is a value.341 /// parameter is the same thing: a generic parameter that is a value.
319 const_gens: HashMap<String, String>,342 const_gens: HashMap<String, String>,
343+ /// Bodies of functions whose return type is a return-position `impl
344+ /// Trait`, keyed the same way as `fns`/`methods`, so `resolve_opaque` can
345+ /// read the concrete type back off the body.
346+ opaque_fns: Vec<(OpaqueKey, syn::Signature, syn::Block)>,
347+ /// Resolved opaque return types, keyed by the signature's source text.
348+ /// `signature` is called again on the emission pass and has only the
349+ /// signature to go on, so that is what the key is made of.
350+ opaque_ret: HashMap<String, Nim>,
320 /// Module names supplied as separate input files. A `mod x;` naming one351 /// Module names supplied as separate input files. A `mod x;` naming one
321 /// of these is satisfied by that file having been passed in.352 /// of these is satisfied by that file having been passed in.
322 pub modules: Vec<String>,353 pub modules: Vec<String>,
@@ -391,6 +422,8 @@ impl Lowerer {
391 local_items: HashMap::new(),422 local_items: HashMap::new(),
392 emitted_local: std::collections::HashSet::new(),423 emitted_local: std::collections::HashSet::new(),
393 const_gens: HashMap::new(),424 const_gens: HashMap::new(),
425+ opaque_fns: Vec::new(),
426+ opaque_ret: HashMap::new(),
394 modules: Vec::new(),427 modules: Vec::new(),
395 emitted: 0,428 emitted: 0,
396 features: Vec::new(),429 features: Vec::new(),
@@ -489,6 +522,16 @@ impl Lowerer {
489 self.collect(item)?;522 self.collect(item)?;
490 }523 }
491 }524 }
525+
526+ // Pass 1.5: resolve return-position `impl Trait`.
527+ //
528+ // Rust's opaque return type is a single concrete type -- the one the
529+ // body produces. The caller is not allowed to name it, but it is not
530+ // ambiguous, so reading it off the body is exact rather than a guess.
531+ // Repeated to a fixpoint because one such function may return the
532+ // result of another.
533+ self.resolve_opaque()?;
534+
492 // Pass 2: type definitions, which every signature may mention.535 // Pass 2: type definitions, which every signature may mention.
493 for (m, f) in files {536 for (m, f) in files {
494 self.cur_mod = m.clone();537 self.cur_mod = m.clone();
@@ -502,6 +545,17 @@ impl Lowerer {
502 // reordering the input would not handle mutual recursion anyway.545 // reordering the input would not handle mutual recursion anyway.
503 if !self.forwards.is_empty() {546 if !self.forwards.is_empty() {
504 for f in self.forwards.clone() {547 for f in self.forwards.clone() {
548+ let f = match f {
549+ Fwd::Text(t) => t,
550+ Fwd::Head(nim, sig, recv, gens, self_ty) => {
551+ let og = std::mem::replace(&mut self.impl_generics, gens);
552+ let os = std::mem::replace(&mut self.self_ty, self_ty);
553+ let r = self.head_of(&nim, &sig, recv.as_ref());
554+ self.impl_generics = og;
555+ self.self_ty = os;
556+ r?
557+ }
558+ };
505 self.line(&f);559 self.line(&f);
506 }560 }
507 self.blank();561 self.blank();
@@ -663,9 +717,19 @@ impl Lowerer {
663 gen_names.extend(self.impl_trait_params(&f.sig));717 gen_names.extend(self.impl_trait_params(&f.sig));
664 let name = f.sig.ident.to_string();718 let name = f.sig.ident.to_string();
665 let nim = self.fn_name(&self.cur_mod, &name);719 let nim = self.fn_name(&self.cur_mod, &name);
666- self.forwards.push(self.head_of(&nim, &f.sig, None)?);720+ self.forwards.push(Fwd::Head(nim.clone(), Box::new(f.sig.clone()), None, self.impl_generics.clone(), self.self_ty.clone()));
667 self.fns721 self.fns
668- .insert((self.cur_mod.clone(), name), Sig { params, ret, generics: gen_names.clone() });722+ .insert(
723+ (self.cur_mod.clone(), name.clone()),
724+ Sig { params, ret: ret.clone(), generics: gen_names.clone() },
725+ );
726+ if ret == Nim::Named(OPAQUE.into(), Vec::new()) {
727+ self.opaque_fns.push((
728+ OpaqueKey::Free(self.cur_mod.clone(), name),
729+ f.sig.clone(),
730+ (*f.block).clone(),
731+ ));
732+ }
669 }733 }
670 Item::Struct(s) => {734 Item::Struct(s) => {
671 self.note_const_generics(&s.generics)?;735 self.note_const_generics(&s.generics)?;
@@ -856,11 +920,11 @@ impl Lowerer {
856 return Ok(());920 return Ok(());
857 }921 }
858 if is_fmt_trait(&tr) {922 if is_fmt_trait(&tr) {
859- self.forwards.push(format!(923+ self.forwards.push(Fwd::Text(format!(
860 "proc {}*(self: {}): string",924 "proc {}*(self: {}): string",
861 fmt_proc(&tr),925 fmt_proc(&tr),
862 self_ty.render()926 self_ty.render()
863- ));927+ )));
864 self.fmt_impls.insert((tyname, tr), ());928 self.fmt_impls.insert((tyname, tr), ());
865 return Ok(());929 return Ok(());
866 }930 }
@@ -876,7 +940,7 @@ impl Lowerer {
876 // The source type may be a tuple, whose rendering has940 // The source type may be a tuple, whose rendering has
877 // parens and commas in it; a Nim identifier may not.941 // parens and commas in it; a Nim identifier may not.
878 let name = format!("rsFrom{}{}", mangle(&tyname), mangle(&type_name(&src)));942 let name = format!("rsFrom{}{}", mangle(&tyname), mangle(&type_name(&src)));
879- self.forwards.push(self.head_of(&name, &m.sig, None)?);943+ self.forwards.push(Fwd::Head(name.clone(), Box::new(m.sig.clone()), None, self.impl_generics.clone(), self.self_ty.clone()));
880 self.from_types944 self.from_types
881 .insert((type_name(&src), tyname.clone()), src.clone());945 .insert((type_name(&src), tyname.clone()), src.clone());
882 self.from_impls946 self.from_impls
@@ -917,10 +981,17 @@ impl Lowerer {
917 None981 None
918 };982 };
919 let nim = trait_method_name(&tyname, &tr, &mname, recv.is_some());983 let nim = trait_method_name(&tyname, &tr, &mname, recv.is_some());
920- self.forwards.push(self.head_of(&nim, &m.sig, recv.as_ref())?);984+ self.forwards.push(Fwd::Head(nim.clone(), Box::new(m.sig.clone()), recv.clone(), self.impl_generics.clone(), self.self_ty.clone()));
921 self.methods985 self.methods
922- .insert((tyname.clone(), mname.clone()), Sig { params, ret, generics: gen_names.clone() });986+ .insert((tyname.clone(), mname.clone()), Sig { params, ret: ret.clone(), generics: gen_names.clone() });
923- self.statics.insert((tyname.clone(), mname), nim);987+ self.statics.insert((tyname.clone(), mname.clone()), nim);
988+ if ret == Nim::Named(OPAQUE.into(), Vec::new()) {
989+ self.opaque_fns.push((
990+ OpaqueKey::Method(tyname.clone(), mname),
991+ m.sig.clone(),
992+ m.block.clone(),
993+ ));
994+ }
924 }995 }
925 return Ok(());996 return Ok(());
926 }997 }
@@ -937,12 +1008,24 @@ impl Lowerer {
937 }1008 }
938 let recv = if takes_self(&m.sig) { Some(self_ty.clone()) } else { None };1009 let recv = if takes_self(&m.sig) { Some(self_ty.clone()) } else { None };
939 let nim = self.fn_name(&self.cur_mod, &m.sig.ident.to_string());1010 let nim = self.fn_name(&self.cur_mod, &m.sig.ident.to_string());
940- let head = self.head_of(&nim, &m.sig, recv.as_ref())?;1011+ self.forwards.push(Fwd::Head(
941- self.forwards.push(head);1012+ nim.clone(),
1013+ Box::new(m.sig.clone()),
1014+ recv.clone(),
1015+ self.impl_generics.clone(),
1016+ self.self_ty.clone(),
1017+ ));
942 self.methods1018 self.methods
943- .insert((tyname.clone(), m.sig.ident.to_string()), Sig { params, ret, generics: gen_names.clone() });1019+ .insert((tyname.clone(), m.sig.ident.to_string()), Sig { params, ret: ret.clone(), generics: gen_names.clone() });
944 self.statics1020 self.statics
945 .insert((tyname.clone(), m.sig.ident.to_string()), nim);1021 .insert((tyname.clone(), m.sig.ident.to_string()), nim);
1022+ if ret == Nim::Named(OPAQUE.into(), Vec::new()) {
1023+ self.opaque_fns.push((
1024+ OpaqueKey::Method(tyname.clone(), m.sig.ident.to_string()),
1025+ m.sig.clone(),
1026+ m.block.clone(),
1027+ ));
1028+ }
946 }1029 }
947 }1030 }
948 }1031 }
@@ -1588,6 +1671,115 @@ impl Lowerer {
1588 Ok(())1671 Ok(())
1589 }1672 }
1590 1673
1674+
1675+ /// Give every return-position `impl Trait` its concrete type, taken from
1676+ /// the body that produces it.
1677+ ///
1678+ /// The body is lowered into a scratch buffer that is then thrown away --
1679+ /// only the *type* of the result expression is kept. Doing it with the
1680+ /// real lowering rather than a separate syntactic guess is what makes the
1681+ /// answer the same one the body will actually produce.
1682+ fn resolve_opaque(&mut self) -> Result<(), String> {
1683+ let opaque = Nim::Named(OPAQUE.into(), Vec::new());
1684+ let pending = std::mem::take(&mut self.opaque_fns);
1685+ let mut left = pending.clone();
1686+ // One function's opaque type may be another's; each round resolves at
1687+ // least one or nothing more can be learned.
1688+ for _ in 0..pending.len().max(1) {
1689+ let mut progressed = false;
1690+ let mut still = Vec::new();
1691+ for (key, sig, block) in left {
1692+ match self.opaque_of(&sig, &block) {
1693+ Some(t) if t != opaque => {
1694+ let sk = quote::quote!(#sig).to_string();
1695+ if let Some(prev) = self.opaque_ret.get(&sk) {
1696+ if prev != &t {
1697+ return Err(format!(
1698+ "two functions share the signature `{}` but \
1699+ their opaque return types are different \
1700+ (`{}` and `{}`); rustnim keys the \
1701+ resolution on the signature text, so it \
1702+ cannot tell them apart",
1703+ sk,
1704+ prev.render(),
1705+ t.render()
1706+ ));
1707+ }
1708+ }
1709+ self.opaque_ret.insert(sk, t.clone());
1710+ match &key {
1711+ OpaqueKey::Free(m, n) => {
1712+ if let Some(s) = self.fns.get_mut(&(m.clone(), n.clone())) {
1713+ s.ret = t;
1714+ }
1715+ }
1716+ OpaqueKey::Method(ty, n) => {
1717+ if let Some(s) = self.methods.get_mut(&(ty.clone(), n.clone())) {
1718+ s.ret = t;
1719+ }
1720+ }
1721+ }
1722+ progressed = true;
1723+ }
1724+ _ => still.push((key, sig, block)),
1725+ }
1726+ }
1727+ left = still;
1728+ if left.is_empty() || !progressed {
1729+ break;
1730+ }
1731+ }
1732+ if let Some((key, sig, _)) = left.first() {
1733+ let what = match key {
1734+ OpaqueKey::Free(_, n) => n.clone(),
1735+ OpaqueKey::Method(t, n) => format!("{t}::{n}"),
1736+ };
1737+ let ret = match &sig.output {
1738+ ReturnType::Type(_, t) => quote::quote!(#t).to_string(),
1739+ ReturnType::Default => String::new(),
1740+ };
1741+ return Err(format!(
1742+ "`fn {what}() -> {ret}`: the concrete type behind the opaque \
1743+ return type could not be read off the body. Rust allows only \
1744+ one such type per function, so it is there -- rustnim could \
1745+ not work out what it is"
1746+ ));
1747+ }
1748+ Ok(())
1749+ }
1750+
1751+ /// The type of what a body returns, lowered for real and then discarded.
1752+ fn opaque_of(&mut self, sig: &syn::Signature, block: &syn::Block) -> Option<Nim> {
1753+ let tail = block.stmts.iter().rev().find_map(|st| match st {
1754+ Stmt::Expr(e, None) => Some(e.clone()),
1755+ _ => None,
1756+ })?;
1757+ // Parameters have to be in scope, or every mention of one is an
1758+ // unknown name and the result types as nothing.
1759+ let (ptys, _) = self.signature(sig).ok()?;
1760+ self.push_scope();
1761+ let mut i = 0usize;
1762+ for a in &sig.inputs {
1763+ if let FnArg::Typed(t) = a {
1764+ if let Pat::Ident(id) = &*t.pat {
1765+ if let Some(ty) = ptys.get(i) {
1766+ self.bind(&id.ident.to_string(), ty.clone().owned());
1767+ }
1768+ }
1769+ i += 1;
1770+ }
1771+ }
1772+ let saved_out = self.out.clone();
1773+ let saved_indent = self.indent;
1774+ let saved_hoisted = self.hoisted.clone();
1775+ let r = self.expr(&tail);
1776+ self.out = saved_out;
1777+ self.indent = saved_indent;
1778+ self.hoisted = saved_hoisted;
1779+ self.pop_scope();
1780+ r.ok().and_then(|v| v.ty)
1781+ }
1782+
1591 fn signature(&self, sig: &syn::Signature) -> Result<(Vec<Nim>, Nim), String> {1783 fn signature(&self, sig: &syn::Signature) -> Result<(Vec<Nim>, Nim), String> {
1592 // `unsafe fn` marks a contract for callers; it does not change what1784 // `unsafe fn` marks a contract for callers; it does not change what
1593 // the body means, so it lowers like any other proc.1785 // the body means, so it lowers like any other proc.
@@ -1612,6 +1804,18 @@ impl Lowerer {
1612 // which is a pragma rather than a type, so the return type here is1804 // which is a pragma rather than a type, so the return type here is
1613 // the absence of one -- and the pragma is added at the proc head.1805 // the absence of one -- and the pragma is added at the proc head.
1614 ReturnType::Type(_, t) if matches!(&**t, syn::Type::Never(_)) => Nim::Unit,1806 ReturnType::Type(_, t) if matches!(&**t, syn::Type::Never(_)) => Nim::Unit,
1807+ // Return-position `impl Trait` names one concrete type -- the one
1808+ // the body produces. Rust hides which; it does not permit two. So
1809+ // the type is recoverable from the body, and `resolve_opaque`
1810+ // recovers it. Marked here, resolved between passes.
1811+ ReturnType::Type(_, t)
1812+ if matches!(&**t, syn::Type::ImplTrait(_)) && self.map_ty(t).is_err() =>
1813+ {
1814+ match self.opaque_ret.get(&quote::quote!(#sig).to_string()) {
1815+ Some(t) => t.clone(),
1816+ None => Nim::Named(OPAQUE.into(), Vec::new()),
1817+ }
1818+ }
1615 ReturnType::Type(_, t) => {1819 ReturnType::Type(_, t) => {
1616 let n = self.map_ty(t)?;1820 let n = self.map_ty(t)?;
1617 if returns_borrow(t) { n } else { n.owned() }1821 if returns_borrow(t) { n } else { n.owned() }
added tests/cases/056-return-impl-trait.rs +35 -0
new file mode 100644
@@ -0,0 +1,35 @@
1+// Return-position `impl Trait`. Rust hides which concrete type comes back but
2+// does not permit two, so the type is recoverable from the body.
3+use std::fmt::Display;
4+
5+struct Tag {
6+ n: u32,
7+}
8+
9+impl Display for Tag {
10+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
11+ write!(f, "tag<{}>", self.n)
12+ }
13+}
14+
15+fn make(n: u32) -> impl Display {
16+ Tag { n: n * 2 }
17+}
18+
19+fn relay(n: u32) -> impl Display {
20+ make(n + 1)
21+}
22+
23+struct Wrap;
24+
25+impl Wrap {
26+ fn build(&self, n: u32) -> impl Display {
27+ Tag { n }
28+ }
29+}
30+
31+fn main() {
32+ println!("{}", make(3));
33+ println!("{}", relay(3));
34+ println!("{}", Wrap.build(9));
35+}
new file mode 100644
@@ -0,0 +1,35 @@
1+// Return-position `impl Trait`. Rust hides which concrete type comes back but
2+// does not permit two, so the type is recoverable from the body.
3+use std::fmt::Display;
4+
5+struct Tag {
6+ n: u32,
7+}
8+
9+impl Display for Tag {
10+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
11+ write!(f, "tag<{}>", self.n)
12+ }
13+}
14+
15+fn make(n: u32) -> impl Display {
16+ Tag { n: n * 2 }
17+}
18+
19+fn relay(n: u32) -> impl Display {
20+ make(n + 1)
21+}
22+
23+struct Wrap;
24+
25+impl Wrap {
26+ fn build(&self, n: u32) -> impl Display {
27+ Tag { n }
28+ }
29+}
30+
31+fn main() {
32+ println!("{}", make(3));
33+ println!("{}", relay(3));
34+ println!("{}", Wrap.build(9));
35+}