Skip to content

Commit 086434f

Browse files
committed
[bindings] Replace associated_types HashMaps with common Generics
Instead of handling associated types separately, we can just shove them into the same generics resolution logic we use for template types. While we should probably have some precedence logic, aliasing type names seems like a bad idea anyway so no effort is made to handle it. This removes a good chunk of code and, more importantly, tees us up for supporting `Type<Self::AssociatedType>`-style generics.
1 parent 70440a5 commit 086434f

File tree

3 files changed

+75
-62
lines changed

3 files changed

+75
-62
lines changed

c-bindings-gen/src/blocks.rs

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
//! Printing logic for basic blocks of Rust-mapped code - parts of functions and declarations but
22
//! not the full mapping logic.
33
4-
use std::collections::HashMap;
54
use std::fs::File;
65
use std::io::Write;
76
use proc_macro2::{TokenTree, Span};
@@ -77,7 +76,7 @@ pub fn writeln_docs<W: std::io::Write>(w: &mut W, attrs: &[syn::Attribute], pref
7776
///
7877
/// this_param is used when returning Self or accepting a self parameter, and should be the
7978
/// concrete, mapped type.
80-
pub fn write_method_params<W: std::io::Write>(w: &mut W, sig: &syn::Signature, associated_types: &HashMap<&syn::Ident, &syn::Ident>, this_param: &str, types: &mut TypeResolver, generics: Option<&GenericTypes>, self_ptr: bool, fn_decl: bool) {
79+
pub fn write_method_params<W: std::io::Write>(w: &mut W, sig: &syn::Signature, this_param: &str, types: &mut TypeResolver, generics: Option<&GenericTypes>, self_ptr: bool, fn_decl: bool) {
8180
if sig.constness.is_some() || sig.asyncness.is_some() || sig.unsafety.is_some() ||
8281
sig.abi.is_some() || sig.variadic.is_some() {
8382
unimplemented!();
@@ -140,26 +139,16 @@ pub fn write_method_params<W: std::io::Write>(w: &mut W, sig: &syn::Signature, a
140139
syn::ReturnType::Type(_, rtype) => {
141140
write!(w, " -> ").unwrap();
142141
if let Some(mut remaining_path) = first_seg_self(&*rtype) {
143-
if let Some(associated_seg) = get_single_remaining_path_seg(&mut remaining_path) {
144-
// We're returning an associated type in a trait impl. Its probably a safe bet
145-
// that its also a trait, so just return the trait type.
146-
let real_type = associated_types.get(associated_seg).unwrap();
147-
types.write_c_type(w, &syn::Type::Path(syn::TypePath { qself: None,
148-
path: syn::PathSegment {
149-
ident: (*real_type).clone(),
150-
arguments: syn::PathArguments::None
151-
}.into()
152-
}), generics, true);
153-
} else {
142+
if remaining_path.next().is_none() {
154143
write!(w, "{}", this_param).unwrap();
144+
return;
155145
}
146+
}
147+
if let syn::Type::Reference(r) = &**rtype {
148+
// We can't return a reference, cause we allocate things on the stack.
149+
types.write_c_type(w, &*r.elem, generics, true);
156150
} else {
157-
if let syn::Type::Reference(r) = &**rtype {
158-
// We can't return a reference, cause we allocate things on the stack.
159-
types.write_c_type(w, &*r.elem, generics, true);
160-
} else {
161-
types.write_c_type(w, &*rtype, generics, true);
162-
}
151+
types.write_c_type(w, &*rtype, generics, true);
163152
}
164153
},
165154
_ => {},
@@ -222,7 +211,7 @@ pub fn write_method_var_decl_body<W: std::io::Write>(w: &mut W, sig: &syn::Signa
222211
///
223212
/// The return value is expected to be bound to a variable named `ret` which is available after a
224213
/// method-call-ending semicolon.
225-
pub fn write_method_call_params<W: std::io::Write>(w: &mut W, sig: &syn::Signature, associated_types: &HashMap<&syn::Ident, &syn::Ident>, extra_indent: &str, types: &TypeResolver, generics: Option<&GenericTypes>, this_type: &str, to_c: bool) {
214+
pub fn write_method_call_params<W: std::io::Write>(w: &mut W, sig: &syn::Signature, extra_indent: &str, types: &TypeResolver, generics: Option<&GenericTypes>, this_type: &str, to_c: bool) {
226215
let mut first_arg = true;
227216
let mut num_unused = 0;
228217
for inp in sig.inputs.iter() {
@@ -291,8 +280,16 @@ pub fn write_method_call_params<W: std::io::Write>(w: &mut W, sig: &syn::Signatu
291280
} else if !to_c && first_seg_self(&*rtype).is_some() {
292281
if let Some(mut remaining_path) = first_seg_self(&*rtype) {
293282
if let Some(associated_seg) = get_single_remaining_path_seg(&mut remaining_path) {
294-
let real_type = associated_types.get(associated_seg).unwrap();
295-
if let Some(t) = types.crate_types.traits.get(&types.maybe_resolve_ident(&real_type).unwrap()) {
283+
// Build a fake path with only associated_seg and resolve it:
284+
let mut segments = syn::punctuated::Punctuated::new();
285+
segments.push(syn::PathSegment {
286+
ident: associated_seg.clone(), arguments: syn::PathArguments::None });
287+
let (_, real_path) = generics.unwrap().maybe_resolve_path(&syn::Path {
288+
leading_colon: None, segments }).unwrap();
289+
290+
assert_eq!(real_path.segments.len(), 1);
291+
let real_ident = &real_path.segments.iter().next().unwrap().ident;
292+
if let Some(t) = types.crate_types.traits.get(&types.maybe_resolve_ident(&real_ident).unwrap()) {
296293
// We're returning an associated trait from a Rust fn call to a C trait
297294
// object.
298295
writeln!(w, "let mut rust_obj = {} {{ inner: Box::into_raw(Box::new(ret)), is_owned: true }};", this_type).unwrap();

c-bindings-gen/src/main.rs

Lines changed: 10 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -109,31 +109,6 @@ macro_rules! walk_supertraits { ($t: expr, $types: expr, ($( $pat: pat => $e: ex
109109
}
110110
} } }
111111

112-
/// Gets a HashMap from name idents to the bounding trait for associated types.
113-
/// eg if a native trait has a "type T = TraitA", this will return a HashMap containing a mapping
114-
/// from "T" to "TraitA".
115-
fn learn_associated_types<'a>(t: &'a syn::ItemTrait) -> HashMap<&'a syn::Ident, &'a syn::Ident> {
116-
let mut associated_types = HashMap::new();
117-
for item in t.items.iter() {
118-
match item {
119-
&syn::TraitItem::Type(ref t) => {
120-
if t.default.is_some() || t.generics.lt_token.is_some() { unimplemented!(); }
121-
let mut bounds_iter = t.bounds.iter();
122-
match bounds_iter.next().unwrap() {
123-
syn::TypeParamBound::Trait(tr) => {
124-
assert_simple_bound(&tr);
125-
associated_types.insert(&t.ident, assert_single_path_seg(&tr.path));
126-
},
127-
_ => unimplemented!(),
128-
}
129-
if bounds_iter.next().is_some() { unimplemented!(); }
130-
},
131-
_ => {},
132-
}
133-
}
134-
associated_types
135-
}
136-
137112
/// Prints a C-mapped trait object containing a void pointer and a jump table for each function in
138113
/// the original trait.
139114
/// Implements the native Rust trait and relevant parent traits for the new C-mapped trait.
@@ -150,10 +125,10 @@ fn writeln_trait<'a, 'b, W: std::io::Write>(w: &mut W, t: &'a syn::ItemTrait, ty
150125

151126
let mut gen_types = GenericTypes::new();
152127
assert!(gen_types.learn_generics(&t.generics, types));
128+
gen_types.learn_associated_types(&t, types);
153129

154130
writeln!(w, "#[repr(C)]\npub struct {} {{", trait_name).unwrap();
155131
writeln!(w, "\tpub this_arg: *mut c_void,").unwrap();
156-
let associated_types = learn_associated_types(t);
157132
let mut generated_fields = Vec::new(); // Every field's name except this_arg, used in Clone generation
158133
for item in t.items.iter() {
159134
match item {
@@ -210,7 +185,7 @@ fn writeln_trait<'a, 'b, W: std::io::Write>(w: &mut W, t: &'a syn::ItemTrait, ty
210185

211186
write!(w, "\tpub {}: extern \"C\" fn (", m.sig.ident).unwrap();
212187
generated_fields.push(format!("{}", m.sig.ident));
213-
write_method_params(w, &m.sig, &associated_types, "c_void", types, Some(&gen_types), true, false);
188+
write_method_params(w, &m.sig, "c_void", types, Some(&gen_types), true, false);
214189
writeln!(w, ",").unwrap();
215190

216191
gen_types.pop_ctx();
@@ -363,7 +338,7 @@ fn writeln_trait<'a, 'b, W: std::io::Write>(w: &mut W, t: &'a syn::ItemTrait, ty
363338
}
364339
write_method_var_decl_body(w, &m.sig, "\t", types, Some(&gen_types), true);
365340
write!(w, "(self.{})(", m.sig.ident).unwrap();
366-
write_method_call_params(w, &m.sig, &associated_types, "\t", types, Some(&gen_types), "", true);
341+
write_method_call_params(w, &m.sig, "\t", types, Some(&gen_types), "", true);
367342

368343
writeln!(w, "\n\t}}").unwrap();
369344
gen_types.pop_ctx();
@@ -605,7 +580,7 @@ fn writeln_impl<W: std::io::Write>(w: &mut W, i: &syn::ItemImpl, types: &mut Typ
605580
// That's great, except that they are unresolved idents, so if we learn
606581
// mappings from a trai defined in a different file, we may mis-resolve or
607582
// fail to resolve the mapped types.
608-
let trait_associated_types = learn_associated_types(trait_obj);
583+
gen_types.learn_associated_types(trait_obj, types);
609584
let mut impl_associated_types = HashMap::new();
610585
for item in i.items.iter() {
611586
match item {
@@ -706,7 +681,7 @@ fn writeln_impl<W: std::io::Write>(w: &mut W, i: &syn::ItemImpl, types: &mut Typ
706681
write!(w, "extern \"C\" fn {}_{}_{}(", ident, trait_obj.ident, $m.sig.ident).unwrap();
707682
gen_types.push_ctx();
708683
assert!(gen_types.learn_generics(&$m.sig.generics, types));
709-
write_method_params(w, &$m.sig, &trait_associated_types, "c_void", types, Some(&gen_types), true, true);
684+
write_method_params(w, &$m.sig, "c_void", types, Some(&gen_types), true, true);
710685
write!(w, " {{\n\t").unwrap();
711686
write_method_var_decl_body(w, &$m.sig, "", types, Some(&gen_types), false);
712687
let mut takes_self = false;
@@ -732,7 +707,7 @@ fn writeln_impl<W: std::io::Write>(w: &mut W, i: &syn::ItemImpl, types: &mut Typ
732707
},
733708
_ => {},
734709
}
735-
write_method_call_params(w, &$m.sig, &trait_associated_types, "", types, Some(&gen_types), &real_type, false);
710+
write_method_call_params(w, &$m.sig, "", types, Some(&gen_types), &real_type, false);
736711
gen_types.pop_ctx();
737712
write!(w, "\n}}\n").unwrap();
738713
if let syn::ReturnType::Type(_, rtype) = &$m.sig.output {
@@ -819,7 +794,7 @@ fn writeln_impl<W: std::io::Write>(w: &mut W, i: &syn::ItemImpl, types: &mut Typ
819794
};
820795
gen_types.push_ctx();
821796
assert!(gen_types.learn_generics(&m.sig.generics, types));
822-
write_method_params(w, &m.sig, &HashMap::new(), &ret_type, types, Some(&gen_types), false, true);
797+
write_method_params(w, &m.sig, &ret_type, types, Some(&gen_types), false, true);
823798
write!(w, " {{\n\t").unwrap();
824799
write_method_var_decl_body(w, &m.sig, "", types, Some(&gen_types), false);
825800
let mut takes_self = false;
@@ -837,7 +812,7 @@ fn writeln_impl<W: std::io::Write>(w: &mut W, i: &syn::ItemImpl, types: &mut Typ
837812
} else {
838813
write!(w, "{}::{}::{}(", types.orig_crate, resolved_path, m.sig.ident).unwrap();
839814
}
840-
write_method_call_params(w, &m.sig, &HashMap::new(), "", types, Some(&gen_types), &ret_type, false);
815+
write_method_call_params(w, &m.sig, "", types, Some(&gen_types), &ret_type, false);
841816
gen_types.pop_ctx();
842817
writeln!(w, "\n}}\n").unwrap();
843818
}
@@ -1018,11 +993,11 @@ fn writeln_fn<'a, 'b, W: std::io::Write>(w: &mut W, f: &'a syn::ItemFn, types: &
1018993
if !gen_types.learn_generics(&f.sig.generics, types) { return; }
1019994

1020995
write!(w, "#[no_mangle]\npub extern \"C\" fn {}(", f.sig.ident).unwrap();
1021-
write_method_params(w, &f.sig, &HashMap::new(), "", types, Some(&gen_types), false, true);
996+
write_method_params(w, &f.sig, "", types, Some(&gen_types), false, true);
1022997
write!(w, " {{\n\t").unwrap();
1023998
write_method_var_decl_body(w, &f.sig, "", types, Some(&gen_types), false);
1024999
write!(w, "{}::{}::{}(", types.orig_crate, types.module_path, f.sig.ident).unwrap();
1025-
write_method_call_params(w, &f.sig, &HashMap::new(), "", types, Some(&gen_types), "", false);
1000+
write_method_call_params(w, &f.sig, "", types, Some(&gen_types), "", false);
10261001
writeln!(w, "\n}}\n").unwrap();
10271002
}
10281003

c-bindings-gen/src/types.rs

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,6 @@ pub fn get_single_remaining_path_seg<'a, I: Iterator<Item=&'a syn::PathSegment>>
3333
} else { None }
3434
}
3535

36-
pub fn assert_single_path_seg<'a>(p: &'a syn::Path) -> &'a syn::Ident {
37-
if p.leading_colon.is_some() { unimplemented!(); }
38-
get_single_remaining_path_seg(&mut p.segments.iter()).unwrap()
39-
}
40-
4136
pub fn single_ident_generic_path_to_ident(p: &syn::Path) -> Option<&syn::Ident> {
4237
if p.segments.len() == 1 {
4338
Some(&p.segments.iter().next().unwrap().ident)
@@ -134,6 +129,7 @@ impl<'a> GenericTypes<'a> {
134129

135130
/// Learn the generics in generics in the current context, given a TypeResolver.
136131
pub fn learn_generics<'b, 'c>(&mut self, generics: &'a syn::Generics, types: &'b TypeResolver<'a, 'c>) -> bool {
132+
// First learn simple generics...
137133
for generic in generics.params.iter() {
138134
match generic {
139135
syn::GenericParam::Type(type_param) => {
@@ -161,6 +157,7 @@ impl<'a> GenericTypes<'a> {
161157
_ => {},
162158
}
163159
}
160+
// Then find generics where we are required to pass a Deref<Target=X> and pretend its just X.
164161
if let Some(wh) = &generics.where_clause {
165162
for pred in wh.predicates.iter() {
166163
if let syn::WherePredicate::Type(t) = pred {
@@ -193,6 +190,38 @@ impl<'a> GenericTypes<'a> {
193190
true
194191
}
195192

193+
/// Learn the associated types from the trait in the current context.
194+
pub fn learn_associated_types<'b, 'c>(&mut self, t: &'a syn::ItemTrait, types: &'b TypeResolver<'a, 'c>) {
195+
for item in t.items.iter() {
196+
match item {
197+
&syn::TraitItem::Type(ref t) => {
198+
if t.default.is_some() || t.generics.lt_token.is_some() { unimplemented!(); }
199+
let mut bounds_iter = t.bounds.iter();
200+
match bounds_iter.next().unwrap() {
201+
syn::TypeParamBound::Trait(tr) => {
202+
assert_simple_bound(&tr);
203+
if let Some(mut path) = types.maybe_resolve_path(&tr.path, None) {
204+
if types.skip_path(&path) { continue; }
205+
// In general we handle Deref<Target=X> as if it were just X (and
206+
// implement Deref<Target=Self> for relevant types). We don't
207+
// bother to implement it for associated types, however, so we just
208+
// ignore such bounds.
209+
let new_ident = if path != "std::ops::Deref" {
210+
path = "crate::".to_string() + &path;
211+
Some(&tr.path)
212+
} else { None };
213+
self.typed_generics.last_mut().unwrap().insert(&t.ident, (path, new_ident));
214+
} else { unimplemented!(); }
215+
},
216+
_ => unimplemented!(),
217+
}
218+
if bounds_iter.next().is_some() { unimplemented!(); }
219+
},
220+
_ => {},
221+
}
222+
}
223+
}
224+
196225
/// Attempt to resolve an Ident as a generic parameter and return the full path.
197226
pub fn maybe_resolve_ident<'b>(&'b self, ident: &syn::Ident) -> Option<&'b String> {
198227
for gen in self.typed_generics.iter().rev() {
@@ -211,6 +240,18 @@ impl<'a> GenericTypes<'a> {
211240
return Some(res);
212241
}
213242
}
243+
} else {
244+
// Associated types are usually specified as "Self::Generic", so we check for that
245+
// explicitly here.
246+
let mut it = path.segments.iter();
247+
if path.segments.len() == 2 && format!("{}", it.next().unwrap().ident) == "Self" {
248+
let ident = &it.next().unwrap().ident;
249+
for gen in self.typed_generics.iter().rev() {
250+
if let Some(res) = gen.get(ident).map(|(a, b)| (a, b.unwrap())) {
251+
return Some(res);
252+
}
253+
}
254+
}
214255
}
215256
None
216257
}

0 commit comments

Comments
 (0)