Check whether the fields of an enum are clonable before implementing clone
[ldk-c-bindings] / c-bindings-gen / src / main.rs
index 94f2ed736ea1356d22f30312c7a20dd7ba1a8314..b9dba8c92e78188d3bb4abf4a80779fcb9ec2732 100644 (file)
@@ -1594,8 +1594,37 @@ fn writeln_enum<'a, 'b, W: std::io::Write>(w: &mut W, e: &'a syn::ItemEnum, type
 
        let mut needs_free = false;
        let mut constr = Vec::new();
+       let mut is_clonable = true;
 
-       writeln!(w, "#[must_use]\n#[derive(Clone)]\n#[repr(C)]\npub enum {} {{", e.ident).unwrap();
+       for var in e.variants.iter() {
+               if let syn::Fields::Named(fields) = &var.fields {
+                       needs_free = true;
+                       for field in fields.named.iter() {
+                               if export_status(&field.attrs) == ExportStatus::TestOnly { continue; }
+
+                               let mut ty_checks = Vec::new();
+                               types.write_c_type(&mut ty_checks, &field.ty, Some(&gen_types), false);
+                               if !types.is_clonable(&String::from_utf8(ty_checks).unwrap()) {
+                                       is_clonable = false;
+                               }
+                       }
+               } else if let syn::Fields::Unnamed(fields) = &var.fields {
+                       for field in fields.unnamed.iter() {
+                               let mut ty_checks = Vec::new();
+                               types.write_c_type(&mut ty_checks, &field.ty, Some(&gen_types), false);
+                               let ty = String::from_utf8(ty_checks).unwrap();
+                               if ty != "" && !types.is_clonable(&ty) {
+                                       is_clonable = false;
+                               }
+                       }
+               }
+       }
+
+       if is_clonable {
+               writeln!(w, "#[derive(Clone)]").unwrap();
+               types.crate_types.set_clonable(format!("{}::{}", types.module_path, e.ident));
+       }
+       writeln!(w, "#[must_use]\n#[repr(C)]\npub enum {} {{", e.ident).unwrap();
        for var in e.variants.iter() {
                assert_eq!(export_status(&var.attrs), ExportStatus::Export); // We can't partially-export a mirrored enum
                writeln_docs(w, &var.attrs, "\t");
@@ -1803,9 +1832,13 @@ fn writeln_enum<'a, 'b, W: std::io::Write>(w: &mut W, e: &'a syn::ItemEnum, type
                }
        }
 
-       write_conv!(format!("to_native(&self) -> native{}", e.ident), false, true);
+       if is_clonable {
+               write_conv!(format!("to_native(&self) -> native{}", e.ident), false, true);
+       }
        write_conv!(format!("into_native(self) -> native{}", e.ident), false, false);
-       write_conv!(format!("from_native(native: &native{}) -> Self", e.ident), true, true);
+       if is_clonable {
+               write_conv!(format!("from_native(native: &native{}) -> Self", e.ident), true, true);
+       }
        write_conv!(format!("native_into(native: native{}) -> Self", e.ident), true, false);
        writeln!(w, "}}").unwrap();
 
@@ -1813,11 +1846,13 @@ fn writeln_enum<'a, 'b, W: std::io::Write>(w: &mut W, e: &'a syn::ItemEnum, type
                writeln!(w, "/// Frees any resources used by the {}", e.ident).unwrap();
                writeln!(w, "#[no_mangle]\npub extern \"C\" fn {}_free(this_ptr: {}) {{ }}", e.ident, e.ident).unwrap();
        }
-       writeln!(w, "/// Creates a copy of the {}", e.ident).unwrap();
-       writeln!(w, "#[no_mangle]").unwrap();
-       writeln!(w, "pub extern \"C\" fn {}_clone(orig: &{}) -> {} {{", e.ident, e.ident, e.ident).unwrap();
-       writeln!(w, "\torig.clone()").unwrap();
-       writeln!(w, "}}").unwrap();
+       if is_clonable {
+               writeln!(w, "/// Creates a copy of the {}", e.ident).unwrap();
+               writeln!(w, "#[no_mangle]").unwrap();
+               writeln!(w, "pub extern \"C\" fn {}_clone(orig: &{}) -> {} {{", e.ident, e.ident, e.ident).unwrap();
+               writeln!(w, "\torig.clone()").unwrap();
+               writeln!(w, "}}").unwrap();
+       }
        w.write_all(&constr).unwrap();
        write_cpp_wrapper(cpp_headers, &format!("{}", e.ident), needs_free, None);
 }
