Resolve `impl Trait` nested in a return type
`-> Option<impl Display>` is opaque in the same way a bare `-> impl Display` is, and the body determines the whole return type, so the resolution added two commits ago already answers for it -- the guard was just too narrow, matching only a bare `impl Trait`. Found by the dependency run, in `drm-fourcc`'s `fourcc_display_form`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
62d5926 parent: 3ba74c0 modified
src/lower.rs +24 -3 | @@ -1851,9 +1851,10 @@ impl Lowerer { | ||
| 1851 | 1851 | // the body produces. Rust hides which; it does not permit two. So |
| 1852 | 1852 | // the type is recoverable from the body, and `resolve_opaque` |
| 1853 | 1853 | // recovers it. Marked here, resolved between passes. |
| 1854 | - ReturnType::Type(_, t) | |
| 1855 | - if matches!(&**t, syn::Type::ImplTrait(_)) && self.map_ty(t).is_err() => | |
| 1856 | - { | |
| 1854 | + // Not only a *bare* `impl Trait`: `-> Option<impl Display>` is | |
| 1855 | + // opaque in the same way, and the body determines the whole | |
| 1856 | + // return type, so the same resolution answers for both. | |
| 1857 | + ReturnType::Type(_, t) if contains_impl_trait(t) && self.map_ty(t).is_err() => { | |
| 1857 | 1858 | match &self.opaque_hint { |
| 1858 | 1859 | Some(t) => t.clone(), |
| 1859 | 1860 | None => Nim::Named(OPAQUE.into(), Vec::new()), |
| @@ -7655,3 +7656,23 @@ fn result_exprs(e: &Expr) -> Vec<Expr> { | ||
| 7655 | 7656 | other => vec![other.clone()], |
| 7656 | 7657 | } |
| 7657 | 7658 | } |
| 7659 | + | |
| 7660 | +/// Whether a type contains an `impl Trait` anywhere inside it. | |
| 7661 | +fn contains_impl_trait(t: &syn::Type) -> bool { | |
| 7662 | + use syn::Type; | |
| 7663 | + match t { | |
| 7664 | + Type::ImplTrait(_) => true, | |
| 7665 | + Type::Reference(r) => contains_impl_trait(&r.elem), | |
| 7666 | + Type::Slice(s) => contains_impl_trait(&s.elem), | |
| 7667 | + Type::Array(a) => contains_impl_trait(&a.elem), | |
| 7668 | + Type::Paren(p) => contains_impl_trait(&p.elem), | |
| 7669 | + Type::Group(g) => contains_impl_trait(&g.elem), | |
| 7670 | + Type::Tuple(tu) => tu.elems.iter().any(contains_impl_trait), | |
| 7671 | + Type::Path(p) => p.path.segments.iter().any(|seg| { | |
| 7672 | + matches!(&seg.arguments, syn::PathArguments::AngleBracketed(a) | |
| 7673 | + if a.args.iter().any(|g| matches!(g, syn::GenericArgument::Type(t) | |
| 7674 | + if contains_impl_trait(t)))) | |
| 7675 | + }), | |
| 7676 | + _ => false, | |
| 7677 | + } | |
| 7678 | +} | |
| @@ -1851,9 +1851,10 @@ impl Lowerer { | |||
| 1851 | // the body produces. Rust hides which; it does not permit two. So | 1851 | // the body produces. Rust hides which; it does not permit two. So |
| 1852 | // the type is recoverable from the body, and `resolve_opaque` | 1852 | // the type is recoverable from the body, and `resolve_opaque` |
| 1853 | // recovers it. Marked here, resolved between passes. | 1853 | // recovers it. Marked here, resolved between passes. |
| 1854 | - ReturnType::Type(_, t) | 1854 | + // Not only a *bare* `impl Trait`: `-> Option<impl Display>` is |
| 1855 | - if matches!(&**t, syn::Type::ImplTrait(_)) && self.map_ty(t).is_err() => | 1855 | + // opaque in the same way, and the body determines the whole |
| 1856 | - { | 1856 | + // return type, so the same resolution answers for both. |
| 1857 | + ReturnType::Type(_, t) if contains_impl_trait(t) && self.map_ty(t).is_err() => { | ||
| 1857 | match &self.opaque_hint { | 1858 | match &self.opaque_hint { |
| 1858 | Some(t) => t.clone(), | 1859 | Some(t) => t.clone(), |
| 1859 | None => Nim::Named(OPAQUE.into(), Vec::new()), | 1860 | None => Nim::Named(OPAQUE.into(), Vec::new()), |
| @@ -7655,3 +7656,23 @@ fn result_exprs(e: &Expr) -> Vec<Expr> { | |||
| 7655 | other => vec![other.clone()], | 7656 | other => vec![other.clone()], |
| 7656 | } | 7657 | } |
| 7657 | } | 7658 | } |
| 7659 | + | ||
| 7660 | +/// Whether a type contains an `impl Trait` anywhere inside it. | ||
| 7661 | +fn contains_impl_trait(t: &syn::Type) -> bool { | ||
| 7662 | + use syn::Type; | ||
| 7663 | + match t { | ||
| 7664 | + Type::ImplTrait(_) => true, | ||
| 7665 | + Type::Reference(r) => contains_impl_trait(&r.elem), | ||
| 7666 | + Type::Slice(s) => contains_impl_trait(&s.elem), | ||
| 7667 | + Type::Array(a) => contains_impl_trait(&a.elem), | ||
| 7668 | + Type::Paren(p) => contains_impl_trait(&p.elem), | ||
| 7669 | + Type::Group(g) => contains_impl_trait(&g.elem), | ||
| 7670 | + Type::Tuple(tu) => tu.elems.iter().any(contains_impl_trait), | ||
| 7671 | + Type::Path(p) => p.path.segments.iter().any(|seg| { | ||
| 7672 | + matches!(&seg.arguments, syn::PathArguments::AngleBracketed(a) | ||
| 7673 | + if a.args.iter().any(|g| matches!(g, syn::GenericArgument::Type(t) | ||
| 7674 | + if contains_impl_trait(t)))) | ||
| 7675 | + }), | ||
| 7676 | + _ => false, | ||
| 7677 | + } | ||
| 7678 | +} | ||
modified
tests/cases/056-return-impl-trait.rs +14 -0 | @@ -38,9 +38,23 @@ fn pick(n: u32) -> impl Display { | ||
| 38 | 38 | } |
| 39 | 39 | } |
| 40 | 40 | |
| 41 | +// `impl Trait` nested in the return type. Opaque in the same way, and the | |
| 42 | +// body determines the whole type, so the same resolution answers for it. | |
| 43 | +fn maybe(n: u32) -> Option<impl Display> { | |
| 44 | + if n > 0 { Some(Tag { n }) } else { None } | |
| 45 | +} | |
| 46 | + | |
| 41 | 47 | fn main() { |
| 42 | 48 | println!("{}", make(3)); |
| 43 | 49 | println!("{}", relay(3)); |
| 44 | 50 | println!("{}", Wrap.build(9)); |
| 45 | 51 | println!("{} {}", pick(0), pick(7)); |
| 52 | + match maybe(5) { | |
| 53 | + Some(v) => println!("{}", v), | |
| 54 | + None => println!("none"), | |
| 55 | + } | |
| 56 | + match maybe(0) { | |
| 57 | + Some(v) => println!("{}", v), | |
| 58 | + None => println!("none"), | |
| 59 | + } | |
| 46 | 60 | } |
| @@ -38,9 +38,23 @@ fn pick(n: u32) -> impl Display { | |||
| 38 | } | 38 | } |
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | +// `impl Trait` nested in the return type. Opaque in the same way, and the | ||
| 42 | +// body determines the whole type, so the same resolution answers for it. | ||
| 43 | +fn maybe(n: u32) -> Option<impl Display> { | ||
| 44 | + if n > 0 { Some(Tag { n }) } else { None } | ||
| 45 | +} | ||
| 46 | + | ||
| 41 | fn main() { | 47 | fn main() { |
| 42 | println!("{}", make(3)); | 48 | println!("{}", make(3)); |
| 43 | println!("{}", relay(3)); | 49 | println!("{}", relay(3)); |
| 44 | println!("{}", Wrap.build(9)); | 50 | println!("{}", Wrap.build(9)); |
| 45 | println!("{} {}", pick(0), pick(7)); | 51 | println!("{} {}", pick(0), pick(7)); |
| 52 | + match maybe(5) { | ||
| 53 | + Some(v) => println!("{}", v), | ||
| 54 | + None => println!("none"), | ||
| 55 | + } | ||
| 56 | + match maybe(0) { | ||
| 57 | + Some(v) => println!("{}", v), | ||
| 58 | + None => println!("none"), | ||
| 59 | + } | ||
| 46 | } | 60 | } |