From 304471b1c185ed25ce8fdefa51fdfc5844caf94e Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Thu, 12 Nov 2020 19:58:51 -0500 Subject: [PATCH] [bindings] Support traits with generic arguments (to support #681) Previously we'd ignored generic arguments in traits, leading to bogus code generation after the Persister trait was added in #681. This adds minimal support for it, fixing code generation on latest upstream. --- c-bindings-gen/src/main.rs | 43 +++++++++++++++++++------------ c-bindings-gen/src/types.rs | 51 ++++++++++++++++++++++++------------- 2 files changed, 60 insertions(+), 34 deletions(-) diff --git a/c-bindings-gen/src/main.rs b/c-bindings-gen/src/main.rs index ecbd947ba..4edad55d1 100644 --- a/c-bindings-gen/src/main.rs +++ b/c-bindings-gen/src/main.rs @@ -148,6 +148,9 @@ fn writeln_trait<'a, 'b, W: std::io::Write>(w: &mut W, t: &'a syn::ItemTrait, ty } writeln_docs(w, &t.attrs, ""); + let mut gen_types = GenericTypes::new(); + assert!(gen_types.learn_generics(&t.generics, types)); + writeln!(w, "#[repr(C)]\npub struct {} {{", trait_name).unwrap(); writeln!(w, "\tpub this_arg: *mut c_void,").unwrap(); let associated_types = learn_associated_types(t); @@ -158,15 +161,17 @@ fn writeln_trait<'a, 'b, W: std::io::Write>(w: &mut W, t: &'a syn::ItemTrait, ty match export_status(&m.attrs) { ExportStatus::NoExport => { // NoExport in this context means we'll hit an unimplemented!() at runtime, - // so add a comment noting that this needs to change in the output. - writeln!(w, "\t//XXX: Need to export {}", m.sig.ident).unwrap(); - continue; + // so bail out. + unimplemented!(); }, ExportStatus::Export => {}, ExportStatus::TestOnly => continue, } if m.default.is_some() { unimplemented!(); } + gen_types.push_ctx(); + assert!(gen_types.learn_generics(&m.sig.generics, types)); + writeln_docs(w, &m.attrs, "\t"); if let syn::ReturnType::Type(_, rtype) = &m.sig.output { @@ -183,7 +188,7 @@ fn writeln_trait<'a, 'b, W: std::io::Write>(w: &mut W, t: &'a syn::ItemTrait, ty // called when the trait method is called which allows updating on the fly. write!(w, "\tpub {}: ", m.sig.ident).unwrap(); generated_fields.push(format!("{}", m.sig.ident)); - types.write_c_type(w, &*r.elem, None, false); + types.write_c_type(w, &*r.elem, Some(&gen_types), false); writeln!(w, ",").unwrap(); writeln!(w, "\t/// Fill in the {} field as a reference to it will be given to Rust after this returns", m.sig.ident).unwrap(); writeln!(w, "\t/// Note that this takes a pointer to this object, not the this_ptr like other methods do").unwrap(); @@ -195,6 +200,7 @@ fn writeln_trait<'a, 'b, W: std::io::Write>(w: &mut W, t: &'a syn::ItemTrait, ty // which does not compile since Thing is not defined before it is used. writeln!(extra_headers, "struct LDK{};", trait_name).unwrap(); writeln!(extra_headers, "typedef struct LDK{} LDK{};", trait_name, trait_name).unwrap(); + gen_types.pop_ctx(); continue; } // Sadly, this currently doesn't do what we want, but it should be easy to get @@ -204,8 +210,10 @@ fn writeln_trait<'a, 'b, W: std::io::Write>(w: &mut W, t: &'a syn::ItemTrait, ty write!(w, "\tpub {}: extern \"C\" fn (", m.sig.ident).unwrap(); generated_fields.push(format!("{}", m.sig.ident)); - write_method_params(w, &m.sig, &associated_types, "c_void", types, None, true, false); + write_method_params(w, &m.sig, &associated_types, "c_void", types, Some(&gen_types), true, false); writeln!(w, ",").unwrap(); + + gen_types.pop_ctx(); }, &syn::TraitItem::Type(_) => {}, _ => unimplemented!(), @@ -284,8 +292,10 @@ fn writeln_trait<'a, 'b, W: std::io::Write>(w: &mut W, t: &'a syn::ItemTrait, ty m.sig.abi.is_some() || m.sig.variadic.is_some() { unimplemented!(); } + gen_types.push_ctx(); + assert!(gen_types.learn_generics(&m.sig.generics, types)); write!(w, "\tfn {}", m.sig.ident).unwrap(); - types.write_rust_generic_param(w, m.sig.generics.params.iter()); + types.write_rust_generic_param(w, Some(&gen_types), m.sig.generics.params.iter()); write!(w, "(").unwrap(); for inp in m.sig.inputs.iter() { match inp { @@ -309,11 +319,11 @@ fn writeln_trait<'a, 'b, W: std::io::Write>(w: &mut W, t: &'a syn::ItemTrait, ty ident.mutability.is_some() || ident.subpat.is_some() { unimplemented!(); } - write!(w, ", {}{}: ", if types.skip_arg(&*arg.ty, None) { "_" } else { "" }, ident.ident).unwrap(); + write!(w, ", {}{}: ", if types.skip_arg(&*arg.ty, Some(&gen_types)) { "_" } else { "" }, ident.ident).unwrap(); } _ => unimplemented!(), } - types.write_rust_type(w, &*arg.ty); + types.write_rust_type(w, Some(&gen_types), &*arg.ty); } } } @@ -321,15 +331,14 @@ fn writeln_trait<'a, 'b, W: std::io::Write>(w: &mut W, t: &'a syn::ItemTrait, ty match &m.sig.output { syn::ReturnType::Type(_, rtype) => { write!(w, " -> ").unwrap(); - types.write_rust_type(w, &*rtype) + types.write_rust_type(w, Some(&gen_types), &*rtype) }, _ => {}, } write!(w, " {{\n\t\t").unwrap(); match export_status(&m.attrs) { ExportStatus::NoExport => { - writeln!(w, "unimplemented!();\n\t}}").unwrap(); - continue; + unimplemented!(); }, _ => {}, } @@ -339,25 +348,27 @@ fn writeln_trait<'a, 'b, W: std::io::Write>(w: &mut W, t: &'a syn::ItemTrait, ty writeln!(w, "if let Some(f) = self.set_{} {{", m.sig.ident).unwrap(); writeln!(w, "\t\t\t(f)(self);").unwrap(); write!(w, "\t\t}}\n\t\t").unwrap(); - types.write_from_c_conversion_to_ref_prefix(w, &*r.elem, None); + types.write_from_c_conversion_to_ref_prefix(w, &*r.elem, Some(&gen_types)); write!(w, "self.{}", m.sig.ident).unwrap(); - types.write_from_c_conversion_to_ref_suffix(w, &*r.elem, None); + types.write_from_c_conversion_to_ref_suffix(w, &*r.elem, Some(&gen_types)); writeln!(w, "\n\t}}").unwrap(); + gen_types.pop_ctx(); continue; } } - write_method_var_decl_body(w, &m.sig, "\t", types, None, true); + write_method_var_decl_body(w, &m.sig, "\t", types, Some(&gen_types), true); write!(w, "(self.{})(", m.sig.ident).unwrap(); - write_method_call_params(w, &m.sig, &associated_types, "\t", types, None, "", true); + write_method_call_params(w, &m.sig, &associated_types, "\t", types, Some(&gen_types), "", true); writeln!(w, "\n\t}}").unwrap(); + gen_types.pop_ctx(); }, &syn::TraitItem::Type(ref t) => { if t.default.is_some() || t.generics.lt_token.is_some() { unimplemented!(); } let mut bounds_iter = t.bounds.iter(); match bounds_iter.next().unwrap() { syn::TypeParamBound::Trait(tr) => { - writeln!(w, "\ttype {} = crate::{};", t.ident, types.resolve_path(&tr.path, None)).unwrap(); + writeln!(w, "\ttype {} = crate::{};", t.ident, types.resolve_path(&tr.path, Some(&gen_types))).unwrap(); }, _ => unimplemented!(), } diff --git a/c-bindings-gen/src/types.rs b/c-bindings-gen/src/types.rs index 3fc35e8b2..483c1d9b9 100644 --- a/c-bindings-gen/src/types.rs +++ b/c-bindings-gen/src/types.rs @@ -932,19 +932,34 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { // *** Original Rust Type Printing *** // *********************************** - fn write_rust_path(&self, w: &mut W, path: &syn::Path) { - if let Some(resolved) = self.maybe_resolve_path(&path, None) { + fn in_rust_prelude(resolved_path: &str) -> bool { + match resolved_path { + "Vec" => true, + "Result" => true, + "Option" => true, + _ => false, + } + } + + fn write_rust_path(&self, w: &mut W, generics_resolver: Option<&GenericTypes>, path: &syn::Path) { + if let Some(resolved) = self.maybe_resolve_path(&path, generics_resolver) { if self.is_primitive(&resolved) { write!(w, "{}", path.get_ident().unwrap()).unwrap(); } else { - if resolved.starts_with("ln::") || resolved.starts_with("chain::") || resolved.starts_with("util::") { - write!(w, "lightning::{}", resolved).unwrap(); + // TODO: We should have a generic "is from a dependency" check here instead of + // checking for "bitcoin" explicitly. + if resolved.starts_with("bitcoin::") || Self::in_rust_prelude(&resolved) { + write!(w, "{}", resolved).unwrap(); + // If we're printing a generic argument, it needs to reference the crate, otherwise + // the original crate: + } else if self.maybe_resolve_path(&path, None).as_ref() == Some(&resolved) { + write!(w, "{}::{}", self.orig_crate, resolved).unwrap(); } else { - write!(w, "{}", resolved).unwrap(); // XXX: Probably doens't work, get_ident().unwrap() + write!(w, "crate::{}", resolved).unwrap(); } } if let syn::PathArguments::AngleBracketed(args) = &path.segments.iter().last().unwrap().arguments { - self.write_rust_generic_arg(w, args.args.iter()); + self.write_rust_generic_arg(w, generics_resolver, args.args.iter()); } } else { if path.leading_colon.is_some() { @@ -954,12 +969,12 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { if idx != 0 { write!(w, "::").unwrap(); } write!(w, "{}", seg.ident).unwrap(); if let syn::PathArguments::AngleBracketed(args) = &seg.arguments { - self.write_rust_generic_arg(w, args.args.iter()); + self.write_rust_generic_arg(w, generics_resolver, args.args.iter()); } } } } - pub fn write_rust_generic_param<'b, W: std::io::Write>(&self, w: &mut W, generics: impl Iterator) { + pub fn write_rust_generic_param<'b, W: std::io::Write>(&self, w: &mut W, generics_resolver: Option<&GenericTypes>, generics: impl Iterator) { let mut had_params = false; for (idx, arg) in generics.enumerate() { if idx != 0 { write!(w, ", ").unwrap(); } else { write!(w, "<").unwrap(); } @@ -974,7 +989,7 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { match bound { syn::TypeParamBound::Trait(tb) => { if tb.paren_token.is_some() || tb.lifetimes.is_some() { unimplemented!(); } - self.write_rust_path(w, &tb.path); + self.write_rust_path(w, generics_resolver, &tb.path); }, _ => unimplemented!(), } @@ -987,24 +1002,24 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { if had_params { write!(w, ">").unwrap(); } } - pub fn write_rust_generic_arg<'b, W: std::io::Write>(&self, w: &mut W, generics: impl Iterator) { + pub fn write_rust_generic_arg<'b, W: std::io::Write>(&self, w: &mut W, generics_resolver: Option<&GenericTypes>, generics: impl Iterator) { write!(w, "<").unwrap(); for (idx, arg) in generics.enumerate() { if idx != 0 { write!(w, ", ").unwrap(); } match arg { - syn::GenericArgument::Type(t) => self.write_rust_type(w, t), + syn::GenericArgument::Type(t) => self.write_rust_type(w, generics_resolver, t), _ => unimplemented!(), } } write!(w, ">").unwrap(); } - pub fn write_rust_type(&self, w: &mut W, t: &syn::Type) { + pub fn write_rust_type(&self, w: &mut W, generics: Option<&GenericTypes>, t: &syn::Type) { match t { syn::Type::Path(p) => { if p.qself.is_some() || p.path.leading_colon.is_some() { unimplemented!(); } - self.write_rust_path(w, &p.path); + self.write_rust_path(w, generics, &p.path); }, syn::Type::Reference(r) => { write!(w, "&").unwrap(); @@ -1014,11 +1029,11 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { if r.mutability.is_some() { write!(w, "mut ").unwrap(); } - self.write_rust_type(w, &*r.elem); + self.write_rust_type(w, generics, &*r.elem); }, syn::Type::Array(a) => { write!(w, "[").unwrap(); - self.write_rust_type(w, &a.elem); + self.write_rust_type(w, generics, &a.elem); if let syn::Expr::Lit(l) = &a.len { if let syn::Lit::Int(i) = &l.lit { write!(w, "; {}]", i).unwrap(); @@ -1027,14 +1042,14 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { } syn::Type::Slice(s) => { write!(w, "[").unwrap(); - self.write_rust_type(w, &s.elem); + self.write_rust_type(w, generics, &s.elem); write!(w, "]").unwrap(); }, syn::Type::Tuple(s) => { write!(w, "(").unwrap(); for (idx, t) in s.elems.iter().enumerate() { if idx != 0 { write!(w, ", ").unwrap(); } - self.write_rust_type(w, &t); + self.write_rust_type(w, generics, &t); } write!(w, ")").unwrap(); }, @@ -1743,7 +1758,7 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { } else if in_crate { write!(w, "{}", c_type).unwrap(); } else { - self.write_rust_type(w, &t); + self.write_rust_type(w, None, &t); } } else { // If we just write out resolved_generic, it may mostly work, however for -- 2.39.5