@@ -2046,6 +2081,49 @@ fn convert_file<'a, 'b>(libast: &'a FullLibraryAST, crate_types: &CrateTypes<'a>
        }
 }
 
+
+/// Walk the FullLibraryAST, determining if impl aliases need to be marked cloneable.
+fn walk_ast_second_pass<'a>(ast_storage: &'a FullLibraryAST, crate_types: &CrateTypes<'a>) {
+       for (module, astmod) in ast_storage.modules.iter() {
+               let orig_crate = module.splitn(2, "::").next().unwrap();
+               let ASTModule { ref attrs, ref items, .. } = astmod;
+               assert_eq!(export_status(&attrs), ExportStatus::Export);
+
+               let import_resolver = ImportResolver::new(orig_crate, &ast_storage.dependencies, module, items);
+               let mut types = TypeResolver::new(module, import_resolver, crate_types);
+
+               for item in items.iter() {
+                       match item {
+                               syn::Item::Impl(i) => {
+                                       match export_status(&i.attrs) {
+                                               ExportStatus::Export => {},
+                                               ExportStatus::NoExport|ExportStatus::TestOnly => continue,
+                                               ExportStatus::NotImplementable => panic!("(C-not implementable) must only appear on traits"),
+                                       }
+                                       if let Some(trait_path) = i.trait_.as_ref() {
+                                               if path_matches_nongeneric(&trait_path.1, &["core", "clone", "Clone"]) ||
+                                                  path_matches_nongeneric(&trait_path.1, &["Clone"])
+                                               {
+                                                       if let &syn::Type::Path(ref p) = &*i.self_ty {
+                                                               if let Some(resolved_path) = types.maybe_resolve_path(&p.path, None) {
+                                                                       create_alias_for_impl(resolved_path, i, &mut types, |aliased_impl, types| {
+                                                                               if let &syn::Type::Path(ref p) = &*aliased_impl.self_ty {
+                                                                                       if let Some(resolved_aliased_path) = types.maybe_resolve_path(&p.path, None) {
+                                                                                               crate_types.set_clonable("crate::".to_owned() + &resolved_aliased_path);
+                                                                                       }
+                                                                               }
+                                                                       });
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               }
+                               _ => {}
+                       }
+               }
+       }
+}
+
 fn walk_private_mod<'a>(ast_storage: &'a FullLibraryAST, orig_crate: &str, module: String, items: &'a syn::ItemMod, crate_types: &mut CrateTypes<'a>) {
        let import_resolver = ImportResolver::new(orig_crate, &ast_storage.dependencies, &module, &items.content.as_ref().unwrap().1);
        for item in items.content.as_ref().unwrap().1.iter() {
@@ -2071,7 +2149,7 @@ fn walk_private_mod<'a>(ast_storage: &'a FullLibraryAST, orig_crate: &str, modul
 }
 
 /// Walk the FullLibraryAST, deciding how things will be mapped and adding tracking to CrateTypes.
-fn walk_ast<'a>(ast_storage: &'a FullLibraryAST, crate_types: &mut CrateTypes<'a>) {
+fn walk_ast_first_pass<'a>(ast_storage: &'a FullLibraryAST, crate_types: &mut CrateTypes<'a>) {
        for (module, astmod) in ast_storage.modules.iter() {
                let ASTModule { ref attrs, ref items, submods: _ } = astmod;
                assert_eq!(export_status(&attrs), ExportStatus::Export);
@@ -2226,7 +2304,11 @@ fn main() {
        // ...then walk the ASTs tracking what types we will map, and how, so that we can resolve them
        // when parsing other file ASTs...
        let mut libtypes = CrateTypes::new(&mut derived_templates, &libast);
-       walk_ast(&libast, &mut libtypes);
+       walk_ast_first_pass(&libast, &mut libtypes);
+
+       // ... using the generated data, determine a few additional fields, specifically which type
+       // aliases are to be clone-able...
+       walk_ast_second_pass(&libast, &libtypes);
 
        // ... finally, do the actual file conversion/mapping, writing out types as we go.
        convert_file(&libast, &libtypes, &args[1], &mut header_file, &mut cpp_header_file);