Lower nested argument-position `impl Trait`
`fn f(x: Option<impl Label>)` is the same desugaring as `fn f(x: impl Label)`
-- a generic parameter -- but rustnim only recognised the bare form, so the
nested one fell through to `ty::map` and was reported as a return-position
opaque type, which it is not. `iced` writes it.
`rewrite_apit` now walks a parameter type and replaces every `impl Trait`
without a direct mapping with a fresh parameter, in source order; both the
generics list and the signature go through it, so the name invented for a
given position always agrees. `impl Iterator`/`impl Fn`/`impl Future` are
untouched, since those have real Nim equivalents already.
Two call-site consequences, both fixed here:
* A parameter type mentioning the *callee's* type variables is useless as an
expectation -- `ImplT1` names nothing at the call site -- so it is no
longer handed down. `mentions_any` decides that.
* With the expectation gone, `Some(x)` falls back to the payload's own type,
and `None::<T>` reads its turbofish. Both are more accurate than what they
replace, not just sufficient here.
Also accepts `impl DoubleEndedIterator` / `impl ExactSizeIterator` in return
position: the forward half is exactly a Nim closure iterator, and the extra
capabilities are not approximated -- `next_back`, `rev` and `len` are simply
not implemented, so they are rejected at the call site like anything else.
tests/cases/050.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>2eee1c9 parent: abb295c modified
src/lower.rs +130 -31 | @@ -1197,6 +1197,31 @@ impl Lowerer { | ||
| 1197 | 1197 | } |
| 1198 | 1198 | } |
| 1199 | 1199 | |
| 1200 | + /// Whether a type mentions one of the *callee's* type parameters. | |
| 1201 | + /// | |
| 1202 | + /// Such a type is useless as an expectation at a call site: `T` names | |
| 1203 | + /// nothing there, and handing it down would annotate a construction with | |
| 1204 | + /// an unbound identifier. The argument is lowered without an expectation | |
| 1205 | + /// instead, and Nim infers the instantiation -- which is what it does for | |
| 1206 | + /// a generic call anyway. | |
| 1207 | + fn mentions_any(t: &Nim, names: &[String]) -> bool { | |
| 1208 | + if names.is_empty() { | |
| 1209 | + return false; | |
| 1210 | + } | |
| 1211 | + match t { | |
| 1212 | + Nim::Named(n, a) => { | |
| 1213 | + names.iter().any(|g| g == n) || a.iter().any(|x| Self::mentions_any(x, names)) | |
| 1214 | + } | |
| 1215 | + Nim::Seq(e) | Nim::OpenArray(e) | Nim::Var(e) | Nim::Array(_, e) | Nim::Ptr(e) | |
| 1216 | + | Nim::ConstPtr(e) | Nim::Iter(e) | Nim::Future(e) => Self::mentions_any(e, names), | |
| 1217 | + Nim::Tuple(ts) => ts.iter().any(|x| Self::mentions_any(x, names)), | |
| 1218 | + Nim::Proc(a, r) => { | |
| 1219 | + a.iter().any(|x| Self::mentions_any(x, names)) || Self::mentions_any(r, names) | |
| 1220 | + } | |
| 1221 | + _ => false, | |
| 1222 | + } | |
| 1223 | + } | |
| 1224 | + | |
| 1200 | 1225 | fn subst(t: &Nim, m: &HashMap<String, Nim>) -> Nim { |
| 1201 | 1226 | match t { |
| 1202 | 1227 | Nim::Named(n, a) if a.is_empty() => m.get(n).cloned().unwrap_or_else(|| t.clone()), |
| @@ -1357,15 +1382,62 @@ impl Lowerer { | ||
| 1357 | 1382 | /// bound is dropped like any other, so what is left is a fresh parameter. |
| 1358 | 1383 | /// In return position it is an opaque type instead, and has no equivalent. |
| 1359 | 1384 | fn impl_trait_params(&self, sig: &syn::Signature) -> Vec<String> { |
| 1360 | - let mut out = Vec::new(); | |
| 1385 | + let mut n = 0usize; | |
| 1361 | 1386 | for a in &sig.inputs { |
| 1362 | 1387 | if let FnArg::Typed(t) = a { |
| 1363 | - if matches!(&*t.ty, syn::Type::ImplTrait(_)) && self.map_ty(&t.ty).is_err() { | |
| 1364 | - out.push(format!("ImplT{}", out.len() + 1)); | |
| 1388 | + self.rewrite_apit(&t.ty, &mut n); | |
| 1389 | + } | |
| 1390 | + } | |
| 1391 | + (1..=n).map(|i| format!("ImplT{i}")).collect() | |
| 1392 | + } | |
| 1393 | + | |
| 1394 | + /// Replace every `impl Trait` in a parameter type that has no direct | |
| 1395 | + /// mapping with a fresh generic parameter, naming them in source order. | |
| 1396 | + /// | |
| 1397 | + /// The desugaring is not only for a *bare* `impl Trait` parameter: | |
| 1398 | + /// `Option<impl Into<X>>` is a generic parameter too, in exactly the same | |
| 1399 | + /// way, and `iced` writes it. Traits that do have a direct mapping -- | |
| 1400 | + /// `impl Iterator`, `impl Fn`, `impl Future` -- are left alone, since | |
| 1401 | + /// `map_ty` already produces the real Nim equivalent for those. | |
| 1402 | + /// | |
| 1403 | + /// Both this and `signature` walk with the same helper, so the parameter | |
| 1404 | + /// they invent for a given position always carries the same name. | |
| 1405 | + fn rewrite_apit(&self, ty: &syn::Type, next: &mut usize) -> syn::Type { | |
| 1406 | + use syn::Type; | |
| 1407 | + if let Type::ImplTrait(_) = ty { | |
| 1408 | + if self.map_ty(ty).is_err() { | |
| 1409 | + *next += 1; | |
| 1410 | + let name = format!("ImplT{next}"); | |
| 1411 | + return syn::parse_str::<Type>(&name).expect("generated ident parses"); | |
| 1412 | + } | |
| 1413 | + return ty.clone(); | |
| 1414 | + } | |
| 1415 | + let mut ty = ty.clone(); | |
| 1416 | + match &mut ty { | |
| 1417 | + Type::Reference(r) => *r.elem = self.rewrite_apit(&r.elem, next), | |
| 1418 | + Type::Slice(sl) => *sl.elem = self.rewrite_apit(&sl.elem, next), | |
| 1419 | + Type::Array(a) => *a.elem = self.rewrite_apit(&a.elem, next), | |
| 1420 | + Type::Paren(p) => *p.elem = self.rewrite_apit(&p.elem, next), | |
| 1421 | + Type::Group(g) => *g.elem = self.rewrite_apit(&g.elem, next), | |
| 1422 | + Type::Tuple(t) => { | |
| 1423 | + for e in t.elems.iter_mut() { | |
| 1424 | + *e = self.rewrite_apit(e, next); | |
| 1425 | + } | |
| 1426 | + } | |
| 1427 | + Type::Path(p) => { | |
| 1428 | + for seg in p.path.segments.iter_mut() { | |
| 1429 | + if let syn::PathArguments::AngleBracketed(a) = &mut seg.arguments { | |
| 1430 | + for g in a.args.iter_mut() { | |
| 1431 | + if let syn::GenericArgument::Type(t) = g { | |
| 1432 | + *t = self.rewrite_apit(t, next); | |
| 1433 | + } | |
| 1434 | + } | |
| 1435 | + } | |
| 1365 | 1436 | } |
| 1366 | 1437 | } |
| 1438 | + _ => {} | |
| 1367 | 1439 | } |
| 1368 | - out | |
| 1440 | + ty | |
| 1369 | 1441 | } |
| 1370 | 1442 | |
| 1371 | 1443 | fn signature(&self, sig: &syn::Signature) -> Result<(Vec<Nim>, Nim), String> { |
| @@ -1385,17 +1457,8 @@ impl Lowerer { | ||
| 1385 | 1457 | let mut fresh = 0usize; |
| 1386 | 1458 | for a in &sig.inputs { |
| 1387 | 1459 | if let FnArg::Typed(t) = a { |
| 1388 | - match self.map_ty(&t.ty) { | |
| 1389 | - Ok(n) => params.push(n), | |
| 1390 | - Err(e) => { | |
| 1391 | - if matches!(&*t.ty, syn::Type::ImplTrait(_)) { | |
| 1392 | - fresh += 1; | |
| 1393 | - params.push(Nim::Named(format!("ImplT{fresh}"), vec![])); | |
| 1394 | - } else { | |
| 1395 | - return Err(e); | |
| 1396 | - } | |
| 1397 | - } | |
| 1398 | - } | |
| 1460 | + let rewritten = self.rewrite_apit(&t.ty, &mut fresh); | |
| 1461 | + params.push(self.map_ty(&rewritten)?); | |
| 1399 | 1462 | } |
| 1400 | 1463 | } |
| 1401 | 1464 | let ret = match &sig.output { |
| @@ -3874,7 +3937,23 @@ impl Lowerer { | ||
| 3874 | 3937 | Expr::Path(p) => { |
| 3875 | 3938 | let name = path_name(&p.path); |
| 3876 | 3939 | if name == "None" { |
| 3877 | - return Ok(Val::new(self.none_of(expect), expect.cloned())); | |
| 3940 | + // `None::<T>` says the type itself, and is the only thing | |
| 3941 | + // that does when the expectation is a callee's type | |
| 3942 | + // variable rather than a type. | |
| 3943 | + let tf = p.path.segments.last().and_then(|seg| match &seg.arguments { | |
| 3944 | + syn::PathArguments::AngleBracketed(a) => { | |
| 3945 | + a.args.iter().find_map(|g| match g { | |
| 3946 | + syn::GenericArgument::Type(t) => self.map_ty(t).ok(), | |
| 3947 | + _ => None, | |
| 3948 | + }) | |
| 3949 | + } | |
| 3950 | + _ => None, | |
| 3951 | + }); | |
| 3952 | + let want = match tf { | |
| 3953 | + Some(t) => Some(Nim::Named("Option".into(), vec![t.owned()])), | |
| 3954 | + None => expect.cloned(), | |
| 3955 | + }; | |
| 3956 | + return Ok(Val::new(self.none_of(want.as_ref()), want)); | |
| 3878 | 3957 | } |
| 3879 | 3958 | // `Ordering::SeqCst` and friends. Nim's names differ but the |
| 3880 | 3959 | // set is the same, and both mean the C11 ordering. |
| @@ -5022,14 +5101,12 @@ impl Lowerer { | ||
| 5022 | 5101 | } |
| 5023 | 5102 | |
| 5024 | 5103 | let target = self.resolve_fn(&p.path); |
| 5025 | - let ptys: Vec<Nim> = target | |
| 5026 | - .as_ref() | |
| 5027 | - .and_then(|k| self.fns.get(k)) | |
| 5028 | - .map(|s| s.params.clone()) | |
| 5029 | - .unwrap_or_default(); | |
| 5104 | + let callee = target.as_ref().and_then(|k| self.fns.get(k)); | |
| 5105 | + let cgen: Vec<String> = callee.map(|s| s.generics.clone()).unwrap_or_default(); | |
| 5106 | + let ptys: Vec<Nim> = callee.map(|s| s.params.clone()).unwrap_or_default(); | |
| 5030 | 5107 | let mut args = Vec::new(); |
| 5031 | 5108 | for (i, a) in c.args.iter().enumerate() { |
| 5032 | - let want = ptys.get(i).cloned(); | |
| 5109 | + let want = ptys.get(i).cloned().filter(|t| !Self::mentions_any(t, &cgen)); | |
| 5033 | 5110 | args.push(self.expr_at(a, want.as_ref())?); |
| 5034 | 5111 | } |
| 5035 | 5112 | let codes: Vec<String> = args.iter().map(|a| a.code.clone()).collect(); |
| @@ -5039,17 +5116,31 @@ impl Lowerer { | ||
| 5039 | 5116 | // being constructed: Nim cannot infer `E` from an `Ok(v)` alone. |
| 5040 | 5117 | match name.as_str() { |
| 5041 | 5118 | "Some" => { |
| 5042 | - let inner = match expect { | |
| 5043 | - Some(Nim::Named(n, a)) if n == "Option" && a.len() == 1 => a[0].render(), | |
| 5044 | - _ => { | |
| 5045 | - return Err("`Some(..)` needs a known `Option<T>` type here; \ | |
| 5046 | - annotate the binding or the return type" | |
| 5047 | - .into()) | |
| 5119 | + // The expectation names `T` when there is one. Failing that, | |
| 5120 | + // the payload's own type does: `Some(x)` is an `Option<T>` for | |
| 5121 | + // whatever `x` is. That second route is what makes | |
| 5122 | + // `f(Some(Tag(1)))` work when `f` takes `Option<impl Trait>` -- | |
| 5123 | + // there the parameter type is the callee's type variable, | |
| 5124 | + // which names nothing at the call site. | |
| 5125 | + let (inner, ty) = match expect { | |
| 5126 | + Some(Nim::Named(n, a)) if n == "Option" && a.len() == 1 => { | |
| 5127 | + (a[0].render(), expect.cloned()) | |
| 5048 | 5128 | } |
| 5129 | + _ => match args.first().and_then(|a| a.ty.clone()) { | |
| 5130 | + Some(t) => { | |
| 5131 | + let t = t.owned(); | |
| 5132 | + (t.render(), Some(Nim::Named("Option".into(), vec![t]))) | |
| 5133 | + } | |
| 5134 | + None => { | |
| 5135 | + return Err("`Some(..)` needs a known `Option<T>` type here; \ | |
| 5136 | + annotate the binding or the return type" | |
| 5137 | + .into()) | |
| 5138 | + } | |
| 5139 | + }, | |
| 5049 | 5140 | }; |
| 5050 | 5141 | return Ok(Val::new( |
| 5051 | 5142 | format!("rsSome[{}]({})", inner, codes.join(", ")), |
| 5052 | - expect.cloned(), | |
| 5143 | + ty, | |
| 5053 | 5144 | )); |
| 5054 | 5145 | } |
| 5055 | 5146 | "Ok" if self.fmt_param.is_some() |
| @@ -5371,8 +5462,9 @@ impl Lowerer { | ||
| 5371 | 5462 | let declared = sig.params.clone(); |
| 5372 | 5463 | let mut args = args.clone(); |
| 5373 | 5464 | let mut codes = codes.clone(); |
| 5465 | + let cgen = sig.generics.clone(); | |
| 5374 | 5466 | for (i, a) in c.args.iter().enumerate() { |
| 5375 | - if let Some(want) = declared.get(i) { | |
| 5467 | + if let Some(want) = declared.get(i).filter(|t| !Self::mentions_any(t, &cgen)) { | |
| 5376 | 5468 | let want = want.clone().unvar(); |
| 5377 | 5469 | args[i] = self.expr_at(a, Some(&want))?; |
| 5378 | 5470 | codes[i] = args[i].code.clone(); |
| @@ -6034,11 +6126,18 @@ impl Lowerer { | ||
| 6034 | 6126 | .as_ref() |
| 6035 | 6127 | .and_then(|k| self.methods.get(k)) |
| 6036 | 6128 | .map(|s| s.params.clone()); |
| 6129 | + let cgen: Vec<String> = key | |
| 6130 | + .as_ref() | |
| 6131 | + .and_then(|k| self.methods.get(k)) | |
| 6132 | + .map(|s| s.generics.clone()) | |
| 6133 | + .unwrap_or_default(); | |
| 6037 | 6134 | if let Some(d) = &declared { |
| 6038 | 6135 | // params[0] is the receiver for a method with `self`. |
| 6039 | 6136 | let skip = usize::from(d.len() == m.args.len() + 1); |
| 6040 | 6137 | for (i, a) in m.args.iter().enumerate() { |
| 6041 | - if let Some(want) = d.get(i + skip) { | |
| 6138 | + if let Some(want) = | |
| 6139 | + d.get(i + skip).filter(|t| !Self::mentions_any(t, &cgen)) | |
| 6140 | + { | |
| 6042 | 6141 | let want = want.clone().unvar(); |
| 6043 | 6142 | args[i] = self.expr_at(a, Some(&want))?; |
| 6044 | 6143 | } |
| @@ -1197,6 +1197,31 @@ impl Lowerer { | |||
| 1197 | } | 1197 | } |
| 1198 | } | 1198 | } |
| 1199 | 1199 | ||
| 1200 | + /// Whether a type mentions one of the *callee's* type parameters. | ||
| 1201 | + /// | ||
| 1202 | + /// Such a type is useless as an expectation at a call site: `T` names | ||
| 1203 | + /// nothing there, and handing it down would annotate a construction with | ||
| 1204 | + /// an unbound identifier. The argument is lowered without an expectation | ||
| 1205 | + /// instead, and Nim infers the instantiation -- which is what it does for | ||
| 1206 | + /// a generic call anyway. | ||
| 1207 | + fn mentions_any(t: &Nim, names: &[String]) -> bool { | ||
| 1208 | + if names.is_empty() { | ||
| 1209 | + return false; | ||
| 1210 | + } | ||
| 1211 | + match t { | ||
| 1212 | + Nim::Named(n, a) => { | ||
| 1213 | + names.iter().any(|g| g == n) || a.iter().any(|x| Self::mentions_any(x, names)) | ||
| 1214 | + } | ||
| 1215 | + Nim::Seq(e) | Nim::OpenArray(e) | Nim::Var(e) | Nim::Array(_, e) | Nim::Ptr(e) | ||
| 1216 | + | Nim::ConstPtr(e) | Nim::Iter(e) | Nim::Future(e) => Self::mentions_any(e, names), | ||
| 1217 | + Nim::Tuple(ts) => ts.iter().any(|x| Self::mentions_any(x, names)), | ||
| 1218 | + Nim::Proc(a, r) => { | ||
| 1219 | + a.iter().any(|x| Self::mentions_any(x, names)) || Self::mentions_any(r, names) | ||
| 1220 | + } | ||
| 1221 | + _ => false, | ||
| 1222 | + } | ||
| 1223 | + } | ||
| 1224 | + | ||
| 1200 | fn subst(t: &Nim, m: &HashMap<String, Nim>) -> Nim { | 1225 | fn subst(t: &Nim, m: &HashMap<String, Nim>) -> Nim { |
| 1201 | match t { | 1226 | match t { |
| 1202 | Nim::Named(n, a) if a.is_empty() => m.get(n).cloned().unwrap_or_else(|| t.clone()), | 1227 | Nim::Named(n, a) if a.is_empty() => m.get(n).cloned().unwrap_or_else(|| t.clone()), |
| @@ -1357,15 +1382,62 @@ impl Lowerer { | |||
| 1357 | /// bound is dropped like any other, so what is left is a fresh parameter. | 1382 | /// bound is dropped like any other, so what is left is a fresh parameter. |
| 1358 | /// In return position it is an opaque type instead, and has no equivalent. | 1383 | /// In return position it is an opaque type instead, and has no equivalent. |
| 1359 | fn impl_trait_params(&self, sig: &syn::Signature) -> Vec<String> { | 1384 | fn impl_trait_params(&self, sig: &syn::Signature) -> Vec<String> { |
| 1360 | - let mut out = Vec::new(); | 1385 | + let mut n = 0usize; |
| 1361 | for a in &sig.inputs { | 1386 | for a in &sig.inputs { |
| 1362 | if let FnArg::Typed(t) = a { | 1387 | if let FnArg::Typed(t) = a { |
| 1363 | - if matches!(&*t.ty, syn::Type::ImplTrait(_)) && self.map_ty(&t.ty).is_err() { | 1388 | + self.rewrite_apit(&t.ty, &mut n); |
| 1364 | - out.push(format!("ImplT{}", out.len() + 1)); | 1389 | + } |
| 1390 | + } | ||
| 1391 | + (1..=n).map(|i| format!("ImplT{i}")).collect() | ||
| 1392 | + } | ||
| 1393 | + | ||
| 1394 | + /// Replace every `impl Trait` in a parameter type that has no direct | ||
| 1395 | + /// mapping with a fresh generic parameter, naming them in source order. | ||
| 1396 | + /// | ||
| 1397 | + /// The desugaring is not only for a *bare* `impl Trait` parameter: | ||
| 1398 | + /// `Option<impl Into<X>>` is a generic parameter too, in exactly the same | ||
| 1399 | + /// way, and `iced` writes it. Traits that do have a direct mapping -- | ||
| 1400 | + /// `impl Iterator`, `impl Fn`, `impl Future` -- are left alone, since | ||
| 1401 | + /// `map_ty` already produces the real Nim equivalent for those. | ||
| 1402 | + /// | ||
| 1403 | + /// Both this and `signature` walk with the same helper, so the parameter | ||
| 1404 | + /// they invent for a given position always carries the same name. | ||
| 1405 | + fn rewrite_apit(&self, ty: &syn::Type, next: &mut usize) -> syn::Type { | ||
| 1406 | + use syn::Type; | ||
| 1407 | + if let Type::ImplTrait(_) = ty { | ||
| 1408 | + if self.map_ty(ty).is_err() { | ||
| 1409 | + *next += 1; | ||
| 1410 | + let name = format!("ImplT{next}"); | ||
| 1411 | + return syn::parse_str::<Type>(&name).expect("generated ident parses"); | ||
| 1412 | + } | ||
| 1413 | + return ty.clone(); | ||
| 1414 | + } | ||
| 1415 | + let mut ty = ty.clone(); | ||
| 1416 | + match &mut ty { | ||
| 1417 | + Type::Reference(r) => *r.elem = self.rewrite_apit(&r.elem, next), | ||
| 1418 | + Type::Slice(sl) => *sl.elem = self.rewrite_apit(&sl.elem, next), | ||
| 1419 | + Type::Array(a) => *a.elem = self.rewrite_apit(&a.elem, next), | ||
| 1420 | + Type::Paren(p) => *p.elem = self.rewrite_apit(&p.elem, next), | ||
| 1421 | + Type::Group(g) => *g.elem = self.rewrite_apit(&g.elem, next), | ||
| 1422 | + Type::Tuple(t) => { | ||
| 1423 | + for e in t.elems.iter_mut() { | ||
| 1424 | + *e = self.rewrite_apit(e, next); | ||
| 1425 | + } | ||
| 1426 | + } | ||
| 1427 | + Type::Path(p) => { | ||
| 1428 | + for seg in p.path.segments.iter_mut() { | ||
| 1429 | + if let syn::PathArguments::AngleBracketed(a) = &mut seg.arguments { | ||
| 1430 | + for g in a.args.iter_mut() { | ||
| 1431 | + if let syn::GenericArgument::Type(t) = g { | ||
| 1432 | + *t = self.rewrite_apit(t, next); | ||
| 1433 | + } | ||
| 1434 | + } | ||
| 1435 | + } | ||
| 1365 | } | 1436 | } |
| 1366 | } | 1437 | } |
| 1438 | + _ => {} | ||
| 1367 | } | 1439 | } |
| 1368 | - out | 1440 | + ty |
| 1369 | } | 1441 | } |
| 1370 | 1442 | ||
| 1371 | fn signature(&self, sig: &syn::Signature) -> Result<(Vec<Nim>, Nim), String> { | 1443 | fn signature(&self, sig: &syn::Signature) -> Result<(Vec<Nim>, Nim), String> { |
| @@ -1385,17 +1457,8 @@ impl Lowerer { | |||
| 1385 | let mut fresh = 0usize; | 1457 | let mut fresh = 0usize; |
| 1386 | for a in &sig.inputs { | 1458 | for a in &sig.inputs { |
| 1387 | if let FnArg::Typed(t) = a { | 1459 | if let FnArg::Typed(t) = a { |
| 1388 | - match self.map_ty(&t.ty) { | 1460 | + let rewritten = self.rewrite_apit(&t.ty, &mut fresh); |
| 1389 | - Ok(n) => params.push(n), | 1461 | + params.push(self.map_ty(&rewritten)?); |
| 1390 | - Err(e) => { | ||
| 1391 | - if matches!(&*t.ty, syn::Type::ImplTrait(_)) { | ||
| 1392 | - fresh += 1; | ||
| 1393 | - params.push(Nim::Named(format!("ImplT{fresh}"), vec![])); | ||
| 1394 | - } else { | ||
| 1395 | - return Err(e); | ||
| 1396 | - } | ||
| 1397 | - } | ||
| 1398 | - } | ||
| 1399 | } | 1462 | } |
| 1400 | } | 1463 | } |
| 1401 | let ret = match &sig.output { | 1464 | let ret = match &sig.output { |
| @@ -3874,7 +3937,23 @@ impl Lowerer { | |||
| 3874 | Expr::Path(p) => { | 3937 | Expr::Path(p) => { |
| 3875 | let name = path_name(&p.path); | 3938 | let name = path_name(&p.path); |
| 3876 | if name == "None" { | 3939 | if name == "None" { |
| 3877 | - return Ok(Val::new(self.none_of(expect), expect.cloned())); | 3940 | + // `None::<T>` says the type itself, and is the only thing |
| 3941 | + // that does when the expectation is a callee's type | ||
| 3942 | + // variable rather than a type. | ||
| 3943 | + let tf = p.path.segments.last().and_then(|seg| match &seg.arguments { | ||
| 3944 | + syn::PathArguments::AngleBracketed(a) => { | ||
| 3945 | + a.args.iter().find_map(|g| match g { | ||
| 3946 | + syn::GenericArgument::Type(t) => self.map_ty(t).ok(), | ||
| 3947 | + _ => None, | ||
| 3948 | + }) | ||
| 3949 | + } | ||
| 3950 | + _ => None, | ||
| 3951 | + }); | ||
| 3952 | + let want = match tf { | ||
| 3953 | + Some(t) => Some(Nim::Named("Option".into(), vec![t.owned()])), | ||
| 3954 | + None => expect.cloned(), | ||
| 3955 | + }; | ||
| 3956 | + return Ok(Val::new(self.none_of(want.as_ref()), want)); | ||
| 3878 | } | 3957 | } |
| 3879 | // `Ordering::SeqCst` and friends. Nim's names differ but the | 3958 | // `Ordering::SeqCst` and friends. Nim's names differ but the |
| 3880 | // set is the same, and both mean the C11 ordering. | 3959 | // set is the same, and both mean the C11 ordering. |
| @@ -5022,14 +5101,12 @@ impl Lowerer { | |||
| 5022 | } | 5101 | } |
| 5023 | 5102 | ||
| 5024 | let target = self.resolve_fn(&p.path); | 5103 | let target = self.resolve_fn(&p.path); |
| 5025 | - let ptys: Vec<Nim> = target | 5104 | + let callee = target.as_ref().and_then(|k| self.fns.get(k)); |
| 5026 | - .as_ref() | 5105 | + let cgen: Vec<String> = callee.map(|s| s.generics.clone()).unwrap_or_default(); |
| 5027 | - .and_then(|k| self.fns.get(k)) | 5106 | + let ptys: Vec<Nim> = callee.map(|s| s.params.clone()).unwrap_or_default(); |
| 5028 | - .map(|s| s.params.clone()) | ||
| 5029 | - .unwrap_or_default(); | ||
| 5030 | let mut args = Vec::new(); | 5107 | let mut args = Vec::new(); |
| 5031 | for (i, a) in c.args.iter().enumerate() { | 5108 | for (i, a) in c.args.iter().enumerate() { |
| 5032 | - let want = ptys.get(i).cloned(); | 5109 | + let want = ptys.get(i).cloned().filter(|t| !Self::mentions_any(t, &cgen)); |
| 5033 | args.push(self.expr_at(a, want.as_ref())?); | 5110 | args.push(self.expr_at(a, want.as_ref())?); |
| 5034 | } | 5111 | } |
| 5035 | let codes: Vec<String> = args.iter().map(|a| a.code.clone()).collect(); | 5112 | let codes: Vec<String> = args.iter().map(|a| a.code.clone()).collect(); |
| @@ -5039,17 +5116,31 @@ impl Lowerer { | |||
| 5039 | // being constructed: Nim cannot infer `E` from an `Ok(v)` alone. | 5116 | // being constructed: Nim cannot infer `E` from an `Ok(v)` alone. |
| 5040 | match name.as_str() { | 5117 | match name.as_str() { |
| 5041 | "Some" => { | 5118 | "Some" => { |
| 5042 | - let inner = match expect { | 5119 | + // The expectation names `T` when there is one. Failing that, |
| 5043 | - Some(Nim::Named(n, a)) if n == "Option" && a.len() == 1 => a[0].render(), | 5120 | + // the payload's own type does: `Some(x)` is an `Option<T>` for |
| 5044 | - _ => { | 5121 | + // whatever `x` is. That second route is what makes |
| 5045 | - return Err("`Some(..)` needs a known `Option<T>` type here; \ | 5122 | + // `f(Some(Tag(1)))` work when `f` takes `Option<impl Trait>` -- |
| 5046 | - annotate the binding or the return type" | 5123 | + // there the parameter type is the callee's type variable, |
| 5047 | - .into()) | 5124 | + // which names nothing at the call site. |
| 5125 | + let (inner, ty) = match expect { | ||
| 5126 | + Some(Nim::Named(n, a)) if n == "Option" && a.len() == 1 => { | ||
| 5127 | + (a[0].render(), expect.cloned()) | ||
| 5048 | } | 5128 | } |
| 5129 | + _ => match args.first().and_then(|a| a.ty.clone()) { | ||
| 5130 | + Some(t) => { | ||
| 5131 | + let t = t.owned(); | ||
| 5132 | + (t.render(), Some(Nim::Named("Option".into(), vec![t]))) | ||
| 5133 | + } | ||
| 5134 | + None => { | ||
| 5135 | + return Err("`Some(..)` needs a known `Option<T>` type here; \ | ||
| 5136 | + annotate the binding or the return type" | ||
| 5137 | + .into()) | ||
| 5138 | + } | ||
| 5139 | + }, | ||
| 5049 | }; | 5140 | }; |
| 5050 | return Ok(Val::new( | 5141 | return Ok(Val::new( |
| 5051 | format!("rsSome[{}]({})", inner, codes.join(", ")), | 5142 | format!("rsSome[{}]({})", inner, codes.join(", ")), |
| 5052 | - expect.cloned(), | 5143 | + ty, |
| 5053 | )); | 5144 | )); |
| 5054 | } | 5145 | } |
| 5055 | "Ok" if self.fmt_param.is_some() | 5146 | "Ok" if self.fmt_param.is_some() |
| @@ -5371,8 +5462,9 @@ impl Lowerer { | |||
| 5371 | let declared = sig.params.clone(); | 5462 | let declared = sig.params.clone(); |
| 5372 | let mut args = args.clone(); | 5463 | let mut args = args.clone(); |
| 5373 | let mut codes = codes.clone(); | 5464 | let mut codes = codes.clone(); |
| 5465 | + let cgen = sig.generics.clone(); | ||
| 5374 | for (i, a) in c.args.iter().enumerate() { | 5466 | for (i, a) in c.args.iter().enumerate() { |
| 5375 | - if let Some(want) = declared.get(i) { | 5467 | + if let Some(want) = declared.get(i).filter(|t| !Self::mentions_any(t, &cgen)) { |
| 5376 | let want = want.clone().unvar(); | 5468 | let want = want.clone().unvar(); |
| 5377 | args[i] = self.expr_at(a, Some(&want))?; | 5469 | args[i] = self.expr_at(a, Some(&want))?; |
| 5378 | codes[i] = args[i].code.clone(); | 5470 | codes[i] = args[i].code.clone(); |
| @@ -6034,11 +6126,18 @@ impl Lowerer { | |||
| 6034 | .as_ref() | 6126 | .as_ref() |
| 6035 | .and_then(|k| self.methods.get(k)) | 6127 | .and_then(|k| self.methods.get(k)) |
| 6036 | .map(|s| s.params.clone()); | 6128 | .map(|s| s.params.clone()); |
| 6129 | + let cgen: Vec<String> = key | ||
| 6130 | + .as_ref() | ||
| 6131 | + .and_then(|k| self.methods.get(k)) | ||
| 6132 | + .map(|s| s.generics.clone()) | ||
| 6133 | + .unwrap_or_default(); | ||
| 6037 | if let Some(d) = &declared { | 6134 | if let Some(d) = &declared { |
| 6038 | // params[0] is the receiver for a method with `self`. | 6135 | // params[0] is the receiver for a method with `self`. |
| 6039 | let skip = usize::from(d.len() == m.args.len() + 1); | 6136 | let skip = usize::from(d.len() == m.args.len() + 1); |
| 6040 | for (i, a) in m.args.iter().enumerate() { | 6137 | for (i, a) in m.args.iter().enumerate() { |
| 6041 | - if let Some(want) = d.get(i + skip) { | 6138 | + if let Some(want) = |
| 6139 | + d.get(i + skip).filter(|t| !Self::mentions_any(t, &cgen)) | ||
| 6140 | + { | ||
| 6042 | let want = want.clone().unvar(); | 6141 | let want = want.clone().unvar(); |
| 6043 | args[i] = self.expr_at(a, Some(&want))?; | 6142 | args[i] = self.expr_at(a, Some(&want))?; |
| 6044 | } | 6143 | } |
modified
src/ty.rs +12 -1 | @@ -438,7 +438,18 @@ pub fn map(t: &Type) -> Result<Nim, String> { | ||
| 438 | 438 | // `impl Iterator<Item = T>`: Nim's closure iterator is |
| 439 | 439 | // lazy and first-class as Rust's is, so this is a real |
| 440 | 440 | // equivalent rather than a materialised `seq`. |
| 441 | - if seg.ident == "Iterator" || seg.ident == "IntoIterator" { | |
| 441 | + // `DoubleEndedIterator` and `ExactSizeIterator` are | |
| 442 | + // subtraits of `Iterator`; the *forward* half is what | |
| 443 | + // a Nim closure iterator is, exactly. The extra | |
| 444 | + // capabilities are not approximated -- they are simply | |
| 445 | + // not implemented, so `next_back`, `rev` and `len` on | |
| 446 | + // the result are rejected at the call site like any | |
| 447 | + // other unsupported method. | |
| 448 | + if seg.ident == "Iterator" | |
| 449 | + || seg.ident == "IntoIterator" | |
| 450 | + || seg.ident == "DoubleEndedIterator" | |
| 451 | + || seg.ident == "ExactSizeIterator" | |
| 452 | + { | |
| 442 | 453 | if let PathArguments::AngleBracketed(a) = &seg.arguments { |
| 443 | 454 | for g in &a.args { |
| 444 | 455 | if let GenericArgument::AssocType(at) = g { |
| @@ -438,7 +438,18 @@ pub fn map(t: &Type) -> Result<Nim, String> { | |||
| 438 | // `impl Iterator<Item = T>`: Nim's closure iterator is | 438 | // `impl Iterator<Item = T>`: Nim's closure iterator is |
| 439 | // lazy and first-class as Rust's is, so this is a real | 439 | // lazy and first-class as Rust's is, so this is a real |
| 440 | // equivalent rather than a materialised `seq`. | 440 | // equivalent rather than a materialised `seq`. |
| 441 | - if seg.ident == "Iterator" || seg.ident == "IntoIterator" { | 441 | + // `DoubleEndedIterator` and `ExactSizeIterator` are |
| 442 | + // subtraits of `Iterator`; the *forward* half is what | ||
| 443 | + // a Nim closure iterator is, exactly. The extra | ||
| 444 | + // capabilities are not approximated -- they are simply | ||
| 445 | + // not implemented, so `next_back`, `rev` and `len` on | ||
| 446 | + // the result are rejected at the call site like any | ||
| 447 | + // other unsupported method. | ||
| 448 | + if seg.ident == "Iterator" | ||
| 449 | + || seg.ident == "IntoIterator" | ||
| 450 | + || seg.ident == "DoubleEndedIterator" | ||
| 451 | + || seg.ident == "ExactSizeIterator" | ||
| 452 | + { | ||
| 442 | if let PathArguments::AngleBracketed(a) = &seg.arguments { | 453 | if let PathArguments::AngleBracketed(a) = &seg.arguments { |
| 443 | for g in &a.args { | 454 | for g in &a.args { |
| 444 | if let GenericArgument::AssocType(at) = g { | 455 | if let GenericArgument::AssocType(at) = g { |
added
tests/cases/050-nested-impl-trait.rs +32 -0 | new file mode 100644 | ||
| @@ -0,0 +1,32 @@ | ||
| 1 | +// `impl Trait` in argument position is a generic parameter — including when it | |
| 2 | +// is nested inside another type. `Option<impl Trait>` is not a return-position | |
| 3 | +// opaque type; it is `fn f<T: Trait>(x: Option<T>)`. | |
| 4 | +trait Label { | |
| 5 | + fn label(&self) -> String; | |
| 6 | +} | |
| 7 | + | |
| 8 | +struct Tag(u32); | |
| 9 | + | |
| 10 | +impl Label for Tag { | |
| 11 | + fn label(&self) -> String { | |
| 12 | + format!("tag-{}", self.0) | |
| 13 | + } | |
| 14 | +} | |
| 15 | + | |
| 16 | +fn describe(x: Option<impl Label>) -> String { | |
| 17 | + match x { | |
| 18 | + Some(v) => v.label(), | |
| 19 | + None => String::from("none"), | |
| 20 | + } | |
| 21 | +} | |
| 22 | + | |
| 23 | +fn pair(a: impl Label, b: Option<impl Label>) -> String { | |
| 24 | + format!("{} / {}", a.label(), describe(b)) | |
| 25 | +} | |
| 26 | + | |
| 27 | +fn main() { | |
| 28 | + println!("{}", describe(Some(Tag(1)))); | |
| 29 | + println!("{}", describe(None::<Tag>)); | |
| 30 | + println!("{}", pair(Tag(7), Some(Tag(9)))); | |
| 31 | + println!("{}", pair(Tag(7), None::<Tag>)); | |
| 32 | +} | |
| new file mode 100644 | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | +// `impl Trait` in argument position is a generic parameter — including when it | ||
| 2 | +// is nested inside another type. `Option<impl Trait>` is not a return-position | ||
| 3 | +// opaque type; it is `fn f<T: Trait>(x: Option<T>)`. | ||
| 4 | +trait Label { | ||
| 5 | + fn label(&self) -> String; | ||
| 6 | +} | ||
| 7 | + | ||
| 8 | +struct Tag(u32); | ||
| 9 | + | ||
| 10 | +impl Label for Tag { | ||
| 11 | + fn label(&self) -> String { | ||
| 12 | + format!("tag-{}", self.0) | ||
| 13 | + } | ||
| 14 | +} | ||
| 15 | + | ||
| 16 | +fn describe(x: Option<impl Label>) -> String { | ||
| 17 | + match x { | ||
| 18 | + Some(v) => v.label(), | ||
| 19 | + None => String::from("none"), | ||
| 20 | + } | ||
| 21 | +} | ||
| 22 | + | ||
| 23 | +fn pair(a: impl Label, b: Option<impl Label>) -> String { | ||
| 24 | + format!("{} / {}", a.label(), describe(b)) | ||
| 25 | +} | ||
| 26 | + | ||
| 27 | +fn main() { | ||
| 28 | + println!("{}", describe(Some(Tag(1)))); | ||
| 29 | + println!("{}", describe(None::<Tag>)); | ||
| 30 | + println!("{}", pair(Tag(7), Some(Tag(9)))); | ||
| 31 | + println!("{}", pair(Tag(7), None::<Tag>)); | ||
| 32 | +} | ||