From 494e0fd8a01dc0b7dfa61f14782805dd7da92395 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Mon, 8 Feb 2021 21:09:48 -0500 Subject: [PATCH] [bindings] Track all declared types in ImportResolver construction This allows an ImportResolver to handle more than imports, allowing ident resolution of objects constructed in the same module. --- c-bindings-gen/src/main.rs | 70 +----------------- c-bindings-gen/src/types.rs | 144 ++++++++++++++++++++---------------- 2 files changed, 84 insertions(+), 130 deletions(-) diff --git a/c-bindings-gen/src/main.rs b/c-bindings-gen/src/main.rs index f8475bbd..07948ed4 100644 --- a/c-bindings-gen/src/main.rs +++ b/c-bindings-gen/src/main.rs @@ -517,7 +517,6 @@ fn writeln_trait<'a, 'b, W: std::io::Write>(w: &mut W, t: &'a syn::ItemTrait, ty writeln!(w, "\t\t}}\n\t}}\n}}").unwrap(); write_cpp_wrapper(cpp_headers, &trait_name, true); - types.trait_declared(&t.ident, t); } /// Write out a simple "opaque" type (eg structs) which contain a pointer to the native Rust type @@ -557,26 +556,11 @@ fn writeln_opaque(w: &mut W, ident: &syn::Ident, struct_name: write_cpp_wrapper(cpp_headers, &format!("{}", ident), true); } -fn declare_struct<'a, 'b>(s: &'a syn::ItemStruct, types: &mut TypeResolver<'b, 'a>) -> bool { - let export = export_status(&s.attrs); - match export { - ExportStatus::Export => {}, - ExportStatus::TestOnly => return false, - ExportStatus::NoExport => { - types.struct_ignored(&s.ident); - return false; - } - } - - types.struct_imported(&s.ident); - true -} - /// Writes out all the relevant mappings for a Rust struct, deferring to writeln_opaque to generate /// the struct itself, and then writing getters and setters for public, understood-type fields and /// a constructor if every field is public. fn writeln_struct<'a, 'b, W: std::io::Write>(w: &mut W, s: &'a syn::ItemStruct, types: &mut TypeResolver<'b, 'a>, extra_headers: &mut File, cpp_headers: &mut File) { - if !declare_struct(s, types) { return; } + if export_status(&s.attrs) != ExportStatus::Export { return; } let struct_name = &format!("{}", s.ident); writeln_opaque(w, &s.ident, struct_name, &s.generics, &s.attrs, types, extra_headers, cpp_headers); @@ -998,37 +982,6 @@ fn writeln_impl(w: &mut W, i: &syn::ItemImpl, types: &mut Typ } } -/// Returns true if the enum will be mapped as an opaue (ie struct with a pointer to the underlying -/// type), otherwise it is mapped into a transparent, C-compatible version of itself. -fn is_enum_opaque(e: &syn::ItemEnum) -> bool { - for var in e.variants.iter() { - if let syn::Fields::Unit = var.fields { - } else if let syn::Fields::Named(fields) = &var.fields { - for field in fields.named.iter() { - match export_status(&field.attrs) { - ExportStatus::Export|ExportStatus::TestOnly => {}, - ExportStatus::NoExport => return true, - } - } - } else { - return true; - } - } - false -} - -fn declare_enum<'a, 'b>(e: &'a syn::ItemEnum, types: &mut TypeResolver<'b, 'a>) { - match export_status(&e.attrs) { - ExportStatus::Export => {}, - ExportStatus::NoExport|ExportStatus::TestOnly => return, - } - - if is_enum_opaque(e) { - types.enum_ignored(&e.ident); - } else { - types.mirrored_enum_declared(&e.ident); - } -} /// Print a mapping of an enum. If all of the enum's fields are C-mapped in some form (or the enum /// is unitary), we generate an equivalent enum with all types replaced with their C mapped @@ -1274,25 +1227,8 @@ fn convert_file<'a, 'b>(libast: &'a FullLibraryAST, crate_types: &mut CrateTypes eprintln!("Converting {} entries...", module); - let mut type_resolver = TypeResolver::new(orig_crate, module, crate_types); - - // First pass over the items and fill in imports and file-declared objects in the type resolver - for item in items.iter() { - match item { - syn::Item::Use(u) => type_resolver.process_use(&mut out, &u), - syn::Item::Struct(s) => { - if let syn::Visibility::Public(_) = s.vis { - declare_struct(&s, &mut type_resolver); - } - }, - syn::Item::Enum(e) => { - if let syn::Visibility::Public(_) = e.vis { - declare_enum(&e, &mut type_resolver); - } - }, - _ => {}, - } - } + let import_resolver = ImportResolver::new(module, items); + let mut type_resolver = TypeResolver::new(orig_crate, module, import_resolver, crate_types); for item in items.iter() { match item { diff --git a/c-bindings-gen/src/types.rs b/c-bindings-gen/src/types.rs index 7e0099cf..46973e0b 100644 --- a/c-bindings-gen/src/types.rs +++ b/c-bindings-gen/src/types.rs @@ -110,6 +110,25 @@ pub fn assert_simple_bound(bound: &syn::TraitBound) { if let syn::TraitBoundModifier::Maybe(_) = bound.modifier { unimplemented!(); } } +/// Returns true if the enum will be mapped as an opaue (ie struct with a pointer to the underlying +/// type), otherwise it is mapped into a transparent, C-compatible version of itself. +pub fn is_enum_opaque(e: &syn::ItemEnum) -> bool { + for var in e.variants.iter() { + if let syn::Fields::Unit = var.fields { + } else if let syn::Fields::Named(fields) = &var.fields { + for field in fields.named.iter() { + match export_status(&field.attrs) { + ExportStatus::Export|ExportStatus::TestOnly => {}, + ExportStatus::NoExport => return true, + } + } + } else { + return true; + } + } + false +} + /// A stack of sets of generic resolutions. /// /// This tracks the template parameters for a function, struct, or trait, allowing resolution into @@ -285,44 +304,24 @@ pub struct ImportResolver<'mod_lifetime, 'crate_lft: 'mod_lifetime> { declared: HashMap>, } impl<'mod_lifetime, 'crate_lft: 'mod_lifetime> ImportResolver<'mod_lifetime, 'crate_lft> { - pub fn new(module_path: &'mod_lifetime str) -> Self { - let mut imports = HashMap::new(); - // Add primitives to the "imports" list: - imports.insert(syn::Ident::new("bool", Span::call_site()), "bool".to_string()); - imports.insert(syn::Ident::new("u64", Span::call_site()), "u64".to_string()); - imports.insert(syn::Ident::new("u32", Span::call_site()), "u32".to_string()); - imports.insert(syn::Ident::new("u16", Span::call_site()), "u16".to_string()); - imports.insert(syn::Ident::new("u8", Span::call_site()), "u8".to_string()); - imports.insert(syn::Ident::new("usize", Span::call_site()), "usize".to_string()); - imports.insert(syn::Ident::new("str", Span::call_site()), "str".to_string()); - imports.insert(syn::Ident::new("String", Span::call_site()), "String".to_string()); - - // These are here to allow us to print native Rust types in trait fn impls even if we don't - // have C mappings: - imports.insert(syn::Ident::new("Result", Span::call_site()), "Result".to_string()); - imports.insert(syn::Ident::new("Vec", Span::call_site()), "Vec".to_string()); - imports.insert(syn::Ident::new("Option", Span::call_site()), "Option".to_string()); - Self { module_path, imports, declared: HashMap::new() } - } - - fn process_use_intern(&mut self, u: &syn::UseTree, partial_path: &str) { + fn process_use_intern(imports: &mut HashMap, u: &syn::UseTree, partial_path: &str) { match u { syn::UseTree::Path(p) => { let new_path = format!("{}::{}", partial_path, p.ident); - self.process_use_intern(&p.tree, &new_path); + Self::process_use_intern(imports, &p.tree, &new_path); }, syn::UseTree::Name(n) => { let full_path = format!("{}::{}", partial_path, n.ident); - self.imports.insert(n.ident.clone(), full_path); + imports.insert(n.ident.clone(), full_path); }, syn::UseTree::Group(g) => { for i in g.items.iter() { - self.process_use_intern(i, partial_path); + Self::process_use_intern(imports, i, partial_path); } }, syn::UseTree::Rename(r) => { let full_path = format!("{}::{}", partial_path, r.ident); - self.imports.insert(r.rename.clone(), full_path); + imports.insert(r.rename.clone(), full_path); }, syn::UseTree::Glob(_) => { eprintln!("Ignoring * use for {} - this may result in resolution failures", partial_path); @@ -330,7 +329,7 @@ impl<'mod_lifetime, 'crate_lft: 'mod_lifetime> ImportResolver<'mod_lifetime, 'cr } } - pub fn process_use(&mut self, u: &syn::ItemUse) { + pub fn process_use(imports: &mut HashMap, u: &syn::ItemUse) { if let syn::Visibility::Public(_) = u.vis { // We actually only use these for #[cfg(fuzztarget)] eprintln!("Ignoring pub(use) tree!"); @@ -340,31 +339,69 @@ impl<'mod_lifetime, 'crate_lft: 'mod_lifetime> ImportResolver<'mod_lifetime, 'cr match &u.tree { syn::UseTree::Path(p) => { let new_path = format!("{}", p.ident); - self.process_use_intern(&p.tree, &new_path); + Self::process_use_intern(imports, &p.tree, &new_path); }, syn::UseTree::Name(n) => { let full_path = format!("{}", n.ident); - self.imports.insert(n.ident.clone(), full_path); + imports.insert(n.ident.clone(), full_path); }, _ => unimplemented!(), } } - pub fn mirrored_enum_declared(&mut self, ident: &syn::Ident) { - self.declared.insert(ident.clone(), DeclType::MirroredEnum); - } - pub fn enum_ignored(&mut self, ident: &'crate_lft syn::Ident) { - self.declared.insert(ident.clone(), DeclType::EnumIgnored); - } - pub fn struct_imported(&mut self, ident: &'crate_lft syn::Ident) { - self.declared.insert(ident.clone(), DeclType::StructImported); - } - pub fn struct_ignored(&mut self, ident: &syn::Ident) { - self.declared.insert(ident.clone(), DeclType::StructIgnored); - } - pub fn trait_declared(&mut self, ident: &syn::Ident, t: &'crate_lft syn::ItemTrait) { - self.declared.insert(ident.clone(), DeclType::Trait(t)); + pub fn new(module_path: &'mod_lifetime str, contents: &'crate_lft [syn::Item]) -> Self { + let mut imports = HashMap::new(); + // Add primitives to the "imports" list: + imports.insert(syn::Ident::new("bool", Span::call_site()), "bool".to_string()); + imports.insert(syn::Ident::new("u64", Span::call_site()), "u64".to_string()); + imports.insert(syn::Ident::new("u32", Span::call_site()), "u32".to_string()); + imports.insert(syn::Ident::new("u16", Span::call_site()), "u16".to_string()); + imports.insert(syn::Ident::new("u8", Span::call_site()), "u8".to_string()); + imports.insert(syn::Ident::new("usize", Span::call_site()), "usize".to_string()); + imports.insert(syn::Ident::new("str", Span::call_site()), "str".to_string()); + imports.insert(syn::Ident::new("String", Span::call_site()), "String".to_string()); + + // These are here to allow us to print native Rust types in trait fn impls even if we don't + // have C mappings: + imports.insert(syn::Ident::new("Result", Span::call_site()), "Result".to_string()); + imports.insert(syn::Ident::new("Vec", Span::call_site()), "Vec".to_string()); + imports.insert(syn::Ident::new("Option", Span::call_site()), "Option".to_string()); + + let mut declared = HashMap::new(); + + for item in contents.iter() { + match item { + syn::Item::Use(u) => Self::process_use(&mut imports, &u), + syn::Item::Struct(s) => { + if let syn::Visibility::Public(_) = s.vis { + match export_status(&s.attrs) { + ExportStatus::Export => { declared.insert(s.ident.clone(), DeclType::StructImported); }, + ExportStatus::NoExport => { declared.insert(s.ident.clone(), DeclType::StructIgnored); }, + ExportStatus::TestOnly => continue, + } + } + }, + syn::Item::Enum(e) => { + if let syn::Visibility::Public(_) = e.vis { + 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); }, + _ => 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)); + } + }, + _ => {}, + } + } + + Self { module_path, imports, declared } } + pub fn get_declared_type(&self, ident: &syn::Ident) -> Option<&DeclType<'crate_lft>> { self.declared.get(ident) } @@ -477,8 +514,8 @@ enum EmptyValExpectedTy { } impl<'a, 'c: 'a> TypeResolver<'a, 'c> { - pub fn new(orig_crate: &'a str, module_path: &'a str, crate_types: &'a mut CrateTypes<'c>) -> Self { - Self { orig_crate, module_path, types: ImportResolver::new(module_path), crate_types } + pub fn new(orig_crate: &'a str, module_path: &'a str, types: ImportResolver<'a, 'c>, crate_types: &'a mut CrateTypes<'c>) -> Self { + Self { orig_crate, module_path, types, crate_types } } // ************************************************* @@ -985,25 +1022,6 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { // *** Type definition during main.rs processing *** // ************************************************* - pub fn process_use(&mut self, w: &mut W, u: &syn::ItemUse) { - self.types.process_use(u); - } - - pub fn mirrored_enum_declared(&mut self, ident: &syn::Ident) { - self.types.mirrored_enum_declared(ident); - } - pub fn enum_ignored(&mut self, ident: &'c syn::Ident) { - self.types.enum_ignored(ident); - } - pub fn struct_imported(&mut self, ident: &'c syn::Ident) { - self.types.struct_imported(ident); - } - pub fn struct_ignored(&mut self, ident: &syn::Ident) { - self.types.struct_ignored(ident); - } - pub fn trait_declared(&mut self, ident: &syn::Ident, t: &'c syn::ItemTrait) { - self.types.trait_declared(ident, t); - } pub fn get_declared_type(&'a self, ident: &syn::Ident) -> Option<&'a DeclType<'c>> { self.types.get_declared_type(ident) } -- 2.30.2