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 {
let line = format!("{}", lit);
if line.contains("(C-not exported)") {
return ExportStatus::NoExport;
+ } else if line.contains("(C-not implementable)") {
+ return ExportStatus::NotImplementable;
}
},
_ => unimplemented!(),
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,
}
}
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,
}
}
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, (syn::Type, syn::Type)>,
}
impl<'a, 'p: 'a> GenericTypes<'a, 'p> {
pub fn new(self_ty: Option<(String, &'a syn::Path)>) -> Self {
- Self { self_ty, parent: None, typed_generics: HashMap::new(), }
+ Self { self_ty, parent: None, typed_generics: HashMap::new(), default_generics: HashMap::new(), }
}
/// push a new context onto the stack, allowing for a new set of generics to be learned which
/// will override any lower contexts, but which will still fall back to resoltion via lower
/// contexts.
pub fn push_ctx<'c>(&'c self) -> GenericTypes<'a, 'c> {
- GenericTypes { self_ty: None, parent: Some(self), typed_generics: HashMap::new(), }
+ GenericTypes { self_ty: None, parent: Some(self), typed_generics: HashMap::new(), default_generics: HashMap::new(), }
}
/// Learn the generics in generics in the current context, given a TypeResolver.
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, _ => {} }
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<Target = ConcreteThing>, 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; }
}
}
+ 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)));
+ }
},
_ => {},
}
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;
// implement Deref<Target=Self> 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 };
None
}
}
+
/// Attempt to resolve a Path as a generic parameter and return the full path. as both a string
/// and syn::Path.
pub fn maybe_resolve_path<'b>(&'b self, path: &syn::Path) -> Option<(&'b String, &'a syn::Path)> {
}
}
+trait ResolveType<'a> { fn resolve_type(&'a self, ty: &'a syn::Type) -> &'a syn::Type; }
+impl<'a, 'b, 'c: 'a + 'b> ResolveType<'c> for Option<&GenericTypes<'a, 'b>> {
+ fn resolve_type(&'c self, ty: &'c syn::Type) -> &'c syn::Type {
+ if let Some(us) = self {
+ match ty {
+ syn::Type::Path(p) => {
+ if let Some(ident) = p.path.get_ident() {
+ if let Some((ty, _)) = us.default_generics.get(ident) {
+ return ty;
+ }
+ }
+ },
+ syn::Type::Reference(syn::TypeReference { elem, .. }) => {
+ if let syn::Type::Path(p) = &**elem {
+ if let Some(ident) = p.path.get_ident() {
+ if let Some((_, refty)) = us.default_generics.get(ident) {
+ return refty;
+ }
+ }
+ }
+ }
+ _ => {},
+ }
+ }
+ ty
+ }
+}
+
#[derive(Clone, PartialEq)]
// The type of declaration and the object itself
pub enum DeclType<'a> {
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!"),
}
}
},
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) => {
"[u8; 3]" if !is_ref => Some("crate::c_types::ThreeBytes"), // Used for RGB values
"str" if is_ref => Some("crate::c_types::Str"),
- "String" if !is_ref => Some("crate::c_types::derived::CVec_u8Z"),
- "String" if is_ref => 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"),
"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"),
"[usize]" if is_ref => Some(""),
"str" if is_ref => Some(""),
- "String" if !is_ref => Some("String::from_utf8("),
+ "alloc::string::String"|"String" => 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(""),
"[u8]" if is_ref => Some(".to_slice()"),
"[usize]" if is_ref => Some(".to_slice()"),
- "str" if is_ref => Some(".into()"),
- "String" if !is_ref => Some(".into_rust()).unwrap()"),
+ "str" if is_ref => Some(".into_str()"),
+ "alloc::string::String"|"String" => Some(".into_string()"),
- "std::time::Duration" => Some(")"),
+ "std::time::Duration"|"core::time::Duration" => Some(")"),
"std::time::SystemTime" => Some("))"),
"bech32::u5" => Some(".into()"),
"[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("),
"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_"),
"[usize]" if is_ref => Some(""),
"str" if is_ref => Some(".into()"),
- "String" if !is_ref => Some(".into_bytes().into()"),
- "String" if is_ref => Some(".as_str().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(")"),
"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()"),
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![
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,
}
}
/// unint'd memory).
pub fn write_empty_rust_val<W: std::io::Write>(&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() {
/// See EmptyValExpectedTy for information on return types.
fn write_empty_rust_val_check_suffix<W: std::io::Write>(&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) {
/// 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<W: std::io::Write>(&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);
LP: Fn(&str, bool, bool) -> Option<String>, DL: Fn(&mut W, &DeclType, &str, bool, bool), SC: Fn(bool, Option<&str>) -> String>
(&self, w: &mut W, t: &syn::Type, generics: Option<&GenericTypes>, is_ref: bool, is_mut: bool, ptr_for_ref: bool,
tupleconv: &str, prefix: bool, sliceconv: SC, path_lookup: LP, decl_lookup: DL) {
- match t {
+ match generics.resolve_type(t) {
syn::Type::Reference(r) => {
self.write_conversion_inline_intern(w, &*r.elem, generics, true, r.mutability.is_some(),
ptr_for_ref, tupleconv, prefix, sliceconv, path_lookup, decl_lookup);
// 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 {
// 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);
}
}
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);
} }
}
- match t {
+ match generics.resolve_type(t) {
syn::Type::Reference(r) => {
if let syn::Type::Slice(_) = &*r.elem {
self.write_conversion_new_var_intern(w, ident, var, &*r.elem, generics, is_ref, ptr_for_ref, to_c, path_lookup, container_lookup, var_prefix, var_suffix)
}
}
fn write_c_type_intern<W: std::io::Write>(&self, w: &mut W, t: &syn::Type, generics: Option<&GenericTypes>, is_ref: bool, is_mut: bool, ptr_for_ref: bool) -> bool {
- match t {
+ match generics.resolve_type(t) {
syn::Type::Path(p) => {
if p.qself.is_some() {
return false;