X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=c-bindings-gen%2Fsrc%2Ftypes.rs;h=74db47ccb35054f45943fcbc90d973cc68215d03;hb=11063db7153ff78d289674672099d69af0ff6a8d;hp=ff1d4b833581602d523e0fce0b46fb6ddc68d511;hpb=855629d01545ae066130bad406352c91e8c31fd5;p=ldk-c-bindings diff --git a/c-bindings-gen/src/types.rs b/c-bindings-gen/src/types.rs index ff1d4b8..74db47c 100644 --- a/c-bindings-gen/src/types.rs +++ b/c-bindings-gen/src/types.rs @@ -890,6 +890,7 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { "std::io::Error" => Some("crate::c_types::IOError"), "bech32::u5" => Some("crate::c_types::u5"), + "core::num::NonZeroU8" => Some("u8"), "bitcoin::secp256k1::key::PublicKey"|"bitcoin::secp256k1::PublicKey"|"secp256k1::key::PublicKey" => Some("crate::c_types::PublicKey"), @@ -910,6 +911,11 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { "bitcoin::blockdata::block::BlockHeader" if is_ref => Some("*const [u8; 80]"), "bitcoin::blockdata::block::Block" if is_ref => Some("crate::c_types::u8slice"), + "bitcoin::hash_types::PubkeyHash"|"bitcoin::hash_types::WPubkeyHash"|"bitcoin::hash_types::ScriptHash" + if is_ref => Some("*const [u8; 20]"), + "bitcoin::hash_types::WScriptHash" + if is_ref => Some("*const [u8; 32]"), + // Newtypes that we just expose in their original form. "bitcoin::hash_types::Txid"|"bitcoin::hash_types::BlockHash"|"bitcoin_hashes::sha256::Hash" if is_ref => Some("*const [u8; 32]"), @@ -963,6 +969,7 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { "std::time::SystemTime" => Some("(::std::time::SystemTime::UNIX_EPOCH + std::time::Duration::from_secs("), "bech32::u5" => Some(""), + "core::num::NonZeroU8" => Some("core::num::NonZeroU8::new("), "bitcoin::secp256k1::key::PublicKey"|"bitcoin::secp256k1::PublicKey"|"secp256k1::key::PublicKey" if is_ref => Some("&"), @@ -984,6 +991,15 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { "bitcoin::blockdata::block::BlockHeader" => Some("&::bitcoin::consensus::encode::deserialize(unsafe { &*"), "bitcoin::blockdata::block::Block" if is_ref => Some("&::bitcoin::consensus::encode::deserialize("), + "bitcoin::hash_types::PubkeyHash" if is_ref => + Some("&bitcoin::hash_types::PubkeyHash::from_hash(bitcoin::hashes::Hash::from_inner(unsafe { *"), + "bitcoin::hash_types::WPubkeyHash" if is_ref => + Some("&bitcoin::hash_types::WPubkeyHash::from_hash(bitcoin::hashes::Hash::from_inner(unsafe { *"), + "bitcoin::hash_types::ScriptHash" if is_ref => + Some("&bitcoin::hash_types::ScriptHash::from_hash(bitcoin::hashes::Hash::from_inner(unsafe { *"), + "bitcoin::hash_types::WScriptHash" if is_ref => + Some("&bitcoin::hash_types::WScriptHash::from_hash(bitcoin::hashes::Hash::from_inner(unsafe { *"), + // Newtypes that we just expose in their original form. "bitcoin::hash_types::Txid" if is_ref => Some("&::bitcoin::hash_types::Txid::from_slice(&unsafe { &*"), "bitcoin::hash_types::Txid" if !is_ref => Some("::bitcoin::hash_types::Txid::from_slice(&"), @@ -1028,6 +1044,7 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { "std::time::SystemTime" => Some("))"), "bech32::u5" => Some(".into()"), + "core::num::NonZeroU8" => Some(").expect(\"Value must be non-zero\")"), "bitcoin::secp256k1::key::PublicKey"|"bitcoin::secp256k1::PublicKey"|"secp256k1::key::PublicKey" => Some(".into_rust()"), @@ -1045,6 +1062,10 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { "bitcoin::blockdata::block::BlockHeader" => Some(" }).unwrap()"), "bitcoin::blockdata::block::Block" => Some(".to_slice()).unwrap()"), + "bitcoin::hash_types::PubkeyHash"|"bitcoin::hash_types::WPubkeyHash"| + "bitcoin::hash_types::ScriptHash"|"bitcoin::hash_types::WScriptHash" + if is_ref => Some(" }.clone()))"), + // Newtypes that we just expose in their original form. "bitcoin::hash_types::Txid" if is_ref => Some(" }[..]).unwrap()"), "bitcoin::hash_types::Txid" => Some(".data[..]).unwrap()"), @@ -1263,7 +1284,7 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { } /// Returns true if the path is a "transparent" container, ie an Option or a container which does /// not require a generated continer class. - fn is_path_transparent_container(&self, full_path: &syn::Path, generics: Option<&GenericTypes>, is_ref: bool) -> bool { + pub fn is_path_transparent_container(&self, full_path: &syn::Path, generics: Option<&GenericTypes>, is_ref: bool) -> bool { let inner_iter = match &full_path.segments.last().unwrap().arguments { syn::PathArguments::None => return false, syn::PathArguments::AngleBracketed(args) => args.args.iter().map(|arg| { @@ -1318,9 +1339,9 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { let is_inner_ref = if let Some(syn::Type::Reference(_)) = single_contained { true } else { false }; if is_ref { return Some(("if ", vec![ - (".is_none() { std::ptr::null() } else { ".to_owned(), + (".is_none() { std::ptr::null() } else { ObjOps::nonnull_ptr_to_inner(".to_owned(), format!("({}{}.unwrap())", var_access, if is_inner_ref { "" } else { ".as_ref()" })) - ], " }", ContainerPrefixLocation::OutsideConv)); + ], ") }", ContainerPrefixLocation::OutsideConv)); } else { return Some(("if ", vec![ (".is_none() { std::ptr::null_mut() } else { ".to_owned(), format!("({}.unwrap())", var_access)) @@ -1410,6 +1431,49 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { } } + /// Constructs a reference to the given type, possibly tweaking the type if relevant to make it + /// convertable to C. + pub fn create_ownable_reference(&self, t: &syn::Type, generics: Option<&GenericTypes>) -> Option { + let default_value = Some(syn::Type::Reference(syn::TypeReference { + and_token: syn::Token!(&)(Span::call_site()), lifetime: None, mutability: None, + elem: Box::new(t.clone()) })); + match generics.resolve_type(t) { + syn::Type::Path(p) => { + if let Some(resolved_path) = self.maybe_resolve_path(&p.path, generics) { + if resolved_path != "Vec" { return default_value; } + if p.path.segments.len() != 1 { unimplemented!(); } + let only_seg = p.path.segments.iter().next().unwrap(); + if let syn::PathArguments::AngleBracketed(args) = &only_seg.arguments { + if args.args.len() != 1 { unimplemented!(); } + let inner_arg = args.args.iter().next().unwrap(); + if let syn::GenericArgument::Type(ty) = &inner_arg { + let mut can_create = self.c_type_has_inner(&ty); + if let syn::Type::Path(inner) = ty { + if inner.path.segments.len() == 1 && + format!("{}", inner.path.segments[0].ident) == "Vec" { + can_create = true; + } + } + if !can_create { return default_value; } + if let Some(inner_ty) = self.create_ownable_reference(&ty, generics) { + return Some(syn::Type::Reference(syn::TypeReference { + and_token: syn::Token![&](Span::call_site()), + lifetime: None, + mutability: None, + elem: Box::new(syn::Type::Slice(syn::TypeSlice { + bracket_token: syn::token::Bracket { span: Span::call_site() }, + elem: Box::new(inner_ty) + })) + })); + } else { return default_value; } + } else { unimplemented!(); } + } else { unimplemented!(); } + } else { return None; } + }, + _ => default_value, + } + } + // ************************************************* // *** Type definition during main.rs processing *** // ************************************************* @@ -1421,12 +1485,14 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { pub fn c_type_has_inner_from_path(&self, full_path: &str) -> bool { self.crate_types.opaques.get(full_path).is_some() } + /// Returns true if the object at the given path is mapped as X { inner: *mut origX, .. }. pub fn c_type_has_inner(&self, ty: &syn::Type) -> bool { match ty { syn::Type::Path(p) => { - let full_path = self.resolve_path(&p.path, None); - self.c_type_has_inner_from_path(&full_path) + if let Some(full_path) = self.maybe_resolve_path(&p.path, None) { + self.c_type_has_inner_from_path(&full_path) + } else { false } }, syn::Type::Reference(r) => { self.c_type_has_inner(&*r.elem) @@ -1788,6 +1854,8 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { } else if let syn::Type::Reference(r) = &*s.elem { if let syn::Type::Path(p) = &*r.elem { write!(w, "{}", sliceconv(self.c_type_has_inner_from_path(&self.resolve_path(&p.path, generics)), None)).unwrap(); + } else if let syn::Type::Slice(_) = &*r.elem { + write!(w, "{}", sliceconv(false, None)).unwrap(); } else { unimplemented!(); } } else if let syn::Type::Tuple(t) = &*s.elem { assert!(!t.elems.is_empty()); @@ -1845,16 +1913,16 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { DeclType::MirroredEnum => write!(w, "crate::{}::native_into(", decl_path).unwrap(), DeclType::EnumIgnored|DeclType::StructImported if is_ref && ptr_for_ref && from_ptr => write!(w, "crate::{} {{ inner: unsafe {{ (", decl_path).unwrap(), - DeclType::EnumIgnored|DeclType::StructImported if is_ref && ptr_for_ref => - write!(w, "crate::{} {{ inner: unsafe {{ ( (&(*", decl_path).unwrap(), - DeclType::EnumIgnored|DeclType::StructImported if is_ref => - write!(w, "&crate::{} {{ inner: unsafe {{ (", decl_path).unwrap(), + DeclType::EnumIgnored|DeclType::StructImported if is_ref => { + if !ptr_for_ref { write!(w, "&").unwrap(); } + write!(w, "crate::{} {{ inner: unsafe {{ ObjOps::nonnull_ptr_to_inner((", decl_path).unwrap() + }, DeclType::EnumIgnored|DeclType::StructImported if !is_ref && from_ptr => write!(w, "crate::{} {{ inner: ", decl_path).unwrap(), DeclType::EnumIgnored|DeclType::StructImported if !is_ref => - write!(w, "crate::{} {{ inner: Box::into_raw(Box::new(", decl_path).unwrap(), + write!(w, "crate::{} {{ inner: ObjOps::heap_alloc(", decl_path).unwrap(), DeclType::Trait(_) if is_ref => write!(w, "").unwrap(), - DeclType::Trait(_) if !is_ref => {}, + DeclType::Trait(_) if !is_ref => write!(w, "Into::into(").unwrap(), _ => panic!("{:?}", decl_path), } }); @@ -1869,20 +1937,18 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { DeclType::MirroredEnum => write!(w, ")").unwrap(), DeclType::EnumIgnored|DeclType::StructImported if is_ref && ptr_for_ref && from_ptr => write!(w, " as *const _) as *mut _ }}, is_owned: false }}").unwrap(), - DeclType::EnumIgnored|DeclType::StructImported if is_ref && ptr_for_ref => - write!(w, ") as *const _) as *mut _) }}, is_owned: false }}").unwrap(), DeclType::EnumIgnored|DeclType::StructImported if is_ref => - write!(w, " as *const _) as *mut _ }}, is_owned: false }}").unwrap(), + write!(w, " as *const _) as *mut _) }}, is_owned: false }}").unwrap(), DeclType::EnumIgnored|DeclType::StructImported if !is_ref && from_ptr => write!(w, ", is_owned: true }}").unwrap(), - DeclType::EnumIgnored|DeclType::StructImported if !is_ref => write!(w, ")), is_owned: true }}").unwrap(), + DeclType::EnumIgnored|DeclType::StructImported if !is_ref => write!(w, "), is_owned: true }}").unwrap(), DeclType::Trait(_) if is_ref => {}, DeclType::Trait(_) => { // This is used when we're converting a concrete Rust type into a C trait // for use when a Rust trait method returns an associated type. // Because all of our C traits implement From // we can just call .into() here and be done. - write!(w, ".into()").unwrap() + write!(w, ")").unwrap() }, _ => unimplemented!(), }); @@ -1891,13 +1957,11 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { self.write_to_c_conversion_inline_suffix_inner(w, t, generics, false, ptr_for_ref, false); } - fn write_from_c_conversion_prefix_inner(&self, w: &mut W, t: &syn::Type, generics: Option<&GenericTypes>, is_ref: bool, ptr_for_ref: bool) { + fn write_from_c_conversion_prefix_inner(&self, w: &mut W, t: &syn::Type, generics: Option<&GenericTypes>, is_ref: bool, _ptr_for_ref: bool) { self.write_conversion_inline_intern(w, t, generics, is_ref, false, false, "() /*", true, |_, _| "&local_".to_owned(), |a, b, _c| self.from_c_conversion_prefix_from_path(a, b), - |w, decl_type, _full_path, is_ref, is_mut| match decl_type { - DeclType::StructImported if is_ref && ptr_for_ref => write!(w, "unsafe {{ &*(*").unwrap(), - DeclType::StructImported if is_mut && is_ref => write!(w, "unsafe {{ &mut *").unwrap(), - DeclType::StructImported if is_ref => write!(w, "unsafe {{ &*").unwrap(), + |w, decl_type, _full_path, is_ref, _is_mut| match decl_type { + DeclType::StructImported if is_ref => write!(w, "").unwrap(), DeclType::StructImported if !is_ref => write!(w, "*unsafe {{ Box::from_raw(").unwrap(), DeclType::MirroredEnum if is_ref => write!(w, "&").unwrap(), DeclType::MirroredEnum => {}, @@ -1917,9 +1981,10 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { (true, Some(_)) => unreachable!(), }, |a, b, _c| self.from_c_conversion_suffix_from_path(a, b), - |w, decl_type, _full_path, is_ref, _is_mut| match decl_type { - DeclType::StructImported if is_ref && ptr_for_ref => write!(w, ").inner }}").unwrap(), - DeclType::StructImported if is_ref => write!(w, ".inner }}").unwrap(), + |w, decl_type, _full_path, is_ref, is_mut| match decl_type { + DeclType::StructImported if is_ref && ptr_for_ref => write!(w, "XXX unimplemented").unwrap(), + DeclType::StructImported if is_mut && is_ref => write!(w, ".get_native_mut_ref()").unwrap(), + DeclType::StructImported if is_ref => write!(w, ".get_native_ref()").unwrap(), DeclType::StructImported if !is_ref => write!(w, ".take_inner()) }}").unwrap(), DeclType::MirroredEnum if is_ref => write!(w, ".to_native()").unwrap(), DeclType::MirroredEnum => write!(w, ".into_native()").unwrap(), @@ -1940,7 +2005,7 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { } else { None } }, |w, decl_type, _full_path, is_ref, _is_mut| match decl_type { - DeclType::StructImported if !is_ref => write!(w, "unsafe {{ &*").unwrap(), + DeclType::StructImported if !is_ref => write!(w, "").unwrap(), _ => unimplemented!(), }); } @@ -1954,7 +2019,7 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { }, |a, b, _c| self.from_c_conversion_suffix_from_path(a, b), |w, decl_type, _full_path, is_ref, _is_mut| match decl_type { - DeclType::StructImported if !is_ref => write!(w, ".inner }}").unwrap(), + DeclType::StructImported if !is_ref => write!(w, ".get_native_ref()").unwrap(), _ => unimplemented!(), }); } @@ -2043,14 +2108,14 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { if prefix_location == ContainerPrefixLocation::PerConv { var_prefix(w, conv_ty, generics, is_ref && ty_has_inner, ptr_for_ref, false); } else if !is_ref && !needs_ref_map && to_c && only_contained_has_inner { - write!(w, "Box::into_raw(Box::new(").unwrap(); + write!(w, "ObjOps::heap_alloc(").unwrap(); } write!(w, "{}{}", if contains_slice { "local_" } else { "" }, if new_var { new_var_name } else { var_access }).unwrap(); if prefix_location == ContainerPrefixLocation::PerConv { var_suffix(w, conv_ty, generics, is_ref && ty_has_inner, ptr_for_ref, false); } else if !is_ref && !needs_ref_map && to_c && only_contained_has_inner { - write!(w, "))").unwrap(); + write!(w, ")").unwrap(); } write!(w, " }}").unwrap(); } @@ -2091,7 +2156,7 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { if let syn::PathArguments::AngleBracketed(args) = &p.path.segments.iter().next().unwrap().arguments { convert_container!(resolved_path, args.args.len(), || args.args.iter().map(|arg| { if let syn::GenericArgument::Type(ty) = arg { - ty + generics.resolve_type(ty) } else { unimplemented!(); } })); } else { unimplemented!(); } @@ -2124,7 +2189,7 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { } else if let syn::Type::Reference(ty) = &*s.elem { let tyref = [&*ty.elem]; is_ref = true; - convert_container!("Slice", 1, || tyref.iter().map(|t| *t)); + convert_container!("Slice", 1, || tyref.iter().map(|t| generics.resolve_type(*t))); unimplemented!("convert_container should return true as container_lookup should succeed for slices"); } else if let syn::Type::Tuple(t) = &*s.elem { // When mapping into a temporary new var, we need to own all the underlying objects. @@ -2399,62 +2464,68 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { } else { return false; } } } - if let syn::Type::Tuple(tuple) = arg { - if tuple.elems.len() == 0 { - write!(w, "None").unwrap(); - write!(mangled_type, "None").unwrap(); - } else { - let mut mangled_tuple_type: Vec = Vec::new(); - - // Figure out what the mangled type should look like. To disambiguate - // ((A, B), C) and (A, B, C) we prefix the generic args with a _ and suffix - // them with a Z. Ideally we wouldn't use Z, but not many special chars are - // available for use in type names. - write!(w, "C{}Tuple_", tuple.elems.len()).unwrap(); - write!(mangled_type, "C{}Tuple_", tuple.elems.len()).unwrap(); - write!(mangled_tuple_type, "C{}Tuple_", tuple.elems.len()).unwrap(); - for elem in tuple.elems.iter() { - if let syn::Type::Path(p) = elem { - write_path!(p, Some(&mut mangled_tuple_type)); - } else if let syn::Type::Reference(refelem) = elem { - if let syn::Type::Path(p) = &*refelem.elem { + match generics.resolve_type(arg) { + syn::Type::Tuple(tuple) => { + if tuple.elems.len() == 0 { + write!(w, "None").unwrap(); + write!(mangled_type, "None").unwrap(); + } else { + let mut mangled_tuple_type: Vec = Vec::new(); + + // Figure out what the mangled type should look like. To disambiguate + // ((A, B), C) and (A, B, C) we prefix the generic args with a _ and suffix + // them with a Z. Ideally we wouldn't use Z, but not many special chars are + // available for use in type names. + write!(w, "C{}Tuple_", tuple.elems.len()).unwrap(); + write!(mangled_type, "C{}Tuple_", tuple.elems.len()).unwrap(); + write!(mangled_tuple_type, "C{}Tuple_", tuple.elems.len()).unwrap(); + for elem in tuple.elems.iter() { + if let syn::Type::Path(p) = elem { write_path!(p, Some(&mut mangled_tuple_type)); + } else if let syn::Type::Reference(refelem) = elem { + if let syn::Type::Path(p) = &*refelem.elem { + write_path!(p, Some(&mut mangled_tuple_type)); + } else { return false; } } else { return false; } - } else { return false; } - } - write!(w, "Z").unwrap(); - write!(mangled_type, "Z").unwrap(); - write!(mangled_tuple_type, "Z").unwrap(); - if !self.check_create_container(String::from_utf8(mangled_tuple_type).unwrap(), - &format!("{}Tuple", tuple.elems.len()), tuple.elems.iter().collect(), generics, is_ref) { - return false; + } + write!(w, "Z").unwrap(); + write!(mangled_type, "Z").unwrap(); + write!(mangled_tuple_type, "Z").unwrap(); + if !self.check_create_container(String::from_utf8(mangled_tuple_type).unwrap(), + &format!("{}Tuple", tuple.elems.len()), tuple.elems.iter().collect(), generics, is_ref) { + return false; + } } - } - } else if let syn::Type::Path(p_arg) = arg { - write_path!(p_arg, None); - } else if let syn::Type::Reference(refty) = arg { - if let syn::Type::Path(p_arg) = &*refty.elem { + }, + syn::Type::Path(p_arg) => { write_path!(p_arg, None); - } else if let syn::Type::Slice(_) = &*refty.elem { - // write_c_type will actually do exactly what we want here, we just need to - // make it a pointer so that its an option. Note that we cannot always convert - // the Vec-as-slice (ie non-ref types) containers, so sometimes need to be able - // to edit it, hence we use *mut here instead of *const. - if args.len() != 1 { return false; } - write!(w, "*mut ").unwrap(); - self.write_c_type(w, arg, None, true); - } else { return false; } - } else if let syn::Type::Array(a) = arg { - if let syn::Type::Path(p_arg) = &*a.elem { - let resolved = self.resolve_path(&p_arg.path, generics); - if !self.is_primitive(&resolved) { return false; } - if let syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Int(len), .. }) = &a.len { - if self.c_type_from_path(&format!("[{}; {}]", resolved, len.base10_digits()), is_ref, ptr_for_ref).is_none() { return false; } - write!(w, "_{}{}", resolved, len.base10_digits()).unwrap(); - write!(mangled_type, "_{}{}", resolved, len.base10_digits()).unwrap(); + }, + syn::Type::Reference(refty) => { + if let syn::Type::Path(p_arg) = &*refty.elem { + write_path!(p_arg, None); + } else if let syn::Type::Slice(_) = &*refty.elem { + // write_c_type will actually do exactly what we want here, we just need to + // make it a pointer so that its an option. Note that we cannot always convert + // the Vec-as-slice (ie non-ref types) containers, so sometimes need to be able + // to edit it, hence we use *mut here instead of *const. + if args.len() != 1 { return false; } + write!(w, "*mut ").unwrap(); + self.write_c_type(w, arg, None, true); + } else { return false; } + }, + syn::Type::Array(a) => { + if let syn::Type::Path(p_arg) = &*a.elem { + let resolved = self.resolve_path(&p_arg.path, generics); + if !self.is_primitive(&resolved) { return false; } + if let syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Int(len), .. }) = &a.len { + if self.c_type_from_path(&format!("[{}; {}]", resolved, len.base10_digits()), is_ref, ptr_for_ref).is_none() { return false; } + write!(w, "_{}{}", resolved, len.base10_digits()).unwrap(); + write!(mangled_type, "_{}{}", resolved, len.base10_digits()).unwrap(); + } else { return false; } } else { return false; } - } else { return false; } - } else { return false; } + }, + _ => { return false; }, + } } if self.is_transparent_container(ident, is_ref, args.iter().map(|a| *a)) { return true; } // Push the "end of type" Z @@ -2580,6 +2651,20 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { } else { return false; }; write!(w, "{}::{}", Self::generated_container_path(), mangled_container).unwrap(); self.check_create_container(mangled_container, "Vec", vec![&*r.elem], generics, false) + } else if let syn::Type::Slice(sl2) = &*r.elem { + if let syn::Type::Reference(r2) = &*sl2.elem { + if let syn::Type::Path(p) = &*r2.elem { + // Slices with slices with opaque types (with is_owned flags) are mapped as non-ref Vecs + let resolved = self.resolve_path(&p.path, generics); + let mangled_container = if let Some(ident) = self.crate_types.opaques.get(&resolved) { + format!("CVec_CVec_{}ZZ", ident) + } else { return false; }; + write!(w, "{}::{}", Self::generated_container_path(), mangled_container).unwrap(); + let inner = &r2.elem; + let vec_ty: syn::Type = syn::parse_quote!(Vec<#inner>); + self.check_create_container(mangled_container, "Vec", vec![&vec_ty], generics, false) + } else { false } + } else { false } } else { false } } else if let syn::Type::Tuple(_) = &*s.elem { let mut args = syn::punctuated::Punctuated::<_, syn::token::Comma>::new();