[bindings] Support traits with generic arguments (to support #681)
authorMatt Corallo <git@bluematt.me>
Fri, 13 Nov 2020 00:58:51 +0000 (19:58 -0500)
committerMatt Corallo <git@bluematt.me>
Mon, 23 Nov 2020 16:08:34 +0000 (11:08 -0500)
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
c-bindings-gen/src/types.rs

index ecbd947baa8fb7dfd72231ee39b73716632842a2..4edad55d1fb9ff9ae071802e76ba5287e77b46dc 100644 (file)
@@ -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!(),
                                }
index 3fc35e8b2558f8de6f7da2d4d017664a8b3390d1..483c1d9b9d12821b427cf8e0e143a56abb9a2400 100644 (file)
@@ -932,19 +932,34 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> {
        // *** Original Rust Type Printing ***
        // ***********************************
 
-       fn write_rust_path<W: std::io::Write>(&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<W: std::io::Write>(&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<Item=&'b syn::GenericParam>) {
+       pub fn write_rust_generic_param<'b, W: std::io::Write>(&self, w: &mut W, generics_resolver: Option<&GenericTypes>, generics: impl Iterator<Item=&'b syn::GenericParam>) {
                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<Item=&'b syn::GenericArgument>) {
+       pub fn write_rust_generic_arg<'b, W: std::io::Write>(&self, w: &mut W, generics_resolver: Option<&GenericTypes>, generics: impl Iterator<Item=&'b syn::GenericArgument>) {
                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<W: std::io::Write>(&self, w: &mut W, t: &syn::Type) {
+       pub fn write_rust_type<W: std::io::Write>(&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