From 4d0cf680ab706fb737d1195d620a91057d449390 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Thu, 1 Oct 2020 21:28:57 -0400 Subject: [PATCH] [bindings] Handle type X = Y aliasing in type resolution For non-generic type aliases which are meant as convinient aliases for more complex types, we need to store the aliased type (with all paths made absolute) and use that in type resolution. The most code by far is just making all the paths in a type absolute but its not too bad either. --- c-bindings-gen/src/main.rs | 96 +++++++++++++++++++++++++++++++++++-- c-bindings-gen/src/types.rs | 12 ++++- 2 files changed, 102 insertions(+), 6 deletions(-) diff --git a/c-bindings-gen/src/main.rs b/c-bindings-gen/src/main.rs index 76a5eebda..611607ede 100644 --- a/c-bindings-gen/src/main.rs +++ b/c-bindings-gen/src/main.rs @@ -1057,8 +1057,6 @@ struct FullLibraryAST { /// `out_path` and fills it with wrapper structs/functions to allow calling the things in the AST /// at `module` from C. fn convert_file<'a, 'b>(libast: &'a FullLibraryAST, crate_types: &mut CrateTypes<'a>, in_dir: &str, out_dir: &str, path: &str, orig_crate: &str, module: &str, header_file: &mut File, cpp_header_file: &mut File) { - eprintln!("Converting {}...", path); - let syntax = if let Some(ast) = libast.files.get(module) { ast } else { return }; assert!(syntax.shebang.is_none()); // Not sure what this is, hope we dont have one @@ -1096,6 +1094,8 @@ fn convert_file<'a, 'b>(libast: &'a FullLibraryAST, crate_types: &mut CrateTypes orig_crate, &new_mod, header_file, cpp_header_file); } + eprintln!("Converting {} entries...", path); + let mut type_resolver = TypeResolver::new(orig_crate, module, crate_types); for item in syntax.items.iter() { @@ -1139,8 +1139,18 @@ fn convert_file<'a, 'b>(libast: &'a FullLibraryAST, crate_types: &mut CrateTypes ExportStatus::Export => {}, ExportStatus::NoExport|ExportStatus::TestOnly => continue, } - if t.generics.lt_token.is_none() { - writeln_opaque(&mut out, &t.ident, &format!("{}", t.ident), &t.generics, &t.attrs, &type_resolver, header_file, cpp_header_file); + + let mut process_alias = true; + for tok in t.generics.params.iter() { + if let syn::GenericParam::Lifetime(_) = tok {} + else { process_alias = false; } + } + if process_alias { + match &*t.ty { + syn::Type::Path(_) => + writeln_opaque(&mut out, &t.ident, &format!("{}", t.ident), &t.generics, &t.attrs, &type_resolver, header_file, cpp_header_file), + _ => {} + } } } }, @@ -1180,6 +1190,52 @@ fn load_ast(in_dir: &str, path: &str, module: String, ast_storage: &mut FullLibr ast_storage.files.insert(module, syntax); } +/// Insert ident -> absolute Path resolutions into imports from the given UseTree and path-prefix. +fn process_use_intern<'a>(u: &'a syn::UseTree, mut path: syn::punctuated::Punctuated, imports: &mut HashMap<&'a syn::Ident, syn::Path>) { + match u { + syn::UseTree::Path(p) => { + path.push(syn::PathSegment { ident: p.ident.clone(), arguments: syn::PathArguments::None }); + process_use_intern(&p.tree, path, imports); + }, + syn::UseTree::Name(n) => { + path.push(syn::PathSegment { ident: n.ident.clone(), arguments: syn::PathArguments::None }); + imports.insert(&n.ident, syn::Path { leading_colon: Some(syn::Token![::](Span::call_site())), segments: path }); + }, + syn::UseTree::Group(g) => { + for i in g.items.iter() { + process_use_intern(i, path.clone(), imports); + } + }, + _ => {} + } +} + +/// Map all the Paths in a Type into absolute paths given a set of imports (generated via process_use_intern) +fn resolve_imported_refs(imports: &HashMap<&syn::Ident, syn::Path>, mut ty: syn::Type) -> syn::Type { + match &mut ty { + syn::Type::Path(p) => { + if let Some(ident) = p.path.get_ident() { + if let Some(newpath) = imports.get(ident) { + p.path = newpath.clone(); + } + } else { unimplemented!(); } + }, + syn::Type::Reference(r) => { + r.elem = Box::new(resolve_imported_refs(imports, (*r.elem).clone())); + }, + syn::Type::Slice(s) => { + s.elem = Box::new(resolve_imported_refs(imports, (*s.elem).clone())); + }, + syn::Type::Tuple(t) => { + for e in t.elems.iter_mut() { + *e = resolve_imported_refs(imports, e.clone()); + } + }, + _ => unimplemented!(), + } + ty +} + /// Walk the FullLibraryAST, deciding how things will be mapped and adding tracking to CrateTypes. fn walk_ast<'a>(in_dir: &str, path: &str, module: String, ast_storage: &'a FullLibraryAST, crate_types: &mut CrateTypes<'a>) { let syntax = if let Some(ast) = ast_storage.files.get(&module) { ast } else { return }; @@ -1189,8 +1245,13 @@ fn walk_ast<'a>(in_dir: &str, path: &str, module: String, ast_storage: &'a FullL walk_ast(in_dir, &path, new_mod, ast_storage, crate_types); } + let mut import_maps = HashMap::new(); + for item in syntax.items.iter() { match item { + syn::Item::Use(u) => { + process_use_intern(&u.tree, syn::punctuated::Punctuated::new(), &mut import_maps); + }, syn::Item::Struct(s) => { if let syn::Visibility::Public(_) = s.vis { match export_status(&s.attrs) { @@ -1211,6 +1272,31 @@ fn walk_ast<'a>(in_dir: &str, path: &str, module: String, ast_storage: &'a FullL crate_types.traits.insert(trait_path, &t); } }, + syn::Item::Type(t) => { + if let syn::Visibility::Public(_) = t.vis { + match export_status(&t.attrs) { + ExportStatus::Export => {}, + ExportStatus::NoExport|ExportStatus::TestOnly => continue, + } + let type_path = format!("{}::{}", module, t.ident); + let mut process_alias = true; + for tok in t.generics.params.iter() { + if let syn::GenericParam::Lifetime(_) = tok {} + else { process_alias = false; } + } + if process_alias { + match &*t.ty { + syn::Type::Path(_) => { + // If its a path with no generics, assume we don't map the aliased type and map it opaque + crate_types.opaques.insert(type_path, &t.ident); + }, + _ => { + crate_types.type_aliases.insert(type_path, resolve_imported_refs(&import_maps, (*t.ty).clone())); + } + } + } + } + }, syn::Item::Enum(e) if is_enum_opaque(e) => { if let syn::Visibility::Public(_) = e.vis { match export_status(&e.attrs) { @@ -1264,7 +1350,7 @@ 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 { traits: HashMap::new(), opaques: HashMap::new(), mirrored_enums: HashMap::new(), - templates_defined: HashMap::new(), template_file: &mut derived_templates }; + type_aliases: HashMap::new(), templates_defined: HashMap::new(), template_file: &mut derived_templates }; walk_ast(&args[1], "/lib.rs", "".to_string(), &libast, &mut libtypes); // ... finally, do the actual file conversion/mapping, writing out types as we go. diff --git a/c-bindings-gen/src/types.rs b/c-bindings-gen/src/types.rs index 14b2826f1..c7a13fe0d 100644 --- a/c-bindings-gen/src/types.rs +++ b/c-bindings-gen/src/types.rs @@ -234,6 +234,8 @@ pub struct CrateTypes<'a> { pub mirrored_enums: HashMap, /// Traits which are mapped as a pointer + jump table pub traits: HashMap, + /// Aliases from paths to some other Type + pub type_aliases: HashMap, /// Template continer types defined, map from mangled type name -> whether a destructor fn /// exists. /// @@ -1164,7 +1166,9 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { } let resolved_path = self.resolve_path(&p.path, generics); - if let Some(c_type) = path_lookup(&resolved_path, is_ref, ptr_for_ref) { + if let Some(aliased_type) = self.crate_types.type_aliases.get(&resolved_path) { + return self.write_conversion_inline_intern(w, aliased_type, None, is_ref, is_mut, ptr_for_ref, tupleconv, prefix, sliceconv, path_lookup, decl_lookup); + } else if let Some(c_type) = path_lookup(&resolved_path, is_ref, ptr_for_ref) { write!(w, "{}", c_type).unwrap(); } else if self.crate_types.opaques.get(&resolved_path).is_some() { decl_lookup(w, &DeclType::StructImported, &resolved_path, is_ref, is_mut); @@ -1476,6 +1480,9 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { unimplemented!(); } let resolved_path = self.resolve_path(&p.path, generics); + if let Some(aliased_type) = self.crate_types.type_aliases.get(&resolved_path) { + return self.write_conversion_new_var_intern(w, ident, var, aliased_type, None, is_ref, ptr_for_ref, to_c, path_lookup, container_lookup, var_prefix, var_suffix); + } if self.is_known_container(&resolved_path, is_ref) || self.is_transparent_container(&resolved_path, is_ref) { if let syn::PathArguments::AngleBracketed(args) = &p.path.segments.iter().next().unwrap().arguments { convert_container!(resolved_path, args.args.len(), || args.args.iter().map(|arg| { @@ -1949,6 +1956,9 @@ impl<'a, 'c: 'a> TypeResolver<'a, 'c> { if self.is_known_container(&full_path, is_ref) || self.is_transparent_container(&full_path, is_ref) { return self.write_c_mangled_container_path(w, Self::path_to_generic_args(&p.path), generics, &full_path, is_ref, is_mut, ptr_for_ref); } + if let Some(aliased_type) = self.crate_types.type_aliases.get(&full_path).cloned() { + return self.write_c_type_intern(w, &aliased_type, None, is_ref, is_mut, ptr_for_ref); + } } self.write_c_path_intern(w, &p.path, generics, is_ref, is_mut, ptr_for_ref) }, -- 2.39.5