X-Git-Url: http://git.bitcoin.ninja/index.cgi?p=ldk-c-bindings;a=blobdiff_plain;f=c-bindings-gen%2Fsrc%2Ftypes.rs;h=13c3b0ed0c2b7b3b00b4af8e58728a117614d6cb;hp=d945431199eab2ba51a9fffac3498f0bd3d5582c;hb=a82e075188fc15a103234832686915c196bfe240;hpb=1926a7a71ae0f37ebd6562996769334e0af0cf1b diff --git a/c-bindings-gen/src/types.rs b/c-bindings-gen/src/types.rs index d945431..13c3b0e 100644 --- a/c-bindings-gen/src/types.rs +++ b/c-bindings-gen/src/types.rs @@ -66,6 +66,11 @@ pub enum ExportStatus { Export, NoExport, TestOnly, + /// This is used only for traits to indicate that users should not be able to implement their + /// own version of a trait, but we should export Rust implementations of the trait (and the + /// trait itself). + /// Concretly, this means that we do not implement the Rust trait for the C trait struct. + NotImplementable, } /// Gets the ExportStatus of an object (struct, fn, etc) given its attributes. pub fn export_status(attrs: &[syn::Attribute]) -> ExportStatus { @@ -117,6 +122,8 @@ pub fn export_status(attrs: &[syn::Attribute]) -> ExportStatus { let line = format!("{}", lit); if line.contains("(C-not exported)") { return ExportStatus::NoExport; + } else if line.contains("(C-not implementable)") { + return ExportStatus::NotImplementable; } }, _ => unimplemented!(), @@ -138,6 +145,7 @@ pub fn is_enum_opaque(e: &syn::ItemEnum) -> bool { for field in fields.named.iter() { match export_status(&field.attrs) { ExportStatus::Export|ExportStatus::TestOnly => {}, + ExportStatus::NotImplementable => panic!("(C-not implementable) should only appear on traits!"), ExportStatus::NoExport => return true, } } @@ -145,6 +153,7 @@ pub fn is_enum_opaque(e: &syn::ItemEnum) -> bool { for field in fields.unnamed.iter() { match export_status(&field.attrs) { ExportStatus::Export|ExportStatus::TestOnly => {}, + ExportStatus::NotImplementable => panic!("(C-not implementable) should only appear on traits!"), ExportStatus::NoExport => return true, } } @@ -167,7 +176,7 @@ pub struct GenericTypes<'a, 'b> { self_ty: Option<(String, &'a syn::Path)>, parent: Option<&'b GenericTypes<'b, 'b>>, typed_generics: HashMap<&'a syn::Ident, (String, Option<&'a syn::Path>)>, - default_generics: HashMap<&'a syn::Ident, (&'a syn::Type, syn::Type)>, + default_generics: HashMap<&'a syn::Ident, (syn::Type, syn::Type)>, } impl<'a, 'p: 'a> GenericTypes<'a, 'p> { pub fn new(self_ty: Option<(String, &'a syn::Path)>) -> Self { @@ -188,7 +197,7 @@ impl<'a, 'p: 'a> GenericTypes<'a, 'p> { match generic { syn::GenericParam::Type(type_param) => { let mut non_lifetimes_processed = false; - for bound in type_param.bounds.iter() { + 'bound_loop: for bound in type_param.bounds.iter() { if let syn::TypeParamBound::Trait(trait_bound) = bound { if let Some(ident) = single_ident_generic_path_to_ident(&trait_bound.path) { match &format!("{}", ident) as &str { "Send" => continue, "Sync" => continue, _ => {} } @@ -201,9 +210,29 @@ impl<'a, 'p: 'a> GenericTypes<'a, 'p> { if path == "Sized" { continue; } if non_lifetimes_processed { return false; } non_lifetimes_processed = true; - let new_ident = if path != "std::ops::Deref" { + let new_ident = if path != "std::ops::Deref" && path != "core::ops::Deref" { path = "crate::".to_string() + &path; Some(&trait_bound.path) + } else if trait_bound.path.segments.len() == 1 { + // If we're templated on Deref, store + // the reference type in `default_generics` which handles full + // types and not just paths. + if let syn::PathArguments::AngleBracketed(ref args) = + trait_bound.path.segments[0].arguments { + for subargument in args.args.iter() { + match subargument { + syn::GenericArgument::Lifetime(_) => {}, + syn::GenericArgument::Binding(ref b) => { + if &format!("{}", b.ident) != "Target" { return false; } + let default = &b.ty; + self.default_generics.insert(&type_param.ident, (parse_quote!(&#default), parse_quote!(&#default))); + break 'bound_loop; + }, + _ => unimplemented!(), + } + } + None + } else { None } } else { None }; self.typed_generics.insert(&type_param.ident, (path, new_ident)); } else { return false; } @@ -211,7 +240,7 @@ 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, parse_quote!(&#default))); + self.default_generics.insert(&type_param.ident, (default.clone(), parse_quote!(&#default))); } }, _ => {}, @@ -226,7 +255,7 @@ impl<'a, 'p: 'a> GenericTypes<'a, 'p> { if p.path.leading_colon.is_some() { return false; } let mut p_iter = p.path.segments.iter(); if let Some(gen) = self.typed_generics.get_mut(&p_iter.next().unwrap().ident) { - if gen.0 != "std::ops::Deref" { return false; } + if gen.0 != "std::ops::Deref" && gen.0 != "core::ops::Deref" { return false; } if &format!("{}", p_iter.next().unwrap().ident) != "Target" { return false; } let mut non_lifetimes_processed = false; @@ -269,7 +298,7 @@ impl<'a, 'p: 'a> GenericTypes<'a, 'p> { // implement Deref for relevant types). We don't // bother to implement it for associated types, however, so we just // ignore such bounds. - let new_ident = if path != "std::ops::Deref" { + let new_ident = if path != "std::ops::Deref" && path != "core::ops::Deref" { path = "crate::".to_string() + &path; Some(&tr.path) } else { None }; @@ -479,6 +508,7 @@ impl<'mod_lifetime, 'crate_lft: 'mod_lifetime> ImportResolver<'mod_lifetime, 'cr ExportStatus::Export => { declared.insert(s.ident.clone(), DeclType::StructImported); }, ExportStatus::NoExport => { declared.insert(s.ident.clone(), DeclType::StructIgnored); }, ExportStatus::TestOnly => continue, + ExportStatus::NotImplementable => panic!("(C-not implementable) should only appear on traits!"), } } }, @@ -499,13 +529,19 @@ impl<'mod_lifetime, 'crate_lft: 'mod_lifetime> ImportResolver<'mod_lifetime, 'cr match export_status(&e.attrs) { ExportStatus::Export if is_enum_opaque(e) => { declared.insert(e.ident.clone(), DeclType::EnumIgnored); }, ExportStatus::Export => { declared.insert(e.ident.clone(), DeclType::MirroredEnum); }, + ExportStatus::NotImplementable => panic!("(C-not implementable) should only appear on traits!"), _ => continue, } } }, - syn::Item::Trait(t) if export_status(&t.attrs) == ExportStatus::Export => { - if let syn::Visibility::Public(_) = t.vis { - declared.insert(t.ident.clone(), DeclType::Trait(t)); + syn::Item::Trait(t) => { + match export_status(&t.attrs) { + ExportStatus::Export|ExportStatus::NotImplementable => { + if let syn::Visibility::Public(_) = t.vis { + declared.insert(t.ident.clone(), DeclType::Trait(t)); + } + }, + _ => continue, } }, syn::Item::Mod(m) => { @@ -687,6 +723,14 @@ impl FullLibraryAST { fn initial_clonable_types() -> HashSet { let mut res = HashSet::new(); res.insert("crate::c_types::u5".to_owned()); + res.insert("crate::c_types::ThirtyTwoBytes".to_owned()); + res.insert("crate::c_types::PublicKey".to_owned()); + res.insert("crate::c_types::Transaction".to_owned()); + res.insert("crate::c_types::TxOut".to_owned()); + res.insert("crate::c_types::Signature".to_owned()); + res.insert("crate::c_types::RecoverableSignature".to_owned()); + res.insert("crate::c_types::Secp256k1Error".to_owned()); + res.insert("crate::c_types::IOError".to_owned()); res } @@ -814,9 +858,6 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { if self.is_primitive(ty) { return true; } match ty { "()" => true, - "crate::c_types::Signature" => true, - "crate::c_types::RecoverableSignature" => true, - "crate::c_types::TxOut" => true, _ => false, } } @@ -842,9 +883,9 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { "[u8; 3]" if !is_ref => Some("crate::c_types::ThreeBytes"), // Used for RGB values "str" if is_ref => Some("crate::c_types::Str"), - "String" => Some("crate::c_types::Str"), + "alloc::string::String"|"String" => Some("crate::c_types::Str"), - "std::time::Duration" => Some("u64"), + "std::time::Duration"|"core::time::Duration" => Some("u64"), "std::time::SystemTime" => Some("u64"), "std::io::Error" => Some("crate::c_types::IOError"), @@ -879,8 +920,7 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { "lightning::ln::PaymentHash" if !is_ref => Some("crate::c_types::ThirtyTwoBytes"), "lightning::ln::PaymentPreimage" if is_ref => Some("*const [u8; 32]"), "lightning::ln::PaymentPreimage" if !is_ref => Some("crate::c_types::ThirtyTwoBytes"), - "lightning::ln::PaymentSecret" if is_ref => Some("crate::c_types::ThirtyTwoBytes"), - "lightning::ln::PaymentSecret" if !is_ref => Some("crate::c_types::ThirtyTwoBytes"), + "lightning::ln::PaymentSecret" => Some("crate::c_types::ThirtyTwoBytes"), // Override the default since Records contain an fmt with a lifetime: "lightning::util::logger::Record" => Some("*const std::os::raw::c_char"), @@ -914,11 +954,12 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { "[usize]" if is_ref => Some(""), "str" if is_ref => Some(""), - "String" => Some(""), + "alloc::string::String"|"String" => Some(""), + "std::io::Error" if !is_ref => Some(""), // Note that we'll panic for String if is_ref, as we only have non-owned memory, we // cannot create a &String. - "std::time::Duration" => Some("std::time::Duration::from_secs("), + "std::time::Duration"|"core::time::Duration" => Some("std::time::Duration::from_secs("), "std::time::SystemTime" => Some("(::std::time::SystemTime::UNIX_EPOCH + std::time::Duration::from_secs("), "bech32::u5" => Some(""), @@ -980,9 +1021,10 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { "[usize]" if is_ref => Some(".to_slice()"), "str" if is_ref => Some(".into_str()"), - "String" => Some(".into_string()"), + "alloc::string::String"|"String" => Some(".into_string()"), + "std::io::Error" if !is_ref => Some(".to_rust()"), - "std::time::Duration" => Some(")"), + "std::time::Duration"|"core::time::Duration" => Some(")"), "std::time::SystemTime" => Some("))"), "bech32::u5" => Some(".into()"), @@ -1059,9 +1101,9 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { "[usize]" if is_ref => Some("local_"), "str" if is_ref => Some(""), - "String" => Some(""), + "alloc::string::String"|"String" => Some(""), - "std::time::Duration" => Some(""), + "std::time::Duration"|"core::time::Duration" => Some(""), "std::time::SystemTime" => Some(""), "std::io::Error" if !is_ref => Some("crate::c_types::IOError::from_rust("), @@ -1099,7 +1141,7 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { "lightning::ln::PaymentHash" if !is_ref => Some("crate::c_types::ThirtyTwoBytes { data: "), "lightning::ln::PaymentPreimage" if is_ref => Some("&"), "lightning::ln::PaymentPreimage" => Some("crate::c_types::ThirtyTwoBytes { data: "), - "lightning::ln::PaymentSecret" if !is_ref => Some("crate::c_types::ThirtyTwoBytes { data: "), + "lightning::ln::PaymentSecret" => Some("crate::c_types::ThirtyTwoBytes { data: "), // Override the default since Records contain an fmt with a lifetime: "lightning::util::logger::Record" => Some("local_"), @@ -1128,10 +1170,10 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { "[usize]" if is_ref => Some(""), "str" if is_ref => Some(".into()"), - "String" if is_ref => Some(".as_str().into()"), - "String" => Some(".into()"), + "alloc::string::String"|"String" if is_ref => Some(".as_str().into()"), + "alloc::string::String"|"String" => Some(".into()"), - "std::time::Duration" => Some(".as_secs()"), + "std::time::Duration"|"core::time::Duration" => Some(".as_secs()"), "std::time::SystemTime" => Some(".duration_since(::std::time::SystemTime::UNIX_EPOCH).expect(\"Times must be post-1970\").as_secs()"), "std::io::Error" if !is_ref => Some(")"), @@ -1168,7 +1210,7 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { "lightning::ln::PaymentHash" => Some(".0 }"), "lightning::ln::PaymentPreimage" if is_ref => Some(".0"), "lightning::ln::PaymentPreimage" => Some(".0 }"), - "lightning::ln::PaymentSecret" if !is_ref => Some(".0 }"), + "lightning::ln::PaymentSecret" => Some(".0 }"), // Override the default since Records contain an fmt with a lifetime: "lightning::util::logger::Record" => Some(".as_ptr()"), @@ -1221,7 +1263,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| { @@ -1248,29 +1290,36 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { (").into(), Err(mut e) => crate::c_types::CResultTempl::err(".to_string(), "e".to_string())], ").into() }", ContainerPrefixLocation::PerConv)) }, - "Vec" if !is_ref => { - Some(("Vec::new(); for mut item in ", vec![(format!(".drain(..) {{ local_{}.push(", var_name), "item".to_string())], "); }", ContainerPrefixLocation::PerConv)) - }, "Vec" => { - // We should only get here if the single contained has an inner - assert!(self.c_type_has_inner(single_contained.unwrap())); - Some(("Vec::new(); for mut item in ", vec![(format!(".drain(..) {{ local_{}.push(", var_name), "*item".to_string())], "); }", ContainerPrefixLocation::PerConv)) + if is_ref { + // We should only get here if the single contained has an inner + assert!(self.c_type_has_inner(single_contained.unwrap())); + } + Some(("Vec::new(); for mut item in ", vec![(format!(".drain(..) {{ local_{}.push(", var_name), "item".to_string())], "); }", ContainerPrefixLocation::PerConv)) }, "Slice" => { - Some(("Vec::new(); for item in ", vec![(format!(".iter() {{ local_{}.push(", var_name), "*item".to_string())], "); }", ContainerPrefixLocation::PerConv)) + Some(("Vec::new(); for item in ", vec![(format!(".iter() {{ local_{}.push(", var_name), "item".to_string())], "); }", ContainerPrefixLocation::PerConv)) }, "Option" => { - if let Some(syn::Type::Path(p)) = single_contained { - let inner_path = self.resolve_path(&p.path, generics); + let contained_struct = if let Some(syn::Type::Path(p)) = single_contained { + Some(self.resolve_path(&p.path, generics)) + } else if let Some(syn::Type::Reference(r)) = single_contained { + if let syn::Type::Path(p) = &*r.elem { + Some(self.resolve_path(&p.path, generics)) + } else { None } + } else { None }; + if let Some(inner_path) = contained_struct { if self.is_primitive(&inner_path) { return Some(("if ", vec![ (format!(".is_none() {{ {}::COption_{}Z::None }} else {{ ", Self::generated_container_path(), inner_path), format!("{}::COption_{}Z::Some({}.unwrap())", Self::generated_container_path(), inner_path, var_access)) ], " }", ContainerPrefixLocation::NoPrefix)); } else if self.c_type_has_inner_from_path(&inner_path) { + 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(), format!("({}.as_ref().unwrap())", var_access)) + (".is_none() { std::ptr::null() } else { ".to_owned(), + format!("({}{}.unwrap())", var_access, if is_inner_ref { "" } else { ".as_ref()" })) ], " }", ContainerPrefixLocation::OutsideConv)); } else { return Some(("if ", vec![ @@ -1379,6 +1428,9 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { let full_path = self.resolve_path(&p.path, None); self.c_type_has_inner_from_path(&full_path) }, + syn::Type::Reference(r) => { + self.c_type_has_inner(&*r.elem) + }, _ => false, } } @@ -1531,6 +1583,9 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { /// unint'd memory). pub fn write_empty_rust_val(&self, generics: Option<&GenericTypes>, w: &mut W, t: &syn::Type) { match t { + syn::Type::Reference(r) => { + self.write_empty_rust_val(generics, w, &*r.elem) + }, syn::Type::Path(p) => { let resolved = self.resolve_path(&p.path, generics); if self.crate_types.opaques.get(&resolved).is_some() { @@ -1576,6 +1631,9 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { /// See EmptyValExpectedTy for information on return types. fn write_empty_rust_val_check_suffix(&self, generics: Option<&GenericTypes>, w: &mut W, t: &syn::Type) -> EmptyValExpectedTy { match t { + syn::Type::Reference(r) => { + return self.write_empty_rust_val_check_suffix(generics, w, &*r.elem); + }, syn::Type::Path(p) => { let resolved = self.resolve_path(&p.path, generics); if let Some(arr_ty) = self.is_real_type_array(&resolved) { @@ -1617,6 +1675,9 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { /// Prints a suffix to determine if a variable is empty (ie was set by write_empty_rust_val). pub fn write_empty_rust_val_check(&self, generics: Option<&GenericTypes>, w: &mut W, t: &syn::Type, var_access: &str) { match t { + syn::Type::Reference(r) => { + self.write_empty_rust_val_check(generics, w, &*r.elem, var_access); + }, syn::Type::Path(_) => { write!(w, "{}", var_access).unwrap(); self.write_empty_rust_val_check_suffix(generics, w, t); @@ -1934,13 +1995,15 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { // pretty manual here and most of the below special-cases are for Options. let mut needs_ref_map = false; let mut only_contained_type = None; + let mut only_contained_type_nonref = None; let mut only_contained_has_inner = false; let mut contains_slice = false; if $args_len == 1 { only_contained_has_inner = ty_has_inner; let arg = $args_iter().next().unwrap(); if let syn::Type::Reference(t) = arg { - only_contained_type = Some(&*t.elem); + only_contained_type = Some(arg); + only_contained_type_nonref = Some(&*t.elem); if let syn::Type::Path(_) = &*t.elem { is_ref = true; } else if let syn::Type::Slice(_) = &*t.elem { @@ -1951,7 +2014,8 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { // do an extra mapping step. needs_ref_map = !only_contained_has_inner; } else { - only_contained_type = Some(&arg); + only_contained_type = Some(arg); + only_contained_type_nonref = Some(arg); } } @@ -1968,7 +2032,7 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { write!(&mut var, "{}", var_name).unwrap(); let var_access = String::from_utf8(var.into_inner()).unwrap(); - let conv_ty = if needs_ref_map { only_contained_type.as_ref().unwrap() } else { ty }; + let conv_ty = if needs_ref_map { only_contained_type_nonref.as_ref().unwrap() } else { ty }; write!(w, "{} {{ ", pfx).unwrap(); let new_var_name = format!("{}_{}", ident, idx);