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

Carry a type alias's default parameters

`pub type Result<O, E = ContextError> = core::result::Result<O, E>` is how a
crate spells its own Result, and it is written `Result<O>` at most use sites.
That is an arity mismatch against the alias's two parameters, so it did not
expand at all -- and the unexpanded name then failed everything downstream
that knows what a `Result` is: `Ok(..)` was "not a known enum variant", and
`?` on it "not a supported combination".

Defaults now travel with the parameters and fill in the missing arguments.

tests/cases/064.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nandithebull committed 2026-09-19T20:38:21-07:00 Browse files
2cccbf7 parent: 1687aba
modified src/lower.rs +29 -6
@@ -312,7 +312,7 @@ pub struct Lowerer {
312312 /// Writes through it produce the proc's string result.
313313 fmt_param: Option<String>,
314314 /// `type X<T> = ...`, expanded before any type is mapped.
315- aliases: HashMap<String, (Vec<String>, syn::Type)>,
315+ aliases: HashMap<String, (Vec<(String, Option<syn::Type>)>, syn::Type)>,
316316 /// Whether anything in the input is a future, which decides if the async
317317 /// runtime is emitted.
318318 /// Whether the program spawns threads. Sharing a `ref` across threads
@@ -733,12 +733,18 @@ impl Lowerer {
733733 match item {
734734 Item::Use(u) => self.collect_use(&u.tree, &[]),
735735 Item::Type(t) => {
736- let params: Vec<String> = t
736+ // Defaults travel with the parameters: `type Result<O, E =
737+ // ContextError>` is written `Result<O>` at most use sites, and
738+ // without the default that is an arity mismatch and no
739+ // expansion at all.
740+ let params: Vec<(String, Option<syn::Type>)> = t
737741 .generics
738742 .params
739743 .iter()
740744 .filter_map(|g| match g {
741- syn::GenericParam::Type(t) => Some(t.ident.to_string()),
745+ syn::GenericParam::Type(t) => {
746+ Some((t.ident.to_string(), t.default.clone().map(|(_, d)| d)))
747+ }
742748 _ => None,
743749 })
744750 .collect();
@@ -986,12 +992,18 @@ impl Lowerer {
986992 }
987993 }
988994 Item::Type(t) => {
989- let params: Vec<String> = t
995+ // Defaults travel with the parameters: `type Result<O, E =
996+ // ContextError>` is written `Result<O>` at most use sites, and
997+ // without the default that is an arity mismatch and no
998+ // expansion at all.
999+ let params: Vec<(String, Option<syn::Type>)> = t
9901000 .generics
9911001 .params
9921002 .iter()
9931003 .filter_map(|g| match g {
994- syn::GenericParam::Type(t) => Some(t.ident.to_string()),
1004+ syn::GenericParam::Type(t) => {
1005+ Some((t.ident.to_string(), t.default.clone().map(|(_, d)| d)))
1006+ }
9951007 _ => None,
9961008 })
9971009 .collect();
@@ -1557,6 +1569,16 @@ impl Lowerer {
15571569 .collect(),
15581570 _ => vec![],
15591571 };
1572+ // A missing argument is supplied by the parameter's default, which is
1573+ // how `type Result<O, E = ContextError>` is written `Result<O>` almost
1574+ // everywhere it is used.
1575+ let mut args = args;
1576+ while args.len() < params.len() {
1577+ match &params[args.len()].1 {
1578+ Some(d) => args.push(d.clone()),
1579+ None => break,
1580+ }
1581+ }
15601582 if args.len() != params.len() {
15611583 // Flattening several files into one module can bring a crate's own
15621584 // alias (`type Result<T> = Result<T, Error>`) into scope at a site
@@ -1565,7 +1587,8 @@ impl Lowerer {
15651587 // neither is left for `ty::map` to report.
15661588 return Ok(t.clone());
15671589 }
1568- self.expand(&substitute(target, params, &args), depth + 1)
1590+ let names: Vec<String> = params.iter().map(|(n, _)| n.clone()).collect();
1591+ self.expand(&substitute(target, &names, &args), depth + 1)
15691592 }
15701593
15711594 /// The type parameters a generic item declares.
@@ -312,7 +312,7 @@ pub struct Lowerer {
312 /// Writes through it produce the proc's string result.312 /// Writes through it produce the proc's string result.
313 fmt_param: Option<String>,313 fmt_param: Option<String>,
314 /// `type X<T> = ...`, expanded before any type is mapped.314 /// `type X<T> = ...`, expanded before any type is mapped.
315- aliases: HashMap<String, (Vec<String>, syn::Type)>,315+ aliases: HashMap<String, (Vec<(String, Option<syn::Type>)>, syn::Type)>,
316 /// Whether anything in the input is a future, which decides if the async316 /// Whether anything in the input is a future, which decides if the async
317 /// runtime is emitted.317 /// runtime is emitted.
318 /// Whether the program spawns threads. Sharing a `ref` across threads318 /// Whether the program spawns threads. Sharing a `ref` across threads
@@ -733,12 +733,18 @@ impl Lowerer {
733 match item {733 match item {
734 Item::Use(u) => self.collect_use(&u.tree, &[]),734 Item::Use(u) => self.collect_use(&u.tree, &[]),
735 Item::Type(t) => {735 Item::Type(t) => {
736- let params: Vec<String> = t736+ // Defaults travel with the parameters: `type Result<O, E =
737+ // ContextError>` is written `Result<O>` at most use sites, and
738+ // without the default that is an arity mismatch and no
739+ // expansion at all.
740+ let params: Vec<(String, Option<syn::Type>)> = t
737 .generics741 .generics
738 .params742 .params
739 .iter()743 .iter()
740 .filter_map(|g| match g {744 .filter_map(|g| match g {
741- syn::GenericParam::Type(t) => Some(t.ident.to_string()),745+ syn::GenericParam::Type(t) => {
746+ Some((t.ident.to_string(), t.default.clone().map(|(_, d)| d)))
747+ }
742 _ => None,748 _ => None,
743 })749 })
744 .collect();750 .collect();
@@ -986,12 +992,18 @@ impl Lowerer {
986 }992 }
987 }993 }
988 Item::Type(t) => {994 Item::Type(t) => {
989- let params: Vec<String> = t995+ // Defaults travel with the parameters: `type Result<O, E =
996+ // ContextError>` is written `Result<O>` at most use sites, and
997+ // without the default that is an arity mismatch and no
998+ // expansion at all.
999+ let params: Vec<(String, Option<syn::Type>)> = t
990 .generics1000 .generics
991 .params1001 .params
992 .iter()1002 .iter()
993 .filter_map(|g| match g {1003 .filter_map(|g| match g {
994- syn::GenericParam::Type(t) => Some(t.ident.to_string()),1004+ syn::GenericParam::Type(t) => {
1005+ Some((t.ident.to_string(), t.default.clone().map(|(_, d)| d)))
1006+ }
995 _ => None,1007 _ => None,
996 })1008 })
997 .collect();1009 .collect();
@@ -1557,6 +1569,16 @@ impl Lowerer {
1557 .collect(),1569 .collect(),
1558 _ => vec![],1570 _ => vec![],
1559 };1571 };
1572+ // A missing argument is supplied by the parameter's default, which is
1573+ // how `type Result<O, E = ContextError>` is written `Result<O>` almost
1574+ // everywhere it is used.
1575+ let mut args = args;
1576+ while args.len() < params.len() {
1577+ match &params[args.len()].1 {
1578+ Some(d) => args.push(d.clone()),
1579+ None => break,
1580+ }
1581+ }
1560 if args.len() != params.len() {1582 if args.len() != params.len() {
1561 // Flattening several files into one module can bring a crate's own1583 // Flattening several files into one module can bring a crate's own
1562 // alias (`type Result<T> = Result<T, Error>`) into scope at a site1584 // alias (`type Result<T> = Result<T, Error>`) into scope at a site
@@ -1565,7 +1587,8 @@ impl Lowerer {
1565 // neither is left for `ty::map` to report.1587 // neither is left for `ty::map` to report.
1566 return Ok(t.clone());1588 return Ok(t.clone());
1567 }1589 }
1568- self.expand(&substitute(target, params, &args), depth + 1)1590+ let names: Vec<String> = params.iter().map(|(n, _)| n.clone()).collect();
1591+ self.expand(&substitute(target, &names, &args), depth + 1)
1569 }1592 }
1570 1593
1571 /// The type parameters a generic item declares.1594 /// The type parameters a generic item declares.
added tests/cases/064-alias-default-param.rs +33 -0
new file mode 100644
@@ -0,0 +1,33 @@
1+// A type alias with a default parameter. `type Result<T, E = MyError>` is how
2+// most crates spell their own Result, and it is written `Result<T>` almost
3+// everywhere it is used — which is an arity mismatch unless the default is
4+// carried along with the parameter.
5+#[derive(Debug)]
6+struct MyError {
7+ code: u32,
8+}
9+
10+type Res<T, E = MyError> = core::result::Result<T, E>;
11+
12+fn halve(n: u32) -> Res<u32> {
13+ if n % 2 == 0 {
14+ Ok(n / 2)
15+ } else {
16+ Err(MyError { code: n })
17+ }
18+}
19+
20+fn both(n: u32) -> Res<u32, MyError> {
21+ halve(n)
22+}
23+
24+fn main() {
25+ match halve(10) {
26+ Ok(v) => println!("ok {}", v),
27+ Err(e) => println!("err {}", e.code),
28+ }
29+ match both(7) {
30+ Ok(v) => println!("ok {}", v),
31+ Err(e) => println!("err {}", e.code),
32+ }
33+}
new file mode 100644
@@ -0,0 +1,33 @@
1+// A type alias with a default parameter. `type Result<T, E = MyError>` is how
2+// most crates spell their own Result, and it is written `Result<T>` almost
3+// everywhere it is used — which is an arity mismatch unless the default is
4+// carried along with the parameter.
5+#[derive(Debug)]
6+struct MyError {
7+ code: u32,
8+}
9+
10+type Res<T, E = MyError> = core::result::Result<T, E>;
11+
12+fn halve(n: u32) -> Res<u32> {
13+ if n % 2 == 0 {
14+ Ok(n / 2)
15+ } else {
16+ Err(MyError { code: n })
17+ }
18+}
19+
20+fn both(n: u32) -> Res<u32, MyError> {
21+ halve(n)
22+}
23+
24+fn main() {
25+ match halve(10) {
26+ Ok(v) => println!("ok {}", v),
27+ Err(e) => println!("err {}", e.code),
28+ }
29+ match both(7) {
30+ Ok(v) => println!("ok {}", v),
31+ Err(e) => println!("err {}", e.code),
32+ }
33+}