(Partially) support supertraits with generic bounds
[ldk-c-bindings] / c-bindings-gen / src / types.rs
index 1a737d17cf11203ef1720015164d75ed7e3dd060..ecc508dbca8d26ff0ab7ec92df486562e39095d6 100644 (file)
@@ -147,7 +147,7 @@ pub fn export_status(attrs: &[syn::Attribute]) -> ExportStatus {
 }
 
 pub fn assert_simple_bound(bound: &syn::TraitBound) {
-       if bound.paren_token.is_some() || bound.lifetimes.is_some() { unimplemented!(); }
+       if bound.paren_token.is_some() { unimplemented!(); }
        if let syn::TraitBoundModifier::Maybe(_) = bound.modifier { unimplemented!(); }
 }
 
@@ -205,10 +205,10 @@ impl<'a, 'p: 'a> GenericTypes<'a, 'p> {
        }
 
        /// Learn the generics in generics in the current context, given a TypeResolver.
-       pub fn learn_generics<'b, 'c>(&mut self, generics: &'a syn::Generics, types: &'b TypeResolver<'a, 'c>) -> bool {
+       pub fn learn_generics_with_impls<'b, 'c>(&mut self, generics: &'a syn::Generics, impld_generics: &'a syn::PathArguments, types: &'b TypeResolver<'a, 'c>) -> bool {
                let mut new_typed_generics = HashMap::new();
                // First learn simple generics...
-               for generic in generics.params.iter() {
+               for (idx, generic) in generics.params.iter().enumerate() {
                        match generic {
                                syn::GenericParam::Type(type_param) => {
                                        let mut non_lifetimes_processed = false;
@@ -260,6 +260,15 @@ impl<'a, 'p: 'a> GenericTypes<'a, 'p> {
                                        if let Some(default) = type_param.default.as_ref() {
                                                assert!(type_param.bounds.is_empty());
                                                self.default_generics.insert(&type_param.ident, (default.clone(), parse_quote!(&#default), parse_quote!(&mut #default)));
+                                       } else if type_param.bounds.is_empty() {
+                                               if let syn::PathArguments::AngleBracketed(args) = impld_generics {
+                                                       match &args.args[idx] {
+                                                               syn::GenericArgument::Type(ty) => {
+                                                                       self.default_generics.insert(&type_param.ident, (ty.clone(), parse_quote!(&#ty), parse_quote!(&mut #ty)));
+                                                               }
+                                                               _ => unimplemented!(),
+                                                       }
+                                               }
                                        }
                                },
                                _ => {},
@@ -270,6 +279,7 @@ impl<'a, 'p: 'a> GenericTypes<'a, 'p> {
                        for pred in wh.predicates.iter() {
                                if let syn::WherePredicate::Type(t) = pred {
                                        if let syn::Type::Path(p) = &t.bounded_ty {
+                                               if first_seg_self(&t.bounded_ty).is_some() && p.path.segments.len() == 1 { continue; }
                                                if p.qself.is_some() { return false; }
                                                if p.path.leading_colon.is_some() { return false; }
                                                let mut p_iter = p.path.segments.iter();
@@ -315,6 +325,11 @@ impl<'a, 'p: 'a> GenericTypes<'a, 'p> {
                true
        }
 
+       /// Learn the generics in generics in the current context, given a TypeResolver.
+       pub fn learn_generics<'b, 'c>(&mut self, generics: &'a syn::Generics, types: &'b TypeResolver<'a, 'c>) -> bool {
+               self.learn_generics_with_impls(generics, &syn::PathArguments::None, types)
+       }
+
        /// Learn the associated types from the trait in the current context.
        pub fn learn_associated_types<'b, 'c>(&mut self, t: &'a syn::ItemTrait, types: &'b TypeResolver<'a, 'c>) {
                for item in t.items.iter() {
@@ -936,6 +951,10 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> {
                        "bitcoin::secp256k1::ecdsa::RecoverableSignature" => Some("crate::c_types::RecoverableSignature"),
                        "bitcoin::secp256k1::SecretKey" if is_ref  => Some("*const [u8; 32]"),
                        "bitcoin::secp256k1::SecretKey" if !is_ref => Some("crate::c_types::SecretKey"),
+                       "bitcoin::secp256k1::Scalar" if is_ref  => Some("*const crate::c_types::BigEndianScalar"),
+                       "bitcoin::secp256k1::Scalar" if !is_ref => Some("crate::c_types::BigEndianScalar"),
+                       "bitcoin::secp256k1::ecdh::SharedSecret" if !is_ref => Some("crate::c_types::ThirtyTwoBytes"),
+
                        "bitcoin::blockdata::script::Script" if is_ref => Some("crate::c_types::u8slice"),
                        "bitcoin::blockdata::script::Script" if !is_ref => Some("crate::c_types::derived::CVec_u8Z"),
                        "bitcoin::blockdata::transaction::OutPoint" => Some("crate::lightning::chain::transaction::OutPoint"),
@@ -1021,6 +1040,9 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> {
                        "bitcoin::secp256k1::ecdsa::RecoverableSignature" => Some(""),
                        "bitcoin::secp256k1::SecretKey" if is_ref => Some("&::bitcoin::secp256k1::SecretKey::from_slice(&unsafe { *"),
                        "bitcoin::secp256k1::SecretKey" if !is_ref => Some(""),
+                       "bitcoin::secp256k1::Scalar" if !is_ref => Some(""),
+                       "bitcoin::secp256k1::ecdh::SharedSecret" if !is_ref => Some("::bitcoin::secp256k1::ecdh::SharedSecret::from_bytes("),
+
                        "bitcoin::blockdata::script::Script" if is_ref => Some("&::bitcoin::blockdata::script::Script::from(Vec::from("),
                        "bitcoin::blockdata::script::Script" if !is_ref => Some("::bitcoin::blockdata::script::Script::from("),
                        "bitcoin::blockdata::transaction::Transaction"|"bitcoin::Transaction" if is_ref => Some("&"),
@@ -1104,6 +1126,9 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> {
                        "bitcoin::secp256k1::ecdsa::RecoverableSignature" => Some(".into_rust()"),
                        "bitcoin::secp256k1::SecretKey" if !is_ref => Some(".into_rust()"),
                        "bitcoin::secp256k1::SecretKey" if is_ref => Some("}[..]).unwrap()"),
+                       "bitcoin::secp256k1::Scalar" if !is_ref => Some(".into_rust()"),
+                       "bitcoin::secp256k1::ecdh::SharedSecret" if !is_ref => Some(".data)"),
+
                        "bitcoin::blockdata::script::Script" if is_ref => Some(".to_slice()))"),
                        "bitcoin::blockdata::script::Script" if !is_ref => Some(".into_rust())"),
                        "bitcoin::blockdata::transaction::Transaction"|"bitcoin::Transaction" => Some(".into_bitcoin()"),
@@ -1196,6 +1221,9 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> {
                        "bitcoin::secp256k1::ecdsa::RecoverableSignature" => Some("crate::c_types::RecoverableSignature::from_rust(&"),
                        "bitcoin::secp256k1::SecretKey" if is_ref => Some(""),
                        "bitcoin::secp256k1::SecretKey" if !is_ref => Some("crate::c_types::SecretKey::from_rust("),
+                       "bitcoin::secp256k1::Scalar" if !is_ref => Some("crate::c_types::BigEndianScalar::from_rust("),
+                       "bitcoin::secp256k1::ecdh::SharedSecret" if !is_ref => Some("crate::c_types::ThirtyTwoBytes { data: "),
+
                        "bitcoin::blockdata::script::Script" if is_ref => Some("crate::c_types::u8slice::from_slice(&"),
                        "bitcoin::blockdata::script::Script" if !is_ref => Some(""),
                        "bitcoin::blockdata::transaction::Transaction"|"bitcoin::Transaction" if is_ref => Some("crate::c_types::Transaction::from_bitcoin("),
@@ -1273,6 +1301,9 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> {
                        "bitcoin::secp256k1::ecdsa::RecoverableSignature" => Some(")"),
                        "bitcoin::secp256k1::SecretKey" if !is_ref => Some(")"),
                        "bitcoin::secp256k1::SecretKey" if is_ref => Some(".as_ref()"),
+                       "bitcoin::secp256k1::Scalar" if !is_ref => Some(")"),
+                       "bitcoin::secp256k1::ecdh::SharedSecret" if !is_ref => Some(".secret_bytes() }"),
+
                        "bitcoin::blockdata::script::Script" if is_ref => Some("[..])"),
                        "bitcoin::blockdata::script::Script" if !is_ref => Some(".into_bytes().into()"),
                        "bitcoin::blockdata::transaction::Transaction"|"bitcoin::Transaction" => Some(")"),
@@ -1381,7 +1412,19 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> {
                                        if let Some(resolved) = self.maybe_resolve_path(&p.path, generics) {
                                                if self.c_type_has_inner_from_path(&resolved) { return true; }
                                                if self.is_primitive(&resolved) { return false; }
-                                               if self.c_type_from_path(&resolved, false, false).is_some() { true } else { false }
+                                               // We want to move to using `Option_` mappings where possible rather than
+                                               // manual mappings, as it makes downstream bindings simpler and is more
+                                               // clear for users. Thus, we default to false but override for a few
+                                               // types which had mappings defined when we were avoiding the `Option_`s.
+                                               match &resolved as &str {
+                                                       "lightning::ln::PaymentSecret" => true,
+                                                       "lightning::ln::PaymentHash" => true,
+                                                       "lightning::ln::PaymentPreimage" => true,
+                                                       "lightning::ln::channelmanager::PaymentId" => true,
+                                                       "bitcoin::hash_types::BlockHash" => true,
+                                                       "secp256k1::PublicKey"|"bitcoin::secp256k1::PublicKey" => true,
+                                                       _ => false,
+                                               }
                                        } else { unimplemented!(); }
                                },
                                syn::Type::Tuple(_) => false,
@@ -1456,7 +1499,7 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> {
                                                                (".is_none() { core::ptr::null_mut() } else { ".to_owned(), format!("({}.unwrap())", var_access))
                                                                ], " }", ContainerPrefixLocation::OutsideConv));
                                                }
-                                       } else if self.is_primitive(&inner_path) || self.c_type_from_path(&inner_path, false, false).is_none() {
+                                       } else if !self.is_transparent_container("Option", is_ref, [single_contained.unwrap()].iter().map(|a| *a), generics) {
                                                if self.is_primitive(&inner_path) || (!is_contained_ref && !is_ref) || only_contained_has_inner {
                                                        let inner_name = self.get_c_mangled_container_type(vec![single_contained.unwrap()], generics, "Option").unwrap();
                                                        return Some(("if ", vec![
@@ -1477,12 +1520,18 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> {
                                }
                                if let Some(t) = single_contained {
                                        if let syn::Type::Tuple(syn::TypeTuple { elems, .. }) = t {
-                                               assert!(elems.is_empty());
                                                let inner_name = self.get_c_mangled_container_type(vec![single_contained.unwrap()], generics, "Option").unwrap();
-                                               return Some(("if ", vec![
-                                                       (format!(".is_none() {{ {}::None }} else {{ {}::Some /*",
-                                                               inner_name, inner_name), format!(""))
-                                                       ], " */}", ContainerPrefixLocation::PerConv));
+                                               if elems.is_empty() {
+                                                       return Some(("if ", vec![
+                                                               (format!(".is_none() {{ {}::None }} else {{ {}::Some /* ",
+                                                                       inner_name, inner_name), format!(""))
+                                                               ], " */ }", ContainerPrefixLocation::PerConv));
+                                               } else {
+                                                       return Some(("if ", vec![
+                                                               (format!(".is_none() {{ {}::None }} else {{ {}::Some(",
+                                                                       inner_name, inner_name), format!("({}.unwrap())", var_access))
+                                                               ], ") }", ContainerPrefixLocation::PerConv));
+                                               }
                                        }
                                        if let syn::Type::Reference(syn::TypeReference { elem, .. }) = t {
                                                if let syn::Type::Slice(_) = &**elem {
@@ -2039,6 +2088,8 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> {
                                                        write!(w, "{}", sliceconv(false, None)).unwrap();
                                                }
                                        }
+                               } else if let syn::Type::Array(_) = &*s.elem {
+                                       write!(w, "{}", sliceconv(false, Some(".map(|a| *a)"))).unwrap();
                                } else { unimplemented!(); }
                        },
                        syn::Type::Tuple(t) => {
@@ -2391,6 +2442,12 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> {
                                        ptr_for_ref = true;
                                        convert_container!("Slice", 1, || ty.iter());
                                        unimplemented!("convert_container should return true as container_lookup should succeed for slices");
+                               } else if let syn::Type::Array(_) = &*s.elem {
+                                       is_ref = false;
+                                       ptr_for_ref = true;
+                                       let arr_elem = [(*s.elem).clone()];
+                                       convert_container!("Slice", 1, || arr_elem.iter());
+                                       unimplemented!("convert_container should return true as container_lookup should succeed for slices");
                                } else { unimplemented!() }
                        },
                        syn::Type::Tuple(t) => {
@@ -2901,6 +2958,22 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> {
                                        let mut segments = syn::punctuated::Punctuated::new();
                                        segments.push(parse_quote!(Vec<#args>));
                                        self.write_c_type_intern(w, &syn::Type::Path(syn::TypePath { qself: None, path: syn::Path { leading_colon: None, segments } }), generics, false, is_mut, ptr_for_ref, with_ref_lifetime, c_ty)
+                               } else if let syn::Type::Array(a) = &*s.elem {
+                                       if let syn::Expr::Lit(l) = &a.len {
+                                               if let syn::Lit::Int(i) = &l.lit {
+                                                       let mut buf = Vec::new();
+                                                       self.write_rust_type(&mut buf, generics, &*a.elem, false);
+                                                       let arr_ty = String::from_utf8(buf).unwrap();
+
+                                                       let arr_str = format!("[{}; {}]", arr_ty, i.base10_digits());
+                                                       let ty = self.c_type_from_path(&arr_str, false, ptr_for_ref).unwrap()
+                                                               .rsplitn(2, "::").next().unwrap();
+
+                                                       let mangled_container = format!("CVec_{}Z", ty);
+                                                       write!(w, "{}::{}", Self::generated_container_path(), mangled_container).unwrap();
+                                                       self.check_create_container(mangled_container, "Vec", vec![&*s.elem], generics, false)
+                                               } else { false }
+                                       } else { false }
                                } else { false }
                        },
                        syn::Type::Tuple(t) => {