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

Infer a generic struct's parameters from its field values

`Quad { b, g, r, a }` with no annotation: Nim cannot infer an object's generic
parameters from a constructor's field values, so rustnim refused. But the
field values do say what they are -- unifying the declared field types against
the actual ones binds the parameters, which is where Rust reads them from too.

The expectation still wins when there is one. The refusal remains when neither
says, and now says that rather than blaming Nim.

tests/cases/069.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nandithebull committed 2026-09-20T10:03:43-07:00 Browse files
d6e6f38 parent: 1250a56
modified src/lower.rs +24 -5
@@ -5841,6 +5841,7 @@ impl Lowerer {
58415841 other => other.to_string(),
58425842 };
58435843 let mut parts = Vec::new();
5844+ let mut field_tys: Vec<(Nim, Nim)> = Vec::new();
58445845 for f in &s.fields {
58455846 let fname = match &f.member {
58465847 syn::Member::Named(n) => n.to_string(),
@@ -5852,6 +5853,9 @@ impl Lowerer {
58525853 .and_then(|fs| fs.iter().find(|(n, _)| *n == fname))
58535854 .map(|(_, t)| t.clone());
58545855 let v = self.expr_at(&f.expr, want.as_ref())?;
5856+ if let (Some(d), Some(a)) = (&want, &v.ty) {
5857+ field_tys.push((d.clone(), a.clone()));
5858+ }
58555859 parts.push(format!("{}: {}", ident(&fname), v.code));
58565860 }
58575861 // Nim cannot infer an object's generic parameters from a
@@ -5865,11 +5869,26 @@ impl Lowerer {
58655869 Nim::Named(name.clone(), a.clone())
58665870 }
58675871 _ => {
5868- return Err(format!(
5869- "`{name} {{ .. }}` is generic, and Nim cannot infer \
5870- its parameters from the field values; annotate the \
5871- binding or the return type"
5872- ))
5872+ // Failing an expectation, the field values say
5873+ // what the parameters are -- which is where Rust
5874+ // reads them from too.
5875+ let mut bound: HashMap<String, Nim> = HashMap::new();
5876+ for (d, a) in &field_tys {
5877+ Self::unify(d, a, &gp, &mut bound);
5878+ }
5879+ let args: Vec<Nim> = gp
5880+ .iter()
5881+ .map(|g| bound.get(g).cloned())
5882+ .collect::<Option<Vec<_>>>()
5883+ .ok_or_else(|| {
5884+ format!(
5885+ "`{name} {{ .. }}` is generic, and neither the \
5886+ expected type nor the field values say what \
5887+ its parameters are; annotate the binding or \
5888+ the return type"
5889+ )
5890+ })?;
5891+ Nim::Named(name.clone(), args)
58735892 }
58745893 }
58755894 };
@@ -5841,6 +5841,7 @@ impl Lowerer {
5841 other => other.to_string(),5841 other => other.to_string(),
5842 };5842 };
5843 let mut parts = Vec::new();5843 let mut parts = Vec::new();
5844+ let mut field_tys: Vec<(Nim, Nim)> = Vec::new();
5844 for f in &s.fields {5845 for f in &s.fields {
5845 let fname = match &f.member {5846 let fname = match &f.member {
5846 syn::Member::Named(n) => n.to_string(),5847 syn::Member::Named(n) => n.to_string(),
@@ -5852,6 +5853,9 @@ impl Lowerer {
5852 .and_then(|fs| fs.iter().find(|(n, _)| *n == fname))5853 .and_then(|fs| fs.iter().find(|(n, _)| *n == fname))
5853 .map(|(_, t)| t.clone());5854 .map(|(_, t)| t.clone());
5854 let v = self.expr_at(&f.expr, want.as_ref())?;5855 let v = self.expr_at(&f.expr, want.as_ref())?;
5856+ if let (Some(d), Some(a)) = (&want, &v.ty) {
5857+ field_tys.push((d.clone(), a.clone()));
5858+ }
5855 parts.push(format!("{}: {}", ident(&fname), v.code));5859 parts.push(format!("{}: {}", ident(&fname), v.code));
5856 }5860 }
5857 // Nim cannot infer an object's generic parameters from a5861 // Nim cannot infer an object's generic parameters from a
@@ -5865,11 +5869,26 @@ impl Lowerer {
5865 Nim::Named(name.clone(), a.clone())5869 Nim::Named(name.clone(), a.clone())
5866 }5870 }
5867 _ => {5871 _ => {
5868- return Err(format!(5872+ // Failing an expectation, the field values say
5869- "`{name} {{ .. }}` is generic, and Nim cannot infer \5873+ // what the parameters are -- which is where Rust
5870- its parameters from the field values; annotate the \5874+ // reads them from too.
5871- binding or the return type"5875+ let mut bound: HashMap<String, Nim> = HashMap::new();
5872- ))5876+ for (d, a) in &field_tys {
5877+ Self::unify(d, a, &gp, &mut bound);
5878+ }
5879+ let args: Vec<Nim> = gp
5880+ .iter()
5881+ .map(|g| bound.get(g).cloned())
5882+ .collect::<Option<Vec<_>>>()
5883+ .ok_or_else(|| {
5884+ format!(
5885+ "`{name} {{ .. }}` is generic, and neither the \
5886+ expected type nor the field values say what \
5887+ its parameters are; annotate the binding or \
5888+ the return type"
5889+ )
5890+ })?;
5891+ Nim::Named(name.clone(), args)
5873 }5892 }
5874 }5893 }
5875 };5894 };
added tests/cases/069-generic-struct-infer.rs +30 -0
new file mode 100644
@@ -0,0 +1,30 @@
1+// A generic struct literal with no annotation. Nim cannot infer an object's
2+// parameters from a constructor's field values, but the field values do say
3+// what they are — which is where Rust reads them from too.
4+struct Quad<T> {
5+ r: T,
6+ g: T,
7+ b: T,
8+}
9+
10+struct Pair<A, B> {
11+ left: A,
12+ right: B,
13+}
14+
15+fn sum(q: Quad<u32>) -> u32 {
16+ q.r + q.g + q.b
17+}
18+
19+fn main() {
20+ let r = 1u32;
21+ let g = 2u32;
22+ let b = 3u32;
23+ println!("{}", sum(Quad { r, g, b }));
24+
25+ let p = Pair { left: 7u8, right: String::from("x") };
26+ println!("{} {}", p.left, p.right);
27+
28+ let f = Quad { r: 1.5f64, g: 2.5, b: 3.0 };
29+ println!("{}", f.r + f.g + f.b);
30+}
new file mode 100644
@@ -0,0 +1,30 @@
1+// A generic struct literal with no annotation. Nim cannot infer an object's
2+// parameters from a constructor's field values, but the field values do say
3+// what they are — which is where Rust reads them from too.
4+struct Quad<T> {
5+ r: T,
6+ g: T,
7+ b: T,
8+}
9+
10+struct Pair<A, B> {
11+ left: A,
12+ right: B,
13+}
14+
15+fn sum(q: Quad<u32>) -> u32 {
16+ q.r + q.g + q.b
17+}
18+
19+fn main() {
20+ let r = 1u32;
21+ let g = 2u32;
22+ let b = 3u32;
23+ println!("{}", sum(Quad { r, g, b }));
24+
25+ let p = Pair { left: 7u8, right: String::from("x") };
26+ println!("{} {}", p.left, p.right);
27+
28+ let f = Quad { r: 1.5f64, g: 2.5, b: 3.0 };
29+ println!("{}", f.r + f.g + f.b);
30+}