Run libcosmic through --expand; it now halts on the founding rule, not a gap
cargo rustc -- -Zunpretty=expanded over the whole crate: 439 dependency crates built, 44,649 lines of expanded Rust with every macro resolved -- derives, println!, bitflags!, all of it. One file, no mod declarations, nothing left to guess about. rustnim needed two things to get through that. Destructuring parameters, which iced writes several times as `fn from((column, row): (u16, u16))`. The parameter is named here and taken apart at the top of the body, which is what Rust does. tests/cases/048 pins it, and two smaller bugs surfaced with it: a tuple literal did not take its element types from the expected tuple, so `Size::from((7, 8))` defaulted to i32 and matched no impl; and a From impl whose source is a tuple generated a proc name containing parens and commas, which is not a Nim identifier. Then u128, where it stops deliberately. 16 occurrences, from enumflags2's bit arithmetic and an iced DragId(pub u128). That is the founding rule refusing to truncate rather than a missing feature. DESIGN.md records what this does not mean as well as what it does: the crate is 44,649 lines of which rustnim has type-checked a prefix, and halting at line N says nothing about N+1 onward. One blocker at a time is the only honest reading. 54 differential cases, all green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
8f6401f parent: e7dbe75 modified
DESIGN.md +23 -0 | @@ -388,6 +388,29 @@ with 66 generic-parameter blockers, 20 trait objects, 51 `async` uses, 235 | ||
| 388 | 388 | not features the crate happens to use; they are its architecture. `libcosmic` |
| 389 | 389 | is a north star, not a next step. |
| 390 | 390 | |
| 391 | +### Measured a third time, with `--expand` | |
| 392 | + | |
| 393 | +`cargo rustc -- -Zunpretty=expanded` over the whole crate: 439 dependency | |
| 394 | +crates built, **44,649 lines** of expanded Rust with every macro already | |
| 395 | +resolved — derives, `println!`, `bitflags!`, the lot. One file, no `mod` | |
| 396 | +declarations, nothing left to guess about. | |
| 397 | + | |
| 398 | +rustnim on that took **two** blockers to get through: | |
| 399 | + | |
| 400 | +1. destructuring parameters — `fn from((column, row): (u16, u16))`, which | |
| 401 | + `iced` writes several times. Named here and taken apart at the top of the | |
| 402 | + body, as Rust does. Now `tests/cases/048`. | |
| 403 | +2. `u128` — and here it stops, **deliberately**. 16 occurrences, from | |
| 404 | + `enumflags2`'s bit arithmetic and an `iced` `DragId(pub u128)`. That is the | |
| 405 | + founding rule refusing to truncate, not a missing feature. | |
| 406 | + | |
| 407 | +So the expanded crate clears the language and halts at the project's own | |
| 408 | +first principle. That is a much more interesting place to stop than "generics | |
| 409 | +are not implemented yet", and it is worth saying plainly what it does *not* | |
| 410 | +mean: the crate is 44,649 lines of which rustnim has type-checked a prefix, | |
| 411 | +and stopping at line N tells you nothing about lines N+1 onward. It is one | |
| 412 | +blocker at a time, and that is the only honest way to read it. | |
| 413 | + | |
| 391 | 414 | ### Measured again, after the language features |
| 392 | 415 | |
| 393 | 416 | Re-run once generics, associated types, `impl Trait` in both positions, |
| @@ -388,6 +388,29 @@ with 66 generic-parameter blockers, 20 trait objects, 51 `async` uses, 235 | |||
| 388 | not features the crate happens to use; they are its architecture. `libcosmic` | 388 | not features the crate happens to use; they are its architecture. `libcosmic` |
| 389 | is a north star, not a next step. | 389 | is a north star, not a next step. |
| 390 | 390 | ||
| 391 | +### Measured a third time, with `--expand` | ||
| 392 | + | ||
| 393 | +`cargo rustc -- -Zunpretty=expanded` over the whole crate: 439 dependency | ||
| 394 | +crates built, **44,649 lines** of expanded Rust with every macro already | ||
| 395 | +resolved — derives, `println!`, `bitflags!`, the lot. One file, no `mod` | ||
| 396 | +declarations, nothing left to guess about. | ||
| 397 | + | ||
| 398 | +rustnim on that took **two** blockers to get through: | ||
| 399 | + | ||
| 400 | +1. destructuring parameters — `fn from((column, row): (u16, u16))`, which | ||
| 401 | + `iced` writes several times. Named here and taken apart at the top of the | ||
| 402 | + body, as Rust does. Now `tests/cases/048`. | ||
| 403 | +2. `u128` — and here it stops, **deliberately**. 16 occurrences, from | ||
| 404 | + `enumflags2`'s bit arithmetic and an `iced` `DragId(pub u128)`. That is the | ||
| 405 | + founding rule refusing to truncate, not a missing feature. | ||
| 406 | + | ||
| 407 | +So the expanded crate clears the language and halts at the project's own | ||
| 408 | +first principle. That is a much more interesting place to stop than "generics | ||
| 409 | +are not implemented yet", and it is worth saying plainly what it does *not* | ||
| 410 | +mean: the crate is 44,649 lines of which rustnim has type-checked a prefix, | ||
| 411 | +and stopping at line N tells you nothing about lines N+1 onward. It is one | ||
| 412 | +blocker at a time, and that is the only honest way to read it. | ||
| 413 | + | ||
| 391 | ### Measured again, after the language features | 414 | ### Measured again, after the language features |
| 392 | 415 | ||
| 393 | Re-run once generics, associated types, `impl Trait` in both positions, | 416 | Re-run once generics, associated types, `impl Trait` in both positions, |
modified
src/lower.rs +84 -9 | @@ -260,6 +260,8 @@ pub struct Lowerer { | ||
| 260 | 260 | fmt_impls: HashMap<(String, String), ()>, |
| 261 | 261 | /// `(from, to)` conversions declared by `impl From<A> for B`. |
| 262 | 262 | from_impls: HashMap<(String, String), String>, |
| 263 | + /// The full source type of each `From` impl, which its key only names. | |
| 264 | + from_types: HashMap<(String, String), Nim>, | |
| 263 | 265 | /// Operator traits implemented for a type, so `a += b` on a user type can |
| 264 | 266 | /// be dispatched to the impl rather than to Nim's built-in operator. |
| 265 | 267 | op_impls: HashMap<(String, String), ()>, |
| @@ -355,6 +357,7 @@ impl Lowerer { | ||
| 355 | 357 | methods: HashMap::new(), |
| 356 | 358 | fmt_impls: HashMap::new(), |
| 357 | 359 | from_impls: HashMap::new(), |
| 360 | + from_types: HashMap::new(), | |
| 358 | 361 | op_impls: HashMap::new(), |
| 359 | 362 | statics: HashMap::new(), |
| 360 | 363 | fmt_param: None, |
| @@ -797,8 +800,12 @@ impl Lowerer { | ||
| 797 | 800 | .first() |
| 798 | 801 | .ok_or("`fn from` takes one argument")? |
| 799 | 802 | .clone(); |
| 800 | - let name = format!("rsFrom{}{}", tyname, type_name(&src)); | |
| 803 | + // The source type may be a tuple, whose rendering has | |
| 804 | + // parens and commas in it; a Nim identifier may not. | |
| 805 | + let name = format!("rsFrom{}{}", mangle(&tyname), mangle(&type_name(&src))); | |
| 801 | 806 | self.forwards.push(self.head_of(&name, &m.sig, None)?); |
| 807 | + self.from_types | |
| 808 | + .insert((type_name(&src), tyname.clone()), src.clone()); | |
| 802 | 809 | self.from_impls |
| 803 | 810 | .insert((type_name(&src), tyname), name); |
| 804 | 811 | return Ok(()); |
| @@ -1297,9 +1304,10 @@ impl Lowerer { | ||
| 1297 | 1304 | let pname = match &*p.pat { |
| 1298 | 1305 | Pat::Ident(id) => id.ident.to_string(), |
| 1299 | 1306 | Pat::Wild(_) => format!("unused{}", parts.len()), |
| 1300 | - _ => return Err("only plain identifier parameters are supported".into()), | |
| 1307 | + // A destructuring parameter takes a name here and is taken | |
| 1308 | + // apart at the top of the body, which is what Rust does too. | |
| 1309 | + _ => format!("rsArg{}", i), | |
| 1301 | 1310 | }; |
| 1302 | - let _ = i; | |
| 1303 | 1311 | parts.push(format!("{}: {}", ident(&pname), t.render())); |
| 1304 | 1312 | } |
| 1305 | 1313 | Ok(if ret == Nim::Unit { |
| @@ -2233,14 +2241,20 @@ impl Lowerer { | ||
| 2233 | 2241 | _ => None, |
| 2234 | 2242 | }) |
| 2235 | 2243 | .collect(); |
| 2244 | + let mut destructures: Vec<(String, Pat, Nim)> = Vec::new(); | |
| 2236 | 2245 | for (p, t) in typed.iter().zip(ptys.iter()) { |
| 2237 | - let pname = match &*p.pat { | |
| 2238 | - Pat::Ident(i) => i.ident.to_string(), | |
| 2246 | + let (pname, destructure) = match &*p.pat { | |
| 2247 | + Pat::Ident(i) => (i.ident.to_string(), None), | |
| 2239 | 2248 | // `fn from(_: Error) -> ..` — the parameter is unused, but Nim |
| 2240 | 2249 | // still needs a name for it. |
| 2241 | - Pat::Wild(_) => format!("unused{}", rendered.len()), | |
| 2242 | - _ => return Err("only plain identifier parameters are supported".into()), | |
| 2250 | + Pat::Wild(_) => (format!("unused{}", rendered.len()), None), | |
| 2251 | + // `fn f((a, b): (i32, i32))` — named here, taken apart at the | |
| 2252 | + // top of the body, as Rust does. | |
| 2253 | + other => (format!("rsArg{}", rendered.len()), Some(other.clone())), | |
| 2243 | 2254 | }; |
| 2255 | + if let Some(d) = destructure { | |
| 2256 | + destructures.push((pname.clone(), d, t.clone())); | |
| 2257 | + } | |
| 2244 | 2258 | rendered.push(format!("{}: {}", ident(&pname), t.render())); |
| 2245 | 2259 | // Inside the body a `var T` parameter is used exactly like a `T`. |
| 2246 | 2260 | self.bind(&pname, t.clone().owned()); |
| @@ -2270,6 +2284,31 @@ impl Lowerer { | ||
| 2270 | 2284 | }; |
| 2271 | 2285 | self.line(&head); |
| 2272 | 2286 | self.indent += 1; |
| 2287 | + // Take a destructuring parameter apart before the body runs, which is | |
| 2288 | + // what Rust does with `fn f((a, b): (i32, i32))`. | |
| 2289 | + for (arg, pat, t) in &destructures { | |
| 2290 | + let elems = match pat { | |
| 2291 | + Pat::Tuple(tp) => tp.elems.clone(), | |
| 2292 | + _ => return Err("only tuple patterns are supported as parameters".into()), | |
| 2293 | + }; | |
| 2294 | + let tys = match t { | |
| 2295 | + Nim::Tuple(ts) if ts.len() == elems.len() => ts.clone(), | |
| 2296 | + _ => return Err("a destructuring parameter needs a tuple type".into()), | |
| 2297 | + }; | |
| 2298 | + for (i, (e, et)) in elems.iter().zip(tys).enumerate() { | |
| 2299 | + let n = match e { | |
| 2300 | + Pat::Ident(id) => id.ident.to_string(), | |
| 2301 | + Pat::Wild(_) => continue, | |
| 2302 | + _ => { | |
| 2303 | + return Err( | |
| 2304 | + "nested destructuring parameters are not implemented yet".into() | |
| 2305 | + ) | |
| 2306 | + } | |
| 2307 | + }; | |
| 2308 | + self.line(&format!("let {}: {} = {}[{}]", ident(&n), et.render(), arg, i)); | |
| 2309 | + self.bind(&n, et); | |
| 2310 | + } | |
| 2311 | + } | |
| 2273 | 2312 | let outer_ret = self.ret.replace(ret.clone()); |
| 2274 | 2313 | |
| 2275 | 2314 | // A Rust fn's trailing expression is its return value. Naming Nim's |
| @@ -4203,10 +4242,16 @@ impl Lowerer { | ||
| 4203 | 4242 | } |
| 4204 | 4243 | Expr::Tuple(t) if t.elems.is_empty() => Ok(Val::new("", Some(Nim::Unit))), |
| 4205 | 4244 | Expr::Tuple(t) => { |
| 4245 | + // Each element takes its type from the expected tuple, the | |
| 4246 | + // same way a `let` annotation types a scalar literal. | |
| 4247 | + let want: Option<&Vec<Nim>> = match expect { | |
| 4248 | + Some(Nim::Tuple(ts)) if ts.len() == t.elems.len() => Some(ts), | |
| 4249 | + _ => None, | |
| 4250 | + }; | |
| 4206 | 4251 | let mut parts = Vec::new(); |
| 4207 | 4252 | let mut tys = Vec::new(); |
| 4208 | - for e in &t.elems { | |
| 4209 | - let v = self.expr(e)?; | |
| 4253 | + for (i, e) in t.elems.iter().enumerate() { | |
| 4254 | + let v = self.expr_at(e, want.and_then(|w| w.get(i)))?; | |
| 4210 | 4255 | tys.push(v.ty.clone()); |
| 4211 | 4256 | parts.push(v.code); |
| 4212 | 4257 | } |
| @@ -5133,6 +5178,27 @@ impl Lowerer { | ||
| 5133 | 5178 | )); |
| 5134 | 5179 | } |
| 5135 | 5180 | } |
| 5181 | + // The argument's own type may not match yet -- a tuple literal | |
| 5182 | + // defaults its elements the way any literal does. If exactly | |
| 5183 | + // one `From` targets this type, its declared source says what | |
| 5184 | + // the argument should be. | |
| 5185 | + let cands: Vec<(String, String)> = self | |
| 5186 | + .from_impls | |
| 5187 | + .iter() | |
| 5188 | + .filter(|((_, to), _)| *to == q) | |
| 5189 | + .map(|((from, _), f)| (from.clone(), f.clone())) | |
| 5190 | + .collect(); | |
| 5191 | + if cands.len() == 1 { | |
| 5192 | + let want = self | |
| 5193 | + .from_types | |
| 5194 | + .get(&(cands[0].0.clone(), q.clone())) | |
| 5195 | + .cloned(); | |
| 5196 | + let v = self.expr_at(&c.args[0], want.as_ref())?; | |
| 5197 | + return Ok(Val::new( | |
| 5198 | + format!("{}({})", cands[0].1, v.code), | |
| 5199 | + Some(Nim::Named(q, vec![])), | |
| 5200 | + )); | |
| 5201 | + } | |
| 5136 | 5202 | } |
| 5137 | 5203 | } |
| 5138 | 5204 | |
| @@ -6425,6 +6491,15 @@ fn elem_of(t: &Option<Nim>) -> Option<Nim> { | ||
| 6425 | 6491 | } |
| 6426 | 6492 | } |
| 6427 | 6493 | |
| 6494 | +/// Reduce a rendered type to something usable inside an identifier. | |
| 6495 | +fn mangle(s: &str) -> String { | |
| 6496 | + s.chars() | |
| 6497 | + .map(|c| if c.is_alphanumeric() { c } else { '_' }) | |
| 6498 | + .collect::<String>() | |
| 6499 | + .trim_matches('_') | |
| 6500 | + .replace("__", "_") | |
| 6501 | +} | |
| 6502 | + | |
| 6428 | 6503 | /// The short name a Nim type is known by, for keying method tables. |
| 6429 | 6504 | fn type_name(t: &Nim) -> String { |
| 6430 | 6505 | match t { |
| @@ -260,6 +260,8 @@ pub struct Lowerer { | |||
| 260 | fmt_impls: HashMap<(String, String), ()>, | 260 | fmt_impls: HashMap<(String, String), ()>, |
| 261 | /// `(from, to)` conversions declared by `impl From<A> for B`. | 261 | /// `(from, to)` conversions declared by `impl From<A> for B`. |
| 262 | from_impls: HashMap<(String, String), String>, | 262 | from_impls: HashMap<(String, String), String>, |
| 263 | + /// The full source type of each `From` impl, which its key only names. | ||
| 264 | + from_types: HashMap<(String, String), Nim>, | ||
| 263 | /// Operator traits implemented for a type, so `a += b` on a user type can | 265 | /// Operator traits implemented for a type, so `a += b` on a user type can |
| 264 | /// be dispatched to the impl rather than to Nim's built-in operator. | 266 | /// be dispatched to the impl rather than to Nim's built-in operator. |
| 265 | op_impls: HashMap<(String, String), ()>, | 267 | op_impls: HashMap<(String, String), ()>, |
| @@ -355,6 +357,7 @@ impl Lowerer { | |||
| 355 | methods: HashMap::new(), | 357 | methods: HashMap::new(), |
| 356 | fmt_impls: HashMap::new(), | 358 | fmt_impls: HashMap::new(), |
| 357 | from_impls: HashMap::new(), | 359 | from_impls: HashMap::new(), |
| 360 | + from_types: HashMap::new(), | ||
| 358 | op_impls: HashMap::new(), | 361 | op_impls: HashMap::new(), |
| 359 | statics: HashMap::new(), | 362 | statics: HashMap::new(), |
| 360 | fmt_param: None, | 363 | fmt_param: None, |
| @@ -797,8 +800,12 @@ impl Lowerer { | |||
| 797 | .first() | 800 | .first() |
| 798 | .ok_or("`fn from` takes one argument")? | 801 | .ok_or("`fn from` takes one argument")? |
| 799 | .clone(); | 802 | .clone(); |
| 800 | - let name = format!("rsFrom{}{}", tyname, type_name(&src)); | 803 | + // The source type may be a tuple, whose rendering has |
| 804 | + // parens and commas in it; a Nim identifier may not. | ||
| 805 | + let name = format!("rsFrom{}{}", mangle(&tyname), mangle(&type_name(&src))); | ||
| 801 | self.forwards.push(self.head_of(&name, &m.sig, None)?); | 806 | self.forwards.push(self.head_of(&name, &m.sig, None)?); |
| 807 | + self.from_types | ||
| 808 | + .insert((type_name(&src), tyname.clone()), src.clone()); | ||
| 802 | self.from_impls | 809 | self.from_impls |
| 803 | .insert((type_name(&src), tyname), name); | 810 | .insert((type_name(&src), tyname), name); |
| 804 | return Ok(()); | 811 | return Ok(()); |
| @@ -1297,9 +1304,10 @@ impl Lowerer { | |||
| 1297 | let pname = match &*p.pat { | 1304 | let pname = match &*p.pat { |
| 1298 | Pat::Ident(id) => id.ident.to_string(), | 1305 | Pat::Ident(id) => id.ident.to_string(), |
| 1299 | Pat::Wild(_) => format!("unused{}", parts.len()), | 1306 | Pat::Wild(_) => format!("unused{}", parts.len()), |
| 1300 | - _ => return Err("only plain identifier parameters are supported".into()), | 1307 | + // A destructuring parameter takes a name here and is taken |
| 1308 | + // apart at the top of the body, which is what Rust does too. | ||
| 1309 | + _ => format!("rsArg{}", i), | ||
| 1301 | }; | 1310 | }; |
| 1302 | - let _ = i; | ||
| 1303 | parts.push(format!("{}: {}", ident(&pname), t.render())); | 1311 | parts.push(format!("{}: {}", ident(&pname), t.render())); |
| 1304 | } | 1312 | } |
| 1305 | Ok(if ret == Nim::Unit { | 1313 | Ok(if ret == Nim::Unit { |
| @@ -2233,14 +2241,20 @@ impl Lowerer { | |||
| 2233 | _ => None, | 2241 | _ => None, |
| 2234 | }) | 2242 | }) |
| 2235 | .collect(); | 2243 | .collect(); |
| 2244 | + let mut destructures: Vec<(String, Pat, Nim)> = Vec::new(); | ||
| 2236 | for (p, t) in typed.iter().zip(ptys.iter()) { | 2245 | for (p, t) in typed.iter().zip(ptys.iter()) { |
| 2237 | - let pname = match &*p.pat { | 2246 | + let (pname, destructure) = match &*p.pat { |
| 2238 | - Pat::Ident(i) => i.ident.to_string(), | 2247 | + Pat::Ident(i) => (i.ident.to_string(), None), |
| 2239 | // `fn from(_: Error) -> ..` — the parameter is unused, but Nim | 2248 | // `fn from(_: Error) -> ..` — the parameter is unused, but Nim |
| 2240 | // still needs a name for it. | 2249 | // still needs a name for it. |
| 2241 | - Pat::Wild(_) => format!("unused{}", rendered.len()), | 2250 | + Pat::Wild(_) => (format!("unused{}", rendered.len()), None), |
| 2242 | - _ => return Err("only plain identifier parameters are supported".into()), | 2251 | + // `fn f((a, b): (i32, i32))` — named here, taken apart at the |
| 2252 | + // top of the body, as Rust does. | ||
| 2253 | + other => (format!("rsArg{}", rendered.len()), Some(other.clone())), | ||
| 2243 | }; | 2254 | }; |
| 2255 | + if let Some(d) = destructure { | ||
| 2256 | + destructures.push((pname.clone(), d, t.clone())); | ||
| 2257 | + } | ||
| 2244 | rendered.push(format!("{}: {}", ident(&pname), t.render())); | 2258 | rendered.push(format!("{}: {}", ident(&pname), t.render())); |
| 2245 | // Inside the body a `var T` parameter is used exactly like a `T`. | 2259 | // Inside the body a `var T` parameter is used exactly like a `T`. |
| 2246 | self.bind(&pname, t.clone().owned()); | 2260 | self.bind(&pname, t.clone().owned()); |
| @@ -2270,6 +2284,31 @@ impl Lowerer { | |||
| 2270 | }; | 2284 | }; |
| 2271 | self.line(&head); | 2285 | self.line(&head); |
| 2272 | self.indent += 1; | 2286 | self.indent += 1; |
| 2287 | + // Take a destructuring parameter apart before the body runs, which is | ||
| 2288 | + // what Rust does with `fn f((a, b): (i32, i32))`. | ||
| 2289 | + for (arg, pat, t) in &destructures { | ||
| 2290 | + let elems = match pat { | ||
| 2291 | + Pat::Tuple(tp) => tp.elems.clone(), | ||
| 2292 | + _ => return Err("only tuple patterns are supported as parameters".into()), | ||
| 2293 | + }; | ||
| 2294 | + let tys = match t { | ||
| 2295 | + Nim::Tuple(ts) if ts.len() == elems.len() => ts.clone(), | ||
| 2296 | + _ => return Err("a destructuring parameter needs a tuple type".into()), | ||
| 2297 | + }; | ||
| 2298 | + for (i, (e, et)) in elems.iter().zip(tys).enumerate() { | ||
| 2299 | + let n = match e { | ||
| 2300 | + Pat::Ident(id) => id.ident.to_string(), | ||
| 2301 | + Pat::Wild(_) => continue, | ||
| 2302 | + _ => { | ||
| 2303 | + return Err( | ||
| 2304 | + "nested destructuring parameters are not implemented yet".into() | ||
| 2305 | + ) | ||
| 2306 | + } | ||
| 2307 | + }; | ||
| 2308 | + self.line(&format!("let {}: {} = {}[{}]", ident(&n), et.render(), arg, i)); | ||
| 2309 | + self.bind(&n, et); | ||
| 2310 | + } | ||
| 2311 | + } | ||
| 2273 | let outer_ret = self.ret.replace(ret.clone()); | 2312 | let outer_ret = self.ret.replace(ret.clone()); |
| 2274 | 2313 | ||
| 2275 | // A Rust fn's trailing expression is its return value. Naming Nim's | 2314 | // A Rust fn's trailing expression is its return value. Naming Nim's |
| @@ -4203,10 +4242,16 @@ impl Lowerer { | |||
| 4203 | } | 4242 | } |
| 4204 | Expr::Tuple(t) if t.elems.is_empty() => Ok(Val::new("", Some(Nim::Unit))), | 4243 | Expr::Tuple(t) if t.elems.is_empty() => Ok(Val::new("", Some(Nim::Unit))), |
| 4205 | Expr::Tuple(t) => { | 4244 | Expr::Tuple(t) => { |
| 4245 | + // Each element takes its type from the expected tuple, the | ||
| 4246 | + // same way a `let` annotation types a scalar literal. | ||
| 4247 | + let want: Option<&Vec<Nim>> = match expect { | ||
| 4248 | + Some(Nim::Tuple(ts)) if ts.len() == t.elems.len() => Some(ts), | ||
| 4249 | + _ => None, | ||
| 4250 | + }; | ||
| 4206 | let mut parts = Vec::new(); | 4251 | let mut parts = Vec::new(); |
| 4207 | let mut tys = Vec::new(); | 4252 | let mut tys = Vec::new(); |
| 4208 | - for e in &t.elems { | 4253 | + for (i, e) in t.elems.iter().enumerate() { |
| 4209 | - let v = self.expr(e)?; | 4254 | + let v = self.expr_at(e, want.and_then(|w| w.get(i)))?; |
| 4210 | tys.push(v.ty.clone()); | 4255 | tys.push(v.ty.clone()); |
| 4211 | parts.push(v.code); | 4256 | parts.push(v.code); |
| 4212 | } | 4257 | } |
| @@ -5133,6 +5178,27 @@ impl Lowerer { | |||
| 5133 | )); | 5178 | )); |
| 5134 | } | 5179 | } |
| 5135 | } | 5180 | } |
| 5181 | + // The argument's own type may not match yet -- a tuple literal | ||
| 5182 | + // defaults its elements the way any literal does. If exactly | ||
| 5183 | + // one `From` targets this type, its declared source says what | ||
| 5184 | + // the argument should be. | ||
| 5185 | + let cands: Vec<(String, String)> = self | ||
| 5186 | + .from_impls | ||
| 5187 | + .iter() | ||
| 5188 | + .filter(|((_, to), _)| *to == q) | ||
| 5189 | + .map(|((from, _), f)| (from.clone(), f.clone())) | ||
| 5190 | + .collect(); | ||
| 5191 | + if cands.len() == 1 { | ||
| 5192 | + let want = self | ||
| 5193 | + .from_types | ||
| 5194 | + .get(&(cands[0].0.clone(), q.clone())) | ||
| 5195 | + .cloned(); | ||
| 5196 | + let v = self.expr_at(&c.args[0], want.as_ref())?; | ||
| 5197 | + return Ok(Val::new( | ||
| 5198 | + format!("{}({})", cands[0].1, v.code), | ||
| 5199 | + Some(Nim::Named(q, vec![])), | ||
| 5200 | + )); | ||
| 5201 | + } | ||
| 5136 | } | 5202 | } |
| 5137 | } | 5203 | } |
| 5138 | 5204 | ||
| @@ -6425,6 +6491,15 @@ fn elem_of(t: &Option<Nim>) -> Option<Nim> { | |||
| 6425 | } | 6491 | } |
| 6426 | } | 6492 | } |
| 6427 | 6493 | ||
| 6494 | +/// Reduce a rendered type to something usable inside an identifier. | ||
| 6495 | +fn mangle(s: &str) -> String { | ||
| 6496 | + s.chars() | ||
| 6497 | + .map(|c| if c.is_alphanumeric() { c } else { '_' }) | ||
| 6498 | + .collect::<String>() | ||
| 6499 | + .trim_matches('_') | ||
| 6500 | + .replace("__", "_") | ||
| 6501 | +} | ||
| 6502 | + | ||
| 6428 | /// The short name a Nim type is known by, for keying method tables. | 6503 | /// The short name a Nim type is known by, for keying method tables. |
| 6429 | fn type_name(t: &Nim) -> String { | 6504 | fn type_name(t: &Nim) -> String { |
| 6430 | match t { | 6505 | match t { |
added
tests/cases/048-destructuring-params.rs +28 -0 | new file mode 100644 | ||
| @@ -0,0 +1,28 @@ | ||
| 1 | +// `fn f((a, b): (i32, i32))` names the parameter and takes it apart at the top | |
| 2 | +// of the body, which is what Rust does too. Found in libcosmic's expanded | |
| 3 | +// source, where `impl From<(u16, u16)>` is written that way. | |
| 4 | +struct Size { | |
| 5 | + w: u16, | |
| 6 | + h: u16, | |
| 7 | +} | |
| 8 | + | |
| 9 | +impl From<(u16, u16)> for Size { | |
| 10 | + fn from((w, h): (u16, u16)) -> Size { | |
| 11 | + Size { w, h } | |
| 12 | + } | |
| 13 | +} | |
| 14 | + | |
| 15 | +fn area((w, h): (i32, i32)) -> i32 { | |
| 16 | + w * h | |
| 17 | +} | |
| 18 | + | |
| 19 | +fn first((a, _): (i32, i32)) -> i32 { | |
| 20 | + a | |
| 21 | +} | |
| 22 | + | |
| 23 | +fn main() { | |
| 24 | + println!("{}", area((3, 4))); | |
| 25 | + println!("{}", first((9, 1))); | |
| 26 | + let s = Size::from((7, 8)); | |
| 27 | + println!("{} {}", s.w, s.h); | |
| 28 | +} | |
| new file mode 100644 | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | +// `fn f((a, b): (i32, i32))` names the parameter and takes it apart at the top | ||
| 2 | +// of the body, which is what Rust does too. Found in libcosmic's expanded | ||
| 3 | +// source, where `impl From<(u16, u16)>` is written that way. | ||
| 4 | +struct Size { | ||
| 5 | + w: u16, | ||
| 6 | + h: u16, | ||
| 7 | +} | ||
| 8 | + | ||
| 9 | +impl From<(u16, u16)> for Size { | ||
| 10 | + fn from((w, h): (u16, u16)) -> Size { | ||
| 11 | + Size { w, h } | ||
| 12 | + } | ||
| 13 | +} | ||
| 14 | + | ||
| 15 | +fn area((w, h): (i32, i32)) -> i32 { | ||
| 16 | + w * h | ||
| 17 | +} | ||
| 18 | + | ||
| 19 | +fn first((a, _): (i32, i32)) -> i32 { | ||
| 20 | + a | ||
| 21 | +} | ||
| 22 | + | ||
| 23 | +fn main() { | ||
| 24 | + println!("{}", area((3, 4))); | ||
| 25 | + println!("{}", first((9, 1))); | ||
| 26 | + let s = Size::from((7, 8)); | ||
| 27 | + println!("{} {}", s.w, s.h); | ||
| 28 | +} | ||