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

Emit trait default bodies as generic procs

rustnim refused any trait with a default body: trait resolution is not
modelled, so there was no impl to emit the body into, and dropping it would
have removed code the implementors inherit. That refusal held for the whole
project until now.

It turns out not to need trait resolution. A default body is code that applies
to every implementor, which is what a Nim generic proc is -- and trait methods
were already named `rs{Trait}_{method}` and overloaded on the receiver, so the
default emitted with `Self` as a type parameter is the fallback and an `impl`
that overrides it is a more specific overload. Nim prefers the specific one.
That is Rust's rule, arrived at by Nim's.

Four things had to come with it:

  * A call to an inherited method resolves through the traits the receiver's
    type implements, *before* the arguments are lowered -- the trait's
    signature is what says their types, and a closure argument has no shape
    without it.
  * `F: FnOnce(&Self) -> R` says `F` is callable and with what signature, so a
    body can call what it was handed. `Self` has to be a type while those
    bounds are collected or the bound cannot be recorded at all.
  * `subst` now descends into proc types. It did not, so `Self` survived into
    the expectation and a closure came out annotated `proc (x: Self)`.
  * A return type that is a trait parameter appearing in no argument cannot be
    inferred from a call. Nim's `auto` takes it from the body -- where Rust's
    monomorphisation gets it too -- and the parameter is then not a parameter
    at all, so it is dropped from the generic list.

