From: Matt Corallo Date: Thu, 11 Feb 2021 16:47:51 +0000 (-0500) Subject: [bindings] Handle generic-ized impl blocks by concretizing them X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=commitdiff_plain;h=5d51edb5ad540ff473e39ab846f0ee485545b91b;p=rust-lightning [bindings] Handle generic-ized impl blocks by concretizing them This handles, for example, the `impl for Features` blocks which are generic across a number of different contexts. We do so by walking the set of structs which alias Features and then walking their generic arguments to check that they meet the bounds specified in the impl block. For each alias which does, we create a dummy, explicit, `impl XFeatures` block with the same content as the original and recurse. --- diff --git a/c-bindings-gen/src/main.rs b/c-bindings-gen/src/main.rs index 6eac851f1..a93122daf 100644 --- a/c-bindings-gen/src/main.rs +++ b/c-bindings-gen/src/main.rs @@ -10,7 +10,7 @@ //! It also generates relevant memory-management functions and free-standing functions with //! parameters mapped. -use std::collections::{HashMap, HashSet}; +use std::collections::{HashMap, hash_map, HashSet}; use std::env; use std::fs::File; use std::io::{Read, Write}; @@ -975,8 +975,62 @@ fn writeln_impl(w: &mut W, i: &syn::ItemImpl, types: &mut Typ } } } + } else if let Some(resolved_path) = types.maybe_resolve_ident(&ident) { + if let Some(aliases) = types.crate_types.reverse_alias_map.get(&resolved_path).cloned() { + 'alias_impls: for (alias, arguments) in aliases { + let alias_resolved = types.resolve_path(&alias, None); + for (idx, gen) in i.generics.params.iter().enumerate() { + match gen { + syn::GenericParam::Type(type_param) => { + 'bounds_check: for bound in type_param.bounds.iter() { + if let syn::TypeParamBound::Trait(trait_bound) = bound { + if let syn::PathArguments::AngleBracketed(ref t) = &arguments { + assert!(idx < t.args.len()); + if let syn::GenericArgument::Type(syn::Type::Path(p)) = &t.args[idx] { + let generic_arg = types.resolve_path(&p.path, None); + let generic_bound = types.resolve_path(&trait_bound.path, None); + if let Some(traits_impld) = types.crate_types.trait_impls.get(&generic_arg) { + for trait_impld in traits_impld { + if *trait_impld == generic_bound { continue 'bounds_check; } + } + eprintln!("struct {}'s generic arg {} didn't match bound {}", alias_resolved, generic_arg, generic_bound); + continue 'alias_impls; + } else { + eprintln!("struct {}'s generic arg {} didn't match bound {}", alias_resolved, generic_arg, generic_bound); + continue 'alias_impls; + } + } else { unimplemented!(); } + } else { unimplemented!(); } + } else { unimplemented!(); } + } + }, + syn::GenericParam::Lifetime(_) => {}, + syn::GenericParam::Const(_) => unimplemented!(), + } + } + let aliased_impl = syn::ItemImpl { + attrs: i.attrs.clone(), + brace_token: syn::token::Brace(Span::call_site()), + defaultness: None, + generics: syn::Generics { + lt_token: None, + params: syn::punctuated::Punctuated::new(), + gt_token: None, + where_clause: None, + }, + impl_token: syn::Token![impl](Span::call_site()), + items: i.items.clone(), + self_ty: Box::new(syn::Type::Path(syn::TypePath { qself: None, path: alias.clone() })), + trait_: i.trait_.clone(), + unsafety: None, + }; + writeln_impl(w, &aliased_impl, types); + } + } else { + eprintln!("Not implementing anything for {} due to it being marked not exported", ident); + } } else { - eprintln!("Not implementing anything for {} due to no-resolve (probably the type isn't pub or its marked not exported)", ident); + eprintln!("Not implementing anything for {} due to no-resolve (probably the type isn't pub)", ident); } } } @@ -1382,9 +1436,21 @@ fn walk_ast<'a>(ast_storage: &'a FullLibraryAST, crate_types: &mut CrateTypes<'a } if process_alias { match &*t.ty { - syn::Type::Path(_) => { + syn::Type::Path(p) => { // 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); + let mut segments = syn::punctuated::Punctuated::new(); + segments.push(syn::PathSegment { + ident: t.ident.clone(), + arguments: syn::PathArguments::None, + }); + let path_obj = syn::Path { leading_colon: None, segments }; + let args_obj = p.path.segments.last().unwrap().arguments.clone(); + match crate_types.reverse_alias_map.entry(import_resolver.maybe_resolve_path(&p.path, None).unwrap()) { + hash_map::Entry::Occupied(mut e) => { e.get_mut().push((path_obj, args_obj)); }, + hash_map::Entry::Vacant(e) => { e.insert(vec![(path_obj, args_obj)]); }, + } + + crate_types.opaques.insert(type_path.clone(), &t.ident); }, _ => { crate_types.type_aliases.insert(type_path, import_resolver.resolve_imported_refs((*t.ty).clone())); @@ -1477,7 +1543,8 @@ 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(), - type_aliases: HashMap::new(), templates_defined: HashMap::default(), template_file: &mut derived_templates, + type_aliases: HashMap::new(), reverse_alias_map: HashMap::new(), templates_defined: HashMap::default(), + template_file: &mut derived_templates, clonable_types: HashSet::new(), trait_impls: HashMap::new() }; walk_ast(&libast, &mut libtypes); diff --git a/c-bindings-gen/src/types.rs b/c-bindings-gen/src/types.rs index ad9cd0497..656ec13f9 100644 --- a/c-bindings-gen/src/types.rs +++ b/c-bindings-gen/src/types.rs @@ -529,6 +529,8 @@ pub struct CrateTypes<'a> { pub traits: HashMap, /// Aliases from paths to some other Type pub type_aliases: HashMap, + /// Value is an alias to Key (maybe with some generics) + pub reverse_alias_map: HashMap>, /// Template continer types defined, map from mangled type name -> whether a destructor fn /// exists. ///