Keep a return type's shape when marking its opaque part
`-> ImageResult<impl ImageDecoder>` was marked opaque as a whole, so the `Result` shape went with it -- and an `Ok(..)` in the body had nothing to take its error half from, even though the signature says plainly that it is an `ImageError`. Only the `impl Trait` is replaced now, and the rest of the type survives to be read. The body's own type still replaces the whole return type once it is known; this is about what the *probe* can see while reading it. `image` gets past its opaque return type on this. It then reports 1079 other failures, which is a large crate being large, not a regression. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
95ac909 parent: 421df4e modified
src/lower.rs +73 -0 | @@ -412,6 +412,10 @@ pub struct Lowerer { | ||
| 412 | 412 | /// Accessors for associated constants, emitted after the types they |
| 413 | 413 | /// name in a `typedesc`. |
| 414 | 414 | const_accessors: Vec<String>, |
| 415 | + /// The declared return type of the body `opaque_of` is reading, with the | |
| 416 | + /// opaque part still a placeholder. Its *other* parts are known, and an | |
| 417 | + /// `Ok(..)` in the body needs the error half. | |
| 418 | + opaque_want: Option<Nim>, | |
| 415 | 419 | /// Procs that cannot be translated, by Nim name, with the reason. |
| 416 | 420 | /// |
| 417 | 421 | /// Not an error on its own. A dependency graph carries far more code than |
| @@ -544,6 +548,7 @@ impl Lowerer { | ||
| 544 | 548 | shadow_n: 0, |
| 545 | 549 | trait_consts: HashMap::new(), |
| 546 | 550 | const_accessors: Vec::new(), |
| 551 | + opaque_want: None, | |
| 547 | 552 | untranslatable: HashMap::new(), |
| 548 | 553 | enum_module: HashMap::new(), |
| 549 | 554 | report: false, |
| @@ -2867,7 +2872,22 @@ impl Lowerer { | ||
| 2867 | 2872 | let saved_out = self.out.len(); |
| 2868 | 2873 | let saved_indent = self.indent; |
| 2869 | 2874 | let saved_hoisted = self.hoisted.len(); |
| 2875 | + // The declared return type, opaque part and all: `ImageResult<impl | |
| 2876 | + // ImageDecoder>` does not say what the decoder is, but it does say the | |
| 2877 | + // error is an `ImageError`, and an `Ok(..)` in the body needs that. | |
| 2878 | + let outer_want = self.opaque_want.take(); | |
| 2879 | + self.opaque_want = match &sig.output { | |
| 2880 | + ReturnType::Type(_, t) => { | |
| 2881 | + // Mapped with the opaque part replaced rather than the whole | |
| 2882 | + // type: `ImageResult<impl ImageDecoder>` keeps its `Result` | |
| 2883 | + // shape and its error half that way. | |
| 2884 | + let marked = mark_impl_trait(t); | |
| 2885 | + self.map_ty(&marked).ok() | |
| 2886 | + } | |
| 2887 | + ReturnType::Default => None, | |
| 2888 | + }; | |
| 2870 | 2889 | let found = self.result_ty(&tail); |
| 2890 | + self.opaque_want = outer_want; | |
| 2871 | 2891 | self.out.truncate(saved_out); |
| 2872 | 2892 | self.indent = saved_indent; |
| 2873 | 2893 | self.hoisted.truncate(saved_hoisted); |
| @@ -7949,6 +7969,24 @@ impl Lowerer { | ||
| 7949 | 7969 | } |
| 7950 | 7970 | } |
| 7951 | 7971 | } |
| 7972 | + // Failing an expectation, the *declared* return type may still | |
| 7973 | + // say what the error half is even when the success half is | |
| 7974 | + // the opaque type being solved for. The payload says the rest. | |
| 7975 | + let fallback = match (&expect, &self.opaque_want) { | |
| 7976 | + (None, Some(Nim::Named(n, a))) if n == "Result" && a.len() == 2 => { | |
| 7977 | + let t = if name == "Ok" { | |
| 7978 | + args.first() | |
| 7979 | + .and_then(|v| v.ty.clone()) | |
| 7980 | + .map(|t| t.owned()) | |
| 7981 | + .unwrap_or_else(|| a[0].clone()) | |
| 7982 | + } else { | |
| 7983 | + a[0].clone() | |
| 7984 | + }; | |
| 7985 | + Some(Nim::Named("Result".into(), vec![t, a[1].clone()])) | |
| 7986 | + } | |
| 7987 | + _ => None, | |
| 7988 | + }; | |
| 7989 | + let expect = expect.or(fallback.as_ref()); | |
| 7952 | 7990 | let (t, e) = match expect { |
| 7953 | 7991 | Some(Nim::Named(n, a)) if n == "Result" && a.len() == 2 => { |
| 7954 | 7992 | (a[0].render(), a[1].render()) |
| @@ -11168,3 +11206,38 @@ fn owned_strings(t: Nim) -> Nim { | ||
| 11168 | 11206 | other => other.owned(), |
| 11169 | 11207 | } |
| 11170 | 11208 | } |
| 11209 | + | |
| 11210 | +/// Replace every `impl Trait` inside a type with the opaque placeholder, | |
| 11211 | +/// leaving the rest of the type alone. | |
| 11212 | +fn mark_impl_trait(t: &syn::Type) -> syn::Type { | |
| 11213 | + use syn::Type; | |
| 11214 | + if matches!(t, Type::ImplTrait(_)) { | |
| 11215 | + return syn::parse_str::<Type>(OPAQUE).expect("placeholder parses"); | |
| 11216 | + } | |
| 11217 | + let mut out = t.clone(); | |
| 11218 | + match &mut out { | |
| 11219 | + Type::Reference(r) => *r.elem = mark_impl_trait(&r.elem), | |
| 11220 | + Type::Slice(s) => *s.elem = mark_impl_trait(&s.elem), | |
| 11221 | + Type::Array(a) => *a.elem = mark_impl_trait(&a.elem), | |
| 11222 | + Type::Paren(p) => *p.elem = mark_impl_trait(&p.elem), | |
| 11223 | + Type::Group(g) => *g.elem = mark_impl_trait(&g.elem), | |
| 11224 | + Type::Tuple(tu) => { | |
| 11225 | + for e in tu.elems.iter_mut() { | |
| 11226 | + *e = mark_impl_trait(e); | |
| 11227 | + } | |
| 11228 | + } | |
| 11229 | + Type::Path(p) => { | |
| 11230 | + for seg in p.path.segments.iter_mut() { | |
| 11231 | + if let syn::PathArguments::AngleBracketed(a) = &mut seg.arguments { | |
| 11232 | + for g in a.args.iter_mut() { | |
| 11233 | + if let syn::GenericArgument::Type(x) = g { | |
| 11234 | + *x = mark_impl_trait(x); | |
| 11235 | + } | |
| 11236 | + } | |
| 11237 | + } | |
| 11238 | + } | |
| 11239 | + } | |
| 11240 | + _ => {} | |
| 11241 | + } | |
| 11242 | + out | |
| 11243 | +} | |
| @@ -412,6 +412,10 @@ pub struct Lowerer { | |||
| 412 | /// Accessors for associated constants, emitted after the types they | 412 | /// Accessors for associated constants, emitted after the types they |
| 413 | /// name in a `typedesc`. | 413 | /// name in a `typedesc`. |
| 414 | const_accessors: Vec<String>, | 414 | const_accessors: Vec<String>, |
| 415 | + /// The declared return type of the body `opaque_of` is reading, with the | ||
| 416 | + /// opaque part still a placeholder. Its *other* parts are known, and an | ||
| 417 | + /// `Ok(..)` in the body needs the error half. | ||
| 418 | + opaque_want: Option<Nim>, | ||
| 415 | /// Procs that cannot be translated, by Nim name, with the reason. | 419 | /// Procs that cannot be translated, by Nim name, with the reason. |
| 416 | /// | 420 | /// |
| 417 | /// Not an error on its own. A dependency graph carries far more code than | 421 | /// Not an error on its own. A dependency graph carries far more code than |
| @@ -544,6 +548,7 @@ impl Lowerer { | |||
| 544 | shadow_n: 0, | 548 | shadow_n: 0, |
| 545 | trait_consts: HashMap::new(), | 549 | trait_consts: HashMap::new(), |
| 546 | const_accessors: Vec::new(), | 550 | const_accessors: Vec::new(), |
| 551 | + opaque_want: None, | ||
| 547 | untranslatable: HashMap::new(), | 552 | untranslatable: HashMap::new(), |
| 548 | enum_module: HashMap::new(), | 553 | enum_module: HashMap::new(), |
| 549 | report: false, | 554 | report: false, |
| @@ -2867,7 +2872,22 @@ impl Lowerer { | |||
| 2867 | let saved_out = self.out.len(); | 2872 | let saved_out = self.out.len(); |
| 2868 | let saved_indent = self.indent; | 2873 | let saved_indent = self.indent; |
| 2869 | let saved_hoisted = self.hoisted.len(); | 2874 | let saved_hoisted = self.hoisted.len(); |
| 2875 | + // The declared return type, opaque part and all: `ImageResult<impl | ||
| 2876 | + // ImageDecoder>` does not say what the decoder is, but it does say the | ||
| 2877 | + // error is an `ImageError`, and an `Ok(..)` in the body needs that. | ||
| 2878 | + let outer_want = self.opaque_want.take(); | ||
| 2879 | + self.opaque_want = match &sig.output { | ||
| 2880 | + ReturnType::Type(_, t) => { | ||
| 2881 | + // Mapped with the opaque part replaced rather than the whole | ||
| 2882 | + // type: `ImageResult<impl ImageDecoder>` keeps its `Result` | ||
| 2883 | + // shape and its error half that way. | ||
| 2884 | + let marked = mark_impl_trait(t); | ||
| 2885 | + self.map_ty(&marked).ok() | ||
| 2886 | + } | ||
| 2887 | + ReturnType::Default => None, | ||
| 2888 | + }; | ||
| 2870 | let found = self.result_ty(&tail); | 2889 | let found = self.result_ty(&tail); |
| 2890 | + self.opaque_want = outer_want; | ||
| 2871 | self.out.truncate(saved_out); | 2891 | self.out.truncate(saved_out); |
| 2872 | self.indent = saved_indent; | 2892 | self.indent = saved_indent; |
| 2873 | self.hoisted.truncate(saved_hoisted); | 2893 | self.hoisted.truncate(saved_hoisted); |
| @@ -7949,6 +7969,24 @@ impl Lowerer { | |||
| 7949 | } | 7969 | } |
| 7950 | } | 7970 | } |
| 7951 | } | 7971 | } |
| 7972 | + // Failing an expectation, the *declared* return type may still | ||
| 7973 | + // say what the error half is even when the success half is | ||
| 7974 | + // the opaque type being solved for. The payload says the rest. | ||
| 7975 | + let fallback = match (&expect, &self.opaque_want) { | ||
| 7976 | + (None, Some(Nim::Named(n, a))) if n == "Result" && a.len() == 2 => { | ||
| 7977 | + let t = if name == "Ok" { | ||
| 7978 | + args.first() | ||
| 7979 | + .and_then(|v| v.ty.clone()) | ||
| 7980 | + .map(|t| t.owned()) | ||
| 7981 | + .unwrap_or_else(|| a[0].clone()) | ||
| 7982 | + } else { | ||
| 7983 | + a[0].clone() | ||
| 7984 | + }; | ||
| 7985 | + Some(Nim::Named("Result".into(), vec![t, a[1].clone()])) | ||
| 7986 | + } | ||
| 7987 | + _ => None, | ||
| 7988 | + }; | ||
| 7989 | + let expect = expect.or(fallback.as_ref()); | ||
| 7952 | let (t, e) = match expect { | 7990 | let (t, e) = match expect { |
| 7953 | Some(Nim::Named(n, a)) if n == "Result" && a.len() == 2 => { | 7991 | Some(Nim::Named(n, a)) if n == "Result" && a.len() == 2 => { |
| 7954 | (a[0].render(), a[1].render()) | 7992 | (a[0].render(), a[1].render()) |
| @@ -11168,3 +11206,38 @@ fn owned_strings(t: Nim) -> Nim { | |||
| 11168 | other => other.owned(), | 11206 | other => other.owned(), |
| 11169 | } | 11207 | } |
| 11170 | } | 11208 | } |
| 11209 | + | ||
| 11210 | +/// Replace every `impl Trait` inside a type with the opaque placeholder, | ||
| 11211 | +/// leaving the rest of the type alone. | ||
| 11212 | +fn mark_impl_trait(t: &syn::Type) -> syn::Type { | ||
| 11213 | + use syn::Type; | ||
| 11214 | + if matches!(t, Type::ImplTrait(_)) { | ||
| 11215 | + return syn::parse_str::<Type>(OPAQUE).expect("placeholder parses"); | ||
| 11216 | + } | ||
| 11217 | + let mut out = t.clone(); | ||
| 11218 | + match &mut out { | ||
| 11219 | + Type::Reference(r) => *r.elem = mark_impl_trait(&r.elem), | ||
| 11220 | + Type::Slice(s) => *s.elem = mark_impl_trait(&s.elem), | ||
| 11221 | + Type::Array(a) => *a.elem = mark_impl_trait(&a.elem), | ||
| 11222 | + Type::Paren(p) => *p.elem = mark_impl_trait(&p.elem), | ||
| 11223 | + Type::Group(g) => *g.elem = mark_impl_trait(&g.elem), | ||
| 11224 | + Type::Tuple(tu) => { | ||
| 11225 | + for e in tu.elems.iter_mut() { | ||
| 11226 | + *e = mark_impl_trait(e); | ||
| 11227 | + } | ||
| 11228 | + } | ||
| 11229 | + Type::Path(p) => { | ||
| 11230 | + for seg in p.path.segments.iter_mut() { | ||
| 11231 | + if let syn::PathArguments::AngleBracketed(a) = &mut seg.arguments { | ||
| 11232 | + for g in a.args.iter_mut() { | ||
| 11233 | + if let syn::GenericArgument::Type(x) = g { | ||
| 11234 | + *x = mark_impl_trait(x); | ||
| 11235 | + } | ||
| 11236 | + } | ||
| 11237 | + } | ||
| 11238 | + } | ||
| 11239 | + } | ||
| 11240 | + _ => {} | ||
| 11241 | + } | ||
| 11242 | + out | ||
| 11243 | +} | ||