Skip to content

Commit 3f5a287

Browse files
committed
[bindings] Un-special-case returning an associated type
In the case that we return an associated type to C (ie when implementing a trait which returns an associated type, we had to convert the Rust-returned concrete Rust type to the C trait struct), we had code to manually create the neccessary trait struct at the return site. This was special-cased in the method-body-writing function instead of letting the type conversion logic handle it. As a result, we are unable to do the same conversion when it appears in a different context, for example inside of a generic like `Result<Self::AssocType, ErrorType>`. To solve this, we do the actual work in a `impl From<nativeType> for CTraitStruct` implementation and then call `into()` from within the type conversion logic.
1 parent 086434f commit 3f5a287

File tree

3 files changed

+30
-30
lines changed

3 files changed

+30
-30
lines changed

c-bindings-gen/src/blocks.rs

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -274,34 +274,12 @@ pub fn write_method_call_params<W: std::io::Write>(w: &mut W, sig: &syn::Signatu
274274
syn::ReturnType::Type(_, rtype) => {
275275
write!(w, ";\n\t{}", extra_indent).unwrap();
276276

277+
let self_segs_iter = first_seg_self(&*rtype);
277278
if to_c && first_seg_self(&*rtype).is_some() {
278279
// Assume rather blindly that we're returning an associated trait from a C fn call to a Rust trait object.
279280
write!(w, "ret").unwrap();
280-
} else if !to_c && first_seg_self(&*rtype).is_some() {
281-
if let Some(mut remaining_path) = first_seg_self(&*rtype) {
282-
if let Some(associated_seg) = get_single_remaining_path_seg(&mut remaining_path) {
283-
// Build a fake path with only associated_seg and resolve it:
284-
let mut segments = syn::punctuated::Punctuated::new();
285-
segments.push(syn::PathSegment {
286-
ident: associated_seg.clone(), arguments: syn::PathArguments::None });
287-
let (_, real_path) = generics.unwrap().maybe_resolve_path(&syn::Path {
288-
leading_colon: None, segments }).unwrap();
289-
290-
assert_eq!(real_path.segments.len(), 1);
291-
let real_ident = &real_path.segments.iter().next().unwrap().ident;
292-
if let Some(t) = types.crate_types.traits.get(&types.maybe_resolve_ident(&real_ident).unwrap()) {
293-
// We're returning an associated trait from a Rust fn call to a C trait
294-
// object.
295-
writeln!(w, "let mut rust_obj = {} {{ inner: Box::into_raw(Box::new(ret)), is_owned: true }};", this_type).unwrap();
296-
writeln!(w, "\t{}let mut ret = {}_as_{}(&rust_obj);", extra_indent, this_type, t.ident).unwrap();
297-
writeln!(w, "\t{}// We want to free rust_obj when ret gets drop()'d, not rust_obj, so wipe rust_obj's pointer and set ret's free() fn", extra_indent).unwrap();
298-
writeln!(w, "\t{}rust_obj.inner = std::ptr::null_mut();", extra_indent).unwrap();
299-
writeln!(w, "\t{}ret.free = Some({}_free_void);", extra_indent, this_type).unwrap();
300-
writeln!(w, "\t{}ret", extra_indent).unwrap();
301-
return;
302-
}
303-
}
304-
}
281+
} else if !to_c && self_segs_iter.is_some() && self_segs_iter.unwrap().next().is_none() {
282+
// If we're returning "Self" (and not "Self::X"), just do it manually
305283
write!(w, "{} {{ inner: Box::into_raw(Box::new(ret)), is_owned: true }}", this_type).unwrap();
306284
} else if to_c {
307285
let new_var = types.write_from_c_conversion_new_var(w, &syn::Ident::new("ret", Span::call_site()), rtype, generics);

c-bindings-gen/src/main.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,21 @@ fn writeln_impl<W: std::io::Write>(w: &mut W, i: &syn::ItemImpl, types: &mut Typ
600600
ExportStatus::Export => {},
601601
ExportStatus::NoExport|ExportStatus::TestOnly => return,
602602
}
603+
604+
// For cases where we have a concrete native object which implements a
605+
// trait and need to return the C-mapped version of the trait, provide a
606+
// From<> implementation which does all the work to ensure free is handled
607+
// properly. This way we can call this method from deep in the
608+
// type-conversion logic without actually knowing the concrete native type.
609+
writeln!(w, "impl From<native{}> for crate::{} {{", ident, full_trait_path).unwrap();
610+
writeln!(w, "\tfn from(obj: native{}) -> Self {{", ident).unwrap();
611+
writeln!(w, "\t\tlet mut rust_obj = {} {{ inner: Box::into_raw(Box::new(obj)), is_owned: true }};", ident).unwrap();
612+
writeln!(w, "\t\tlet mut ret = {}_as_{}(&rust_obj);", ident, trait_obj.ident).unwrap();
613+
writeln!(w, "\t\t// We want to free rust_obj when ret gets drop()'d, not rust_obj, so wipe rust_obj's pointer and set ret's free() fn").unwrap();
614+
writeln!(w, "\t\trust_obj.inner = std::ptr::null_mut();").unwrap();
615+
writeln!(w, "\t\tret.free = Some({}_free_void);", ident).unwrap();
616+
writeln!(w, "\t\tret\n\t}}\n}}").unwrap();
617+
603618
write!(w, "#[no_mangle]\npub extern \"C\" fn {}_as_{}(this_arg: *const {}) -> crate::{} {{\n", ident, trait_obj.ident, ident, full_trait_path).unwrap();
604619
writeln!(w, "\tcrate::{} {{", full_trait_path).unwrap();
605620
writeln!(w, "\t\tthis_arg: unsafe {{ (*this_arg).inner as *mut c_void }},").unwrap();

c-bindings-gen/src/types.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1250,17 +1250,16 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> {
12501250
decl_lookup(w, &DeclType::StructImported, &resolved_path, is_ref, is_mut);
12511251
} else if self.crate_types.mirrored_enums.get(&resolved_path).is_some() {
12521252
decl_lookup(w, &DeclType::MirroredEnum, &resolved_path, is_ref, is_mut);
1253+
} else if let Some(t) = self.crate_types.traits.get(&resolved_path) {
1254+
decl_lookup(w, &DeclType::Trait(t), &resolved_path, is_ref, is_mut);
12531255
} else if let Some(ident) = single_ident_generic_path_to_ident(&p.path) {
1254-
if let Some(t) = self.crate_types.traits.get(&resolved_path) {
1255-
decl_lookup(w, &DeclType::Trait(t), &resolved_path, is_ref, is_mut);
1256-
return;
1257-
} else if let Some(_) = self.imports.get(ident) {
1256+
if let Some(_) = self.imports.get(ident) {
12581257
// crate_types lookup has to have succeeded:
12591258
panic!("Failed to print inline conversion for {}", ident);
12601259
} else if let Some(decl_type) = self.declared.get(ident) {
12611260
decl_lookup(w, decl_type, &self.maybe_resolve_ident(ident).unwrap(), is_ref, is_mut);
12621261
} else { unimplemented!(); }
1263-
}
1262+
} else { unimplemented!(); }
12641263
},
12651264
syn::Type::Array(a) => {
12661265
// We assume all arrays contain only [int_literal; X]s.
@@ -1343,6 +1342,7 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> {
13431342
DeclType::EnumIgnored|DeclType::StructImported if !is_ref =>
13441343
write!(w, "crate::{} {{ inner: Box::into_raw(Box::new(", decl_path).unwrap(),
13451344
DeclType::Trait(_) if is_ref => write!(w, "&").unwrap(),
1345+
DeclType::Trait(_) if !is_ref => {},
13461346
_ => panic!("{:?}", decl_path),
13471347
}
13481348
});
@@ -1365,6 +1365,13 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> {
13651365
write!(w, ", is_owned: true }}").unwrap(),
13661366
DeclType::EnumIgnored|DeclType::StructImported if !is_ref => write!(w, ")), is_owned: true }}").unwrap(),
13671367
DeclType::Trait(_) if is_ref => {},
1368+
DeclType::Trait(_) => {
1369+
// This is used when we're converting a concrete Rust type into a C trait
1370+
// for use when a Rust trait method returns an associated type.
1371+
// Because all of our C traits implement From<RustTypesImplementingTraits>
1372+
// we can just call .into() here and be done.
1373+
write!(w, ".into()").unwrap()
1374+
},
13681375
_ => unimplemented!(),
13691376
});
13701377
}

0 commit comments

Comments
 (0)