nandi/rustnimpublic Fork
⑂ 95ac909
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

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>
nandithebull committed 2026-09-20T15:04:55-07:00 Browse files
95ac909 parent: 421df4e
modified src/lower.rs +73 -0
@@ -412,6 +412,10 @@ pub struct Lowerer {
412412 /// Accessors for associated constants, emitted after the types they
413413 /// name in a `typedesc`.
414414 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>,
415419 /// Procs that cannot be translated, by Nim name, with the reason.
416420 ///
417421 /// Not an error on its own. A dependency graph carries far more code than
@@ -544,6 +548,7 @@ impl Lowerer {
544548 shadow_n: 0,
545549 trait_consts: HashMap::new(),
546550 const_accessors: Vec::new(),
551+ opaque_want: None,
547552 untranslatable: HashMap::new(),
548553 enum_module: HashMap::new(),
549554 report: false,
@@ -2867,7 +2872,22 @@ impl Lowerer {
28672872 let saved_out = self.out.len();
28682873 let saved_indent = self.indent;
28692874 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+ };
28702889 let found = self.result_ty(&tail);
2890+ self.opaque_want = outer_want;
28712891 self.out.truncate(saved_out);
28722892 self.indent = saved_indent;
28732893 self.hoisted.truncate(saved_hoisted);
@@ -7949,6 +7969,24 @@ impl Lowerer {
79497969 }
79507970 }
79517971 }
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());
79527990 let (t, e) = match expect {
79537991 Some(Nim::Named(n, a)) if n == "Result" && a.len() == 2 => {
79547992 (a[0].render(), a[1].render())
@@ -11168,3 +11206,38 @@ fn owned_strings(t: Nim) -> Nim {
1116811206 other => other.owned(),
1116911207 }
1117011208 }
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 they412 /// 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 than421 /// 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+}