`apply` compiles, and `tests/cases/071` covers an inherited default, an
overridden one, a default calling another default, and one that invokes a
closure it was passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nandithebull committed 2026-09-20T12:08:24-07:00 Browse files
7e4c463 parent: 211aebe
modified src/lower.rs +205 -15
@@ -381,6 +381,14 @@ pub struct Lowerer {
381381 /// the closure must have -- which is the only thing that types a closure
382382 /// passed to a generic parameter.
383383 fn_blankets: HashMap<String, (Vec<String>, Vec<Nim>, Nim)>,
384+ /// Generic parameters that are callable: `F: FnOnce(A) -> B`.
385+ fn_bounds: HashMap<String, Nim>,
386+ /// Which traits each type implements, so a method it does not declare
387+ /// can still be found on a trait that gives it a default body.
388+ type_traits: HashMap<String, Vec<(String, Vec<Nim>)>>,
389+ /// Return type to use instead of the signature's, for a default body
390+ /// whose return type Nim cannot infer.
391+ ret_override: Option<Nim>,
384392 /// Procs that cannot be translated, by Nim name, with the reason.
385393 ///
386394 /// Not an error on its own. A dependency graph carries far more code than
@@ -504,6 +512,9 @@ impl Lowerer {
504512 opaque_why: None,
505513 trait_methods: HashMap::new(),
506514 fn_blankets: HashMap::new(),
515+ fn_bounds: HashMap::new(),
516+ type_traits: HashMap::new(),
517+ ret_override: None,
507518 untranslatable: HashMap::new(),
508519 enum_module: HashMap::new(),
509520 report: false,
@@ -1003,9 +1014,16 @@ impl Lowerer {
10031014 // so what is needed here is the *signature*, not a body.
10041015 let tname = t.ident.to_string();
10051016 let tgen = Self::generics_of(&t.generics);
1006- let outer = std::mem::replace(&mut self.impl_generics, tgen.clone());
1017+ let mut g = vec!["Self".to_string()];
1018+ g.extend(tgen.clone());
1019+ let outer = std::mem::replace(&mut self.impl_generics, g);
1020+ // `Self` has to be a type here, or a bound written in terms of
1021+ // it -- `F: FnOnce(&Self) -> R` -- cannot be recorded at all.
1022+ let outer_self =
1023+ self.self_ty.replace(Nim::Named("Self".into(), Vec::new()));
10071024 for it in &t.items {
10081025 if let syn::TraitItem::Fn(f) = it {
1026+ self.note_bounds(&f.sig.generics);
10091027 if let Ok((mut params, ret)) = self.signature(&f.sig) {
10101028 if takes_self(&f.sig) {
10111029 params.insert(0, Nim::Named("Self".into(), Vec::new()));
@@ -1018,6 +1036,7 @@ impl Lowerer {
10181036 }
10191037 }
10201038 self.impl_generics = outer;
1039+ self.self_ty = outer_self;
10211040 }
10221041 Item::Static(st) => {
10231042 // The name and type, so a body reached before the emission
@@ -1214,6 +1233,25 @@ impl Lowerer {
12141233 self.note_const_generics(&im.generics)?;
12151234 self.note_bounds(&im.generics);
12161235 self.note_fn_blanket(im);
1236+ if let (Some((path, _)), Ok(t)) = (&im.trait_, self.map_ty(&im.self_ty)) {
1237+ if let Some(seg) = path.segments.last() {
1238+ let targs: Vec<Nim> = match &seg.arguments {
1239+ syn::PathArguments::AngleBracketed(a) => a
1240+ .args
1241+ .iter()
1242+ .filter_map(|g| match g {
1243+ GenericArgument::Type(x) => self.map_ty(x).ok(),
1244+ _ => None,
1245+ })
1246+ .collect(),
1247+ _ => Vec::new(),
1248+ };
1249+ self.type_traits
1250+ .entry(type_name(&t))
1251+ .or_default()
1252+ .push((seg.ident.to_string(), targs));
1253+ }
1254+ }
12171255 // Associated consts are registered here for the same reason anything
12181256 // else is: an array length elsewhere may name one, and that name has
12191257 // to resolve before any body is lowered. `assoc_const` records them
@@ -1851,6 +1889,23 @@ impl Lowerer {
18511889 for b in bounds {
18521890 let syn::TypeParamBound::Trait(tb) = b else { continue };
18531891 let Some(seg) = tb.path.segments.last() else { continue };
1892+ // `F: FnOnce(A) -> B` says `F` *is* a proc of that shape,
1893+ // which is what lets the body call it.
1894+ if matches!(seg.ident.to_string().as_str(), "Fn" | "FnMut" | "FnOnce") {
1895+ if let syn::PathArguments::Parenthesized(pa) = &seg.arguments {
1896+ let args: Result<Vec<Nim>, String> =
1897+ pa.inputs.iter().map(|x| me.map_ty(&x.ty)).collect();
1898+ let ret = match &pa.output {
1899+ ReturnType::Default => Ok(Nim::Unit),
1900+ ReturnType::Type(_, t) => me.map_ty(t),
1901+ };
1902+ if let (Ok(a), Ok(r)) = (args, ret) {
1903+ me.fn_bounds
1904+ .insert(name.clone(), Nim::Proc(a, Box::new(r)));
1905+ }
1906+ }
1907+ continue;
1908+ }
18541909 let args: Vec<Nim> = match &seg.arguments {
18551910 syn::PathArguments::AngleBracketed(a) => a
18561911 .args
@@ -2010,6 +2065,16 @@ impl Lowerer {
20102065 Nim::Array(n, e) => Nim::Array(n.clone(), Box::new(Self::subst(e, m))),
20112066 Nim::Var(e) => Nim::Var(Box::new(Self::subst(e, m))),
20122067 Nim::Tuple(ts) => Nim::Tuple(ts.iter().map(|x| Self::subst(x, m)).collect()),
2068+ // A proc type carries parameters too: `F: FnOnce(&Self) -> R` is
2069+ // useless as an expectation if `Self` survives into it.
2070+ Nim::Proc(a, r) => Nim::Proc(
2071+ a.iter().map(|x| Self::subst(x, m)).collect(),
2072+ Box::new(Self::subst(r, m)),
2073+ ),
2074+ Nim::Ptr(e) => Nim::Ptr(Box::new(Self::subst(e, m))),
2075+ Nim::ConstPtr(e) => Nim::ConstPtr(Box::new(Self::subst(e, m))),
2076+ Nim::Iter(e) => Nim::Iter(Box::new(Self::subst(e, m))),
2077+ Nim::Future(e) => Nim::Future(Box::new(Self::subst(e, m))),
20132078 other => other.clone(),
20142079 }
20152080 }
@@ -2838,22 +2903,56 @@ impl Lowerer {
28382903 Item::Macro(m) if path_name(&m.mac.path) == "macro_rules" => Ok(()),
28392904 Item::Type(_) => Ok(()), // expanded at every use site
28402905 Item::Trait(t) => {
2841- // We do not model trait resolution, so a declaration generates
2842- // nothing and a use that needed it is rejected where it
2843- // appears. A *default body*, though, is code: dropping it
2844- // would silently remove a method the impls inherit.
2906+ // A trait declaration generates nothing by itself. A *default
2907+ // body* is code, though, and it is code that applies to every
2908+ // implementor -- which is what a Nim generic proc is.
2909+ //
2910+ // Trait methods are already named `rs{Trait}_{method}` and
2911+ // overloaded on the receiver, so a default body emitted with
2912+ // `Self` as a type parameter is the fallback, and an `impl`
2913+ // that overrides it is a more specific overload that Nim
2914+ // prefers. That is Rust's rule, arrived at by Nim's.
2915+ let tname = t.ident.to_string();
2916+ let tgen = Self::generics_of(&t.generics);
28452917 for it in &t.items {
2846- if let syn::TraitItem::Fn(f) = it {
2847- if f.default.is_some() {
2848- return Err(format!(
2849- "`trait {}` gives `{}` a default body; trait \
2850- resolution is not modelled, so that body has no \
2851- impl to be emitted into and dropping it would \
2852- remove code",
2853- t.ident, f.sig.ident
2854- ));
2918+ let syn::TraitItem::Fn(f) = it else { continue };
2919+ let Some(body) = &f.default else { continue };
2920+ let outer_g = std::mem::replace(&mut self.impl_generics, {
2921+ let mut g = vec!["Self".to_string()];
2922+ g.extend(tgen.clone());
2923+ g
2924+ });
2925+ let outer_self =
2926+ self.self_ty.replace(Nim::Named("Self".into(), Vec::new()));
2927+ // The method's own bounds: `fn apply<F: FnOnce(Self) -> Res>`
2928+ // is what says `f` can be called.
2929+ self.note_bounds(&f.sig.generics);
2930+ let nim = trait_method_name(
2931+ &tname,
2932+ &tname,
2933+ &f.sig.ident.to_string(),
2934+ takes_self(&f.sig),
2935+ );
2936+ let recv = takes_self(&f.sig)
2937+ .then(|| Nim::Named("Self".into(), Vec::new()));
2938+ // A return type that is a trait parameter appearing
2939+ // nowhere in the arguments cannot be inferred from a call.
2940+ // Nim's `auto` takes it from the body, which is where
2941+ // Rust's monomorphisation gets it too.
2942+ if let Ok((ptys, rt)) = self.signature(&f.sig) {
2943+ if let Nim::Named(g, a) = &rt {
2944+ if a.is_empty()
2945+ && g != "Self"
2946+ && !ptys.iter().any(|p| Self::mentions_any(p, &[g.clone()]))
2947+ {
2948+ self.ret_override = Some(Nim::Prim("auto".into()));
2949+ }
28552950 }
28562951 }
2952+ let r = self.func_named(&nim, &f.sig, body, recv);
2953+ self.self_ty = outer_self;
2954+ self.impl_generics = outer_g;
2955+ r?;
28572956 }
28582957 Ok(())
28592958 }
@@ -3651,6 +3750,9 @@ impl Lowerer {
36513750 self.opaque_hint = self.opaque_ret.get(&opaque_key(sig, body)).cloned();
36523751 let (ptys, ret) = self.signature(sig)?;
36533752 self.opaque_hint = None;
3753+ let trim = self.ret_override.is_some();
3754+ let recv_ty = recv.clone();
3755+ let ret = self.ret_override.take().unwrap_or(ret);
36543756
36553757 self.push_scope();
36563758 let mut rendered: Vec<String> = Vec::new();
@@ -3705,6 +3807,18 @@ impl Lowerer {
37053807 gparams.push(g);
37063808 }
37073809 }
3810+ // A parameter that appears in no argument cannot be bound by a call.
3811+ // With an `auto` return there is nothing else to bind it either, so it
3812+ // is not a parameter at all -- it was only ever the return type's name.
3813+ if trim {
3814+ // The receiver counts: `self: Self` is what binds `Self`, and it
3815+ // is not one of `ptys`.
3816+ let mut bound: Vec<Nim> = ptys.clone();
3817+ if let Some(r) = &recv_ty {
3818+ bound.push(r.clone());
3819+ }
3820+ gparams.retain(|g| bound.iter().any(|p| Self::mentions_any(p, &[g.clone()])));
3821+ }
37083822 let gens = self.gen_list(&gparams);
37093823 let outer_fg = std::mem::replace(&mut self.fn_generics, gparams.clone());
37103824 let head = if ret == Nim::Unit {
@@ -6938,6 +7052,68 @@ impl Lowerer {
69387052 format!("(block:\n{}\n{pad}{})", copies.join("\n"), proc_code)
69397053 }
69407054
7055+ /// A method the receiver's type does not declare, but a trait it
7056+ /// implements does -- an inherited default body.
7057+ ///
7058+ /// Those are emitted as generic procs under the trait's name, so the call
7059+ /// is the same one an overriding impl would answer; Nim picks the more
7060+ /// specific overload where there is one, which is Rust's rule.
7061+ fn inherited_default(
7062+ &mut self,
7063+ m: &syn::ExprMethodCall,
7064+ name: &str,
7065+ recv: &Val,
7066+ ) -> Result<Option<Val>, String> {
7067+ let Some(rt) = recv.ty.clone() else { return Ok(None) };
7068+ let rn = type_name(&rt.clone().unvar());
7069+ if self.fn_generics.iter().any(|g| *g == rn) {
7070+ return Ok(None);
7071+ }
7072+ if self.methods.contains_key(&(rn.clone(), name.to_string())) {
7073+ return Ok(None);
7074+ }
7075+ let trs = self.type_traits.get(&rn).cloned().unwrap_or_default();
7076+ let Some((tr, targs)) = trs
7077+ .iter()
7078+ .find(|(tr, _)| self.trait_methods.contains_key(&(tr.clone(), name.to_string())))
7079+ else {
7080+ return Ok(None);
7081+ };
7082+ let sig = self.trait_methods[&(tr.clone(), name.to_string())].clone();
7083+ // `Self` is the receiver's type here, and the trait's own parameters
7084+ // are whatever the `impl` supplied for them.
7085+ let mut sm: HashMap<String, Nim> = HashMap::new();
7086+ sm.insert("Self".into(), rt.clone().unvar());
7087+ for (p, a) in sig.generics.iter().zip(targs.iter()) {
7088+ sm.insert(p.clone(), a.clone());
7089+ }
7090+ let mut codes = vec![recv.code.clone()];
7091+ for (i, a) in m.args.iter().enumerate() {
7092+ let raw = sig.params.get(i + 1).cloned();
7093+ // A parameter that is a callable generic says what shape a
7094+ // closure written here has.
7095+ let want = match &raw {
7096+ Some(Nim::Named(g, _)) if self.fn_bounds.contains_key(g) => {
7097+ Some(Self::subst(&self.fn_bounds[g].clone(), &sm))
7098+ }
7099+ Some(t) if !Self::mentions_any(t, &sig.generics) && type_name(t) != "Self" => {
7100+ Some(t.clone())
7101+ }
7102+ _ => None,
7103+ };
7104+ codes.push(self.expr_at(a, want.as_ref())?.code);
7105+ }
7106+ let ret = Self::subst(&sig.ret, &sm);
7107+ Ok(Some(Val::new(
7108+ format!(
7109+ "{}({})",
7110+ trait_method_name(&rn, tr, name, true),
7111+ codes.join(", ")
7112+ ),
7113+ Some(ret),
7114+ )))
7115+ }
7116+
69417117 /// `|x| x + 1` -> a Nim anonymous proc.
69427118 ///
69437119 /// Nim's closures capture by reference, as Rust's non-`move` closures do.
@@ -7768,7 +7944,13 @@ impl Lowerer {
77687944 // conversion, e.g. `String::from(..)`; handled by the method path.
77697945 // Calling a proc-typed local, which is how an `impl Fn(..)` parameter
77707946 // is invoked.
7771- if let Some(Nim::Proc(ptys, ret)) = self.lookup(&name) {
7947+ // A local whose type is a generic parameter bound by `Fn`: the bound
7948+ // says it is callable, and with what signature.
7949+ let callee_ty = match self.lookup(&name) {
7950+ Some(Nim::Named(g, _)) => self.fn_bounds.get(&g).cloned(),
7951+ other => other,
7952+ };
7953+ if let Some(Nim::Proc(ptys, ret)) = callee_ty {
77727954 // Re-lowered against the proc's own parameter types: a literal
77737955 // argument takes its width from them, and nothing earlier in this
77747956 // function knew them.
@@ -8073,6 +8255,14 @@ impl Lowerer {
80738255 ("push", Some(Nim::Seq(t))) | ("push", Some(Nim::OpenArray(t))) => Some((**t).clone()),
80748256 (_, t) => t.clone(),
80758257 };
8258+ // A method the receiver's type does not declare, but a trait it
8259+ // implements does -- an inherited default body. Resolved before the
8260+ // arguments are lowered, because the trait's signature is what says
8261+ // their types: a closure argument has no shape without it.
8262+ if let Some(v) = self.inherited_default(m, &name, &recv)? {
8263+ return Ok(v);
8264+ }
8265+
80768266 let mut args = Vec::new();
80778267 for a in &m.args {
80788268 args.push(self.expr_at(a, arg_want.as_ref())?);
@@ -381,6 +381,14 @@ pub struct Lowerer {
381 /// the closure must have -- which is the only thing that types a closure381 /// the closure must have -- which is the only thing that types a closure
382 /// passed to a generic parameter.382 /// passed to a generic parameter.
383 fn_blankets: HashMap<String, (Vec<String>, Vec<Nim>, Nim)>,383 fn_blankets: HashMap<String, (Vec<String>, Vec<Nim>, Nim)>,
384+ /// Generic parameters that are callable: `F: FnOnce(A) -> B`.
385+ fn_bounds: HashMap<String, Nim>,
386+ /// Which traits each type implements, so a method it does not declare
387+ /// can still be found on a trait that gives it a default body.
388+ type_traits: HashMap<String, Vec<(String, Vec<Nim>)>>,
389+ /// Return type to use instead of the signature's, for a default body
390+ /// whose return type Nim cannot infer.
391+ ret_override: Option<Nim>,
384 /// Procs that cannot be translated, by Nim name, with the reason.392 /// Procs that cannot be translated, by Nim name, with the reason.
385 ///393 ///
386 /// Not an error on its own. A dependency graph carries far more code than394 /// Not an error on its own. A dependency graph carries far more code than
@@ -504,6 +512,9 @@ impl Lowerer {
504 opaque_why: None,512 opaque_why: None,
505 trait_methods: HashMap::new(),513 trait_methods: HashMap::new(),
506 fn_blankets: HashMap::new(),514 fn_blankets: HashMap::new(),
515+ fn_bounds: HashMap::new(),
516+ type_traits: HashMap::new(),
517+ ret_override: None,
507 untranslatable: HashMap::new(),518 untranslatable: HashMap::new(),
508 enum_module: HashMap::new(),519 enum_module: HashMap::new(),
509 report: false,520 report: false,
@@ -1003,9 +1014,16 @@ impl Lowerer {
1003 // so what is needed here is the *signature*, not a body.1014 // so what is needed here is the *signature*, not a body.
1004 let tname = t.ident.to_string();1015 let tname = t.ident.to_string();
1005 let tgen = Self::generics_of(&t.generics);1016 let tgen = Self::generics_of(&t.generics);
1006- let outer = std::mem::replace(&mut self.impl_generics, tgen.clone());1017+ let mut g = vec!["Self".to_string()];
1018+ g.extend(tgen.clone());
1019+ let outer = std::mem::replace(&mut self.impl_generics, g);
1020+ // `Self` has to be a type here, or a bound written in terms of
1021+ // it -- `F: FnOnce(&Self) -> R` -- cannot be recorded at all.
1022+ let outer_self =
1023+ self.self_ty.replace(Nim::Named("Self".into(), Vec::new()));
1007 for it in &t.items {1024 for it in &t.items {
1008 if let syn::TraitItem::Fn(f) = it {1025 if let syn::TraitItem::Fn(f) = it {
1026+ self.note_bounds(&f.sig.generics);
1009 if let Ok((mut params, ret)) = self.signature(&f.sig) {1027 if let Ok((mut params, ret)) = self.signature(&f.sig) {
1010 if takes_self(&f.sig) {1028 if takes_self(&f.sig) {
1011 params.insert(0, Nim::Named("Self".into(), Vec::new()));1029 params.insert(0, Nim::Named("Self".into(), Vec::new()));
@@ -1018,6 +1036,7 @@ impl Lowerer {
1018 }1036 }
1019 }1037 }
1020 self.impl_generics = outer;1038 self.impl_generics = outer;
1039+ self.self_ty = outer_self;
1021 }1040 }
1022 Item::Static(st) => {1041 Item::Static(st) => {
1023 // The name and type, so a body reached before the emission1042 // The name and type, so a body reached before the emission
@@ -1214,6 +1233,25 @@ impl Lowerer {
1214 self.note_const_generics(&im.generics)?;1233 self.note_const_generics(&im.generics)?;
1215 self.note_bounds(&im.generics);1234 self.note_bounds(&im.generics);
1216 self.note_fn_blanket(im);1235 self.note_fn_blanket(im);
1236+ if let (Some((path, _)), Ok(t)) = (&im.trait_, self.map_ty(&im.self_ty)) {
1237+ if let Some(seg) = path.segments.last() {
1238+ let targs: Vec<Nim> = match &seg.arguments {
1239+ syn::PathArguments::AngleBracketed(a) => a
1240+ .args
1241+ .iter()
1242+ .filter_map(|g| match g {
1243+ GenericArgument::Type(x) => self.map_ty(x).ok(),
1244+ _ => None,
1245+ })
1246+ .collect(),
1247+ _ => Vec::new(),
1248+ };
1249+ self.type_traits
1250+ .entry(type_name(&t))
1251+ .or_default()
1252+ .push((seg.ident.to_string(), targs));
1253+ }
1254+ }
1217 // Associated consts are registered here for the same reason anything1255 // Associated consts are registered here for the same reason anything
1218 // else is: an array length elsewhere may name one, and that name has1256 // else is: an array length elsewhere may name one, and that name has
1219 // to resolve before any body is lowered. `assoc_const` records them1257 // to resolve before any body is lowered. `assoc_const` records them
@@ -1851,6 +1889,23 @@ impl Lowerer {
1851 for b in bounds {1889 for b in bounds {
1852 let syn::TypeParamBound::Trait(tb) = b else { continue };1890 let syn::TypeParamBound::Trait(tb) = b else { continue };
1853 let Some(seg) = tb.path.segments.last() else { continue };1891 let Some(seg) = tb.path.segments.last() else { continue };
1892+ // `F: FnOnce(A) -> B` says `F` *is* a proc of that shape,
1893+ // which is what lets the body call it.
1894+ if matches!(seg.ident.to_string().as_str(), "Fn" | "FnMut" | "FnOnce") {
1895+ if let syn::PathArguments::Parenthesized(pa) = &seg.arguments {
1896+ let args: Result<Vec<Nim>, String> =
1897+ pa.inputs.iter().map(|x| me.map_ty(&x.ty)).collect();
1898+ let ret = match &pa.output {
1899+ ReturnType::Default => Ok(Nim::Unit),
1900+ ReturnType::Type(_, t) => me.map_ty(t),
1901+ };
1902+ if let (Ok(a), Ok(r)) = (args, ret) {
1903+ me.fn_bounds
1904+ .insert(name.clone(), Nim::Proc(a, Box::new(r)));
1905+ }
1906+ }
1907+ continue;
1908+ }
1854 let args: Vec<Nim> = match &seg.arguments {1909 let args: Vec<Nim> = match &seg.arguments {
1855 syn::PathArguments::AngleBracketed(a) => a1910 syn::PathArguments::AngleBracketed(a) => a
1856 .args1911 .args
@@ -2010,6 +2065,16 @@ impl Lowerer {
2010 Nim::Array(n, e) => Nim::Array(n.clone(), Box::new(Self::subst(e, m))),2065 Nim::Array(n, e) => Nim::Array(n.clone(), Box::new(Self::subst(e, m))),
2011 Nim::Var(e) => Nim::Var(Box::new(Self::subst(e, m))),2066 Nim::Var(e) => Nim::Var(Box::new(Self::subst(e, m))),
2012 Nim::Tuple(ts) => Nim::Tuple(ts.iter().map(|x| Self::subst(x, m)).collect()),2067 Nim::Tuple(ts) => Nim::Tuple(ts.iter().map(|x| Self::subst(x, m)).collect()),
2068+ // A proc type carries parameters too: `F: FnOnce(&Self) -> R` is
2069+ // useless as an expectation if `Self` survives into it.
2070+ Nim::Proc(a, r) => Nim::Proc(
2071+ a.iter().map(|x| Self::subst(x, m)).collect(),
2072+ Box::new(Self::subst(r, m)),
2073+ ),
2074+ Nim::Ptr(e) => Nim::Ptr(Box::new(Self::subst(e, m))),
2075+ Nim::ConstPtr(e) => Nim::ConstPtr(Box::new(Self::subst(e, m))),
2076+ Nim::Iter(e) => Nim::Iter(Box::new(Self::subst(e, m))),
2077+ Nim::Future(e) => Nim::Future(Box::new(Self::subst(e, m))),
2013 other => other.clone(),2078 other => other.clone(),
2014 }2079 }
2015 }2080 }
@@ -2838,22 +2903,56 @@ impl Lowerer {
2838 Item::Macro(m) if path_name(&m.mac.path) == "macro_rules" => Ok(()),2903 Item::Macro(m) if path_name(&m.mac.path) == "macro_rules" => Ok(()),
2839 Item::Type(_) => Ok(()), // expanded at every use site2904 Item::Type(_) => Ok(()), // expanded at every use site
2840 Item::Trait(t) => {2905 Item::Trait(t) => {
2841- // We do not model trait resolution, so a declaration generates2906+ // A trait declaration generates nothing by itself. A *default
2842- // nothing and a use that needed it is rejected where it2907+ // body* is code, though, and it is code that applies to every
2843- // appears. A *default body*, though, is code: dropping it2908+ // implementor -- which is what a Nim generic proc is.
2844- // would silently remove a method the impls inherit.2909+ //
2910+ // Trait methods are already named `rs{Trait}_{method}` and
2911+ // overloaded on the receiver, so a default body emitted with
2912+ // `Self` as a type parameter is the fallback, and an `impl`
2913+ // that overrides it is a more specific overload that Nim
2914+ // prefers. That is Rust's rule, arrived at by Nim's.
2915+ let tname = t.ident.to_string();
2916+ let tgen = Self::generics_of(&t.generics);
2845 for it in &t.items {2917 for it in &t.items {
2846- if let syn::TraitItem::Fn(f) = it {2918+ let syn::TraitItem::Fn(f) = it else { continue };
2847- if f.default.is_some() {2919+ let Some(body) = &f.default else { continue };
2848- return Err(format!(2920+ let outer_g = std::mem::replace(&mut self.impl_generics, {
2849- "`trait {}` gives `{}` a default body; trait \2921+ let mut g = vec!["Self".to_string()];
2850- resolution is not modelled, so that body has no \2922+ g.extend(tgen.clone());
2851- impl to be emitted into and dropping it would \2923+ g
2852- remove code",2924+ });
2853- t.ident, f.sig.ident2925+ let outer_self =
2854- ));2926+ self.self_ty.replace(Nim::Named("Self".into(), Vec::new()));
2927+ // The method's own bounds: `fn apply<F: FnOnce(Self) -> Res>`
2928+ // is what says `f` can be called.
2929+ self.note_bounds(&f.sig.generics);
2930+ let nim = trait_method_name(
2931+ &tname,
2932+ &tname,
2933+ &f.sig.ident.to_string(),
2934+ takes_self(&f.sig),
2935+ );
2936+ let recv = takes_self(&f.sig)
2937+ .then(|| Nim::Named("Self".into(), Vec::new()));
2938+ // A return type that is a trait parameter appearing
2939+ // nowhere in the arguments cannot be inferred from a call.
2940+ // Nim's `auto` takes it from the body, which is where
2941+ // Rust's monomorphisation gets it too.
2942+ if let Ok((ptys, rt)) = self.signature(&f.sig) {
2943+ if let Nim::Named(g, a) = &rt {
2944+ if a.is_empty()
2945+ && g != "Self"
2946+ && !ptys.iter().any(|p| Self::mentions_any(p, &[g.clone()]))
2947+ {
2948+ self.ret_override = Some(Nim::Prim("auto".into()));
2949+ }
2855 }2950 }
2856 }2951 }
2952+ let r = self.func_named(&nim, &f.sig, body, recv);
2953+ self.self_ty = outer_self;
2954+ self.impl_generics = outer_g;
2955+ r?;
2857 }2956 }
2858 Ok(())2957 Ok(())
2859 }2958 }
@@ -3651,6 +3750,9 @@ impl Lowerer {
3651 self.opaque_hint = self.opaque_ret.get(&opaque_key(sig, body)).cloned();3750 self.opaque_hint = self.opaque_ret.get(&opaque_key(sig, body)).cloned();
3652 let (ptys, ret) = self.signature(sig)?;3751 let (ptys, ret) = self.signature(sig)?;
3653 self.opaque_hint = None;3752 self.opaque_hint = None;
3753+ let trim = self.ret_override.is_some();
3754+ let recv_ty = recv.clone();
3755+ let ret = self.ret_override.take().unwrap_or(ret);
3654 3756
3655 self.push_scope();3757 self.push_scope();
3656 let mut rendered: Vec<String> = Vec::new();3758 let mut rendered: Vec<String> = Vec::new();
@@ -3705,6 +3807,18 @@ impl Lowerer {
3705 gparams.push(g);3807 gparams.push(g);
3706 }3808 }
3707 }3809 }
3810+ // A parameter that appears in no argument cannot be bound by a call.
3811+ // With an `auto` return there is nothing else to bind it either, so it
3812+ // is not a parameter at all -- it was only ever the return type's name.
3813+ if trim {
3814+ // The receiver counts: `self: Self` is what binds `Self`, and it
3815+ // is not one of `ptys`.
3816+ let mut bound: Vec<Nim> = ptys.clone();
3817+ if let Some(r) = &recv_ty {
3818+ bound.push(r.clone());
3819+ }
3820+ gparams.retain(|g| bound.iter().any(|p| Self::mentions_any(p, &[g.clone()])));
3821+ }
3708 let gens = self.gen_list(&gparams);3822 let gens = self.gen_list(&gparams);
3709 let outer_fg = std::mem::replace(&mut self.fn_generics, gparams.clone());3823 let outer_fg = std::mem::replace(&mut self.fn_generics, gparams.clone());
3710 let head = if ret == Nim::Unit {3824 let head = if ret == Nim::Unit {
@@ -6938,6 +7052,68 @@ impl Lowerer {
6938 format!("(block:\n{}\n{pad}{})", copies.join("\n"), proc_code)7052 format!("(block:\n{}\n{pad}{})", copies.join("\n"), proc_code)
6939 }7053 }
6940 7054
7055+ /// A method the receiver's type does not declare, but a trait it
7056+ /// implements does -- an inherited default body.
7057+ ///
7058+ /// Those are emitted as generic procs under the trait's name, so the call
7059+ /// is the same one an overriding impl would answer; Nim picks the more
7060+ /// specific overload where there is one, which is Rust's rule.
7061+ fn inherited_default(
7062+ &mut self,
7063+ m: &syn::ExprMethodCall,
7064+ name: &str,
7065+ recv: &Val,
7066+ ) -> Result<Option<Val>, String> {
7067+ let Some(rt) = recv.ty.clone() else { return Ok(None) };
7068+ let rn = type_name(&rt.clone().unvar());
7069+ if self.fn_generics.iter().any(|g| *g == rn) {
7070+ return Ok(None);
7071+ }
7072+ if self.methods.contains_key(&(rn.clone(), name.to_string())) {
7073+ return Ok(None);
7074+ }
7075+ let trs = self.type_traits.get(&rn).cloned().unwrap_or_default();
7076+ let Some((tr, targs)) = trs
7077+ .iter()
7078+ .find(|(tr, _)| self.trait_methods.contains_key(&(tr.clone(), name.to_string())))
7079+ else {
7080+ return Ok(None);
7081+ };
7082+ let sig = self.trait_methods[&(tr.clone(), name.to_string())].clone();
7083+ // `Self` is the receiver's type here, and the trait's own parameters
7084+ // are whatever the `impl` supplied for them.
7085+ let mut sm: HashMap<String, Nim> = HashMap::new();
7086+ sm.insert("Self".into(), rt.clone().unvar());
7087+ for (p, a) in sig.generics.iter().zip(targs.iter()) {
7088+ sm.insert(p.clone(), a.clone());
7089+ }
7090+ let mut codes = vec![recv.code.clone()];
7091+ for (i, a) in m.args.iter().enumerate() {
7092+ let raw = sig.params.get(i + 1).cloned();
7093+ // A parameter that is a callable generic says what shape a
7094+ // closure written here has.
7095+ let want = match &raw {
7096+ Some(Nim::Named(g, _)) if self.fn_bounds.contains_key(g) => {
7097+ Some(Self::subst(&self.fn_bounds[g].clone(), &sm))
7098+ }
7099+ Some(t) if !Self::mentions_any(t, &sig.generics) && type_name(t) != "Self" => {
7100+ Some(t.clone())
7101+ }
7102+ _ => None,
7103+ };
7104+ codes.push(self.expr_at(a, want.as_ref())?.code);
7105+ }
7106+ let ret = Self::subst(&sig.ret, &sm);
7107+ Ok(Some(Val::new(
7108+ format!(
7109+ "{}({})",
7110+ trait_method_name(&rn, tr, name, true),
7111+ codes.join(", ")
7112+ ),
7113+ Some(ret),
7114+ )))
7115+ }
7116+
6941 /// `|x| x + 1` -> a Nim anonymous proc.7117 /// `|x| x + 1` -> a Nim anonymous proc.
6942 ///7118 ///
6943 /// Nim's closures capture by reference, as Rust's non-`move` closures do.7119 /// Nim's closures capture by reference, as Rust's non-`move` closures do.
@@ -7768,7 +7944,13 @@ impl Lowerer {
7768 // conversion, e.g. `String::from(..)`; handled by the method path.7944 // conversion, e.g. `String::from(..)`; handled by the method path.
7769 // Calling a proc-typed local, which is how an `impl Fn(..)` parameter7945 // Calling a proc-typed local, which is how an `impl Fn(..)` parameter
7770 // is invoked.7946 // is invoked.
7771- if let Some(Nim::Proc(ptys, ret)) = self.lookup(&name) {7947+ // A local whose type is a generic parameter bound by `Fn`: the bound
7948+ // says it is callable, and with what signature.
7949+ let callee_ty = match self.lookup(&name) {
7950+ Some(Nim::Named(g, _)) => self.fn_bounds.get(&g).cloned(),
7951+ other => other,
7952+ };
7953+ if let Some(Nim::Proc(ptys, ret)) = callee_ty {
7772 // Re-lowered against the proc's own parameter types: a literal7954 // Re-lowered against the proc's own parameter types: a literal
7773 // argument takes its width from them, and nothing earlier in this7955 // argument takes its width from them, and nothing earlier in this
7774 // function knew them.7956 // function knew them.
@@ -8073,6 +8255,14 @@ impl Lowerer {
8073 ("push", Some(Nim::Seq(t))) | ("push", Some(Nim::OpenArray(t))) => Some((**t).clone()),8255 ("push", Some(Nim::Seq(t))) | ("push", Some(Nim::OpenArray(t))) => Some((**t).clone()),
8074 (_, t) => t.clone(),8256 (_, t) => t.clone(),
8075 };8257 };
8258+ // A method the receiver's type does not declare, but a trait it
8259+ // implements does -- an inherited default body. Resolved before the
8260+ // arguments are lowered, because the trait's signature is what says
8261+ // their types: a closure argument has no shape without it.
8262+ if let Some(v) = self.inherited_default(m, &name, &recv)? {
8263+ return Ok(v);
8264+ }
8265+
8076 let mut args = Vec::new();8266 let mut args = Vec::new();
8077 for a in &m.args {8267 for a in &m.args {
8078 args.push(self.expr_at(a, arg_want.as_ref())?);8268 args.push(self.expr_at(a, arg_want.as_ref())?);
added tests/cases/071-trait-default-body.rs +57 -0
new file mode 100644
@@ -0,0 +1,57 @@
1+// A trait's default body is code that applies to every implementor, which is
2+// what a Nim generic proc is. An `impl` that overrides it is a more specific
3+// overload, which Nim prefers — Rust's rule, arrived at by Nim's.
4+trait Describe {
5+ fn name(&self) -> String;
6+
7+ // Default: used by anything that does not override it.
8+ fn describe(&self) -> String {
9+ format!("<{}>", self.name())
10+ }
11+
12+ fn shout(&self) -> String {
13+ format!("{}!", self.describe())
14+ }
15+}
16+
17+struct Dog;
18+struct Cat;
19+
20+impl Describe for Dog {
21+ fn name(&self) -> String {
22+ String::from("dog")
23+ }
24+}
25+
26+impl Describe for Cat {
27+ fn name(&self) -> String {
28+ String::from("cat")
29+ }
30+ // Overrides the default.
31+ fn describe(&self) -> String {
32+ String::from("[cat]")
33+ }
34+}
35+
36+// A default body that calls a function it was handed.
37+trait Apply<R> {
38+ fn apply<F: FnOnce(&Self) -> R>(&self, f: F) -> R {
39+ f(self)
40+ }
41+}
42+
43+struct Num {
44+ v: u32,
45+}
46+
47+impl Apply<u32> for Num {}
48+
49+fn main() {
50+ let d = Dog;
51+ let c = Cat;
52+ println!("{} {}", d.describe(), d.shout());
53+ println!("{} {}", c.describe(), c.shout());
54+
55+ let n = Num { v: 20 };
56+ println!("{}", n.apply(|x| x.v * 2));
57+}
new file mode 100644
@@ -0,0 +1,57 @@
1+// A trait's default body is code that applies to every implementor, which is
2+// what a Nim generic proc is. An `impl` that overrides it is a more specific
3+// overload, which Nim prefers — Rust's rule, arrived at by Nim's.
4+trait Describe {
5+ fn name(&self) -> String;
6+
7+ // Default: used by anything that does not override it.
8+ fn describe(&self) -> String {
9+ format!("<{}>", self.name())
10+ }
11+
12+ fn shout(&self) -> String {
13+ format!("{}!", self.describe())
14+ }
15+}
16+
17+struct Dog;
18+struct Cat;
19+
20+impl Describe for Dog {
21+ fn name(&self) -> String {
22+ String::from("dog")
23+ }
24+}
25+
26+impl Describe for Cat {
27+ fn name(&self) -> String {
28+ String::from("cat")
29+ }
30+ // Overrides the default.
31+ fn describe(&self) -> String {
32+ String::from("[cat]")
33+ }
34+}
35+
36+// A default body that calls a function it was handed.
37+trait Apply<R> {
38+ fn apply<F: FnOnce(&Self) -> R>(&self, f: F) -> R {
39+ f(self)
40+ }
41+}
42+
43+struct Num {
44+ v: u32,
45+}
46+
47+impl Apply<u32> for Num {}
48+
49+fn main() {
50+ let d = Dog;
51+ let c = Cat;
52+ println!("{} {}", d.describe(), d.shout());
53+ println!("{} {}", c.describe(), c.shout());
54+
55+ let n = Num { v: 20 };
56+ println!("{}", n.apply(|x| x.v * 2));
57+}