//! Printing logic for basic blocks of Rust-mapped code - parts of functions and declarations but
//! not the full mapping logic.
-use std::collections::HashMap;
use std::fs::File;
use std::io::Write;
use proc_macro2::{TokenTree, Span};
///
/// this_param is used when returning Self or accepting a self parameter, and should be the
/// concrete, mapped type.
-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) {
+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) {
if sig.constness.is_some() || sig.asyncness.is_some() || sig.unsafety.is_some() ||
sig.abi.is_some() || sig.variadic.is_some() {
unimplemented!();
syn::ReturnType::Type(_, rtype) => {
write!(w, " -> ").unwrap();
if let Some(mut remaining_path) = first_seg_self(&*rtype) {
- if let Some(associated_seg) = get_single_remaining_path_seg(&mut remaining_path) {
- // We're returning an associated type in a trait impl. Its probably a safe bet
- // that its also a trait, so just return the trait type.
- let real_type = associated_types.get(associated_seg).unwrap();
- types.write_c_type(w, &syn::Type::Path(syn::TypePath { qself: None,
- path: syn::PathSegment {
- ident: (*real_type).clone(),
- arguments: syn::PathArguments::None
- }.into()
- }), generics, true);
- } else {
+ if remaining_path.next().is_none() {
write!(w, "{}", this_param).unwrap();
+ return;
}
+ }
+ if let syn::Type::Reference(r) = &**rtype {
+ // We can't return a reference, cause we allocate things on the stack.
+ types.write_c_type(w, &*r.elem, generics, true);
} else {
- if let syn::Type::Reference(r) = &**rtype {
- // We can't return a reference, cause we allocate things on the stack.
- types.write_c_type(w, &*r.elem, generics, true);
- } else {
- types.write_c_type(w, &*rtype, generics, true);
- }
+ types.write_c_type(w, &*rtype, generics, true);
}
},
_ => {},
///
/// The return value is expected to be bound to a variable named `ret` which is available after a
/// method-call-ending semicolon.
-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) {
+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) {
let mut first_arg = true;
let mut num_unused = 0;
for inp in sig.inputs.iter() {
} else if !to_c && first_seg_self(&*rtype).is_some() {
if let Some(mut remaining_path) = first_seg_self(&*rtype) {
if let Some(associated_seg) = get_single_remaining_path_seg(&mut remaining_path) {
- let real_type = associated_types.get(associated_seg).unwrap();
- if let Some(t) = types.crate_types.traits.get(&types.maybe_resolve_ident(&real_type).unwrap()) {
+ // Build a fake path with only associated_seg and resolve it:
+ let mut segments = syn::punctuated::Punctuated::new();
+ segments.push(syn::PathSegment {
+ ident: associated_seg.clone(), arguments: syn::PathArguments::None });
+ let (_, real_path) = generics.unwrap().maybe_resolve_path(&syn::Path {
+ leading_colon: None, segments }).unwrap();
+
+ assert_eq!(real_path.segments.len(), 1);
+ let real_ident = &real_path.segments.iter().next().unwrap().ident;
+ if let Some(t) = types.crate_types.traits.get(&types.maybe_resolve_ident(&real_ident).unwrap()) {
// We're returning an associated trait from a Rust fn call to a C trait
// object.
writeln!(w, "let mut rust_obj = {} {{ inner: Box::into_raw(Box::new(ret)), is_owned: true }};", this_type).unwrap();
}
} } }
-/// Gets a HashMap from name idents to the bounding trait for associated types.
-/// eg if a native trait has a "type T = TraitA", this will return a HashMap containing a mapping
-/// from "T" to "TraitA".
-fn learn_associated_types<'a>(t: &'a syn::ItemTrait) -> HashMap<&'a syn::Ident, &'a syn::Ident> {
- let mut associated_types = HashMap::new();
- for item in t.items.iter() {
- match item {
- &syn::TraitItem::Type(ref t) => {
- if t.default.is_some() || t.generics.lt_token.is_some() { unimplemented!(); }
- let mut bounds_iter = t.bounds.iter();
- match bounds_iter.next().unwrap() {
- syn::TypeParamBound::Trait(tr) => {
- assert_simple_bound(&tr);
- associated_types.insert(&t.ident, assert_single_path_seg(&tr.path));
- },
- _ => unimplemented!(),
- }
- if bounds_iter.next().is_some() { unimplemented!(); }
- },
- _ => {},
- }
- }
- associated_types
-}
-
/// Prints a C-mapped trait object containing a void pointer and a jump table for each function in
/// the original trait.
/// Implements the native Rust trait and relevant parent traits for the new C-mapped trait.
let mut gen_types = GenericTypes::new();
assert!(gen_types.learn_generics(&t.generics, types));
+ gen_types.learn_associated_types(&t, types);
writeln!(w, "#[repr(C)]\npub struct {} {{", trait_name).unwrap();
writeln!(w, "\tpub this_arg: *mut c_void,").unwrap();
- let associated_types = learn_associated_types(t);
let mut generated_fields = Vec::new(); // Every field's name except this_arg, used in Clone generation
for item in t.items.iter() {
match item {
write!(w, "\tpub {}: extern \"C\" fn (", m.sig.ident).unwrap();
generated_fields.push(format!("{}", m.sig.ident));
- write_method_params(w, &m.sig, &associated_types, "c_void", types, Some(&gen_types), true, false);
+ write_method_params(w, &m.sig, "c_void", types, Some(&gen_types), true, false);
writeln!(w, ",").unwrap();
gen_types.pop_ctx();
}
write_method_var_decl_body(w, &m.sig, "\t", types, Some(&gen_types), true);
write!(w, "(self.{})(", m.sig.ident).unwrap();
- write_method_call_params(w, &m.sig, &associated_types, "\t", types, Some(&gen_types), "", true);
+ write_method_call_params(w, &m.sig, "\t", types, Some(&gen_types), "", true);
writeln!(w, "\n\t}}").unwrap();
gen_types.pop_ctx();
// That's great, except that they are unresolved idents, so if we learn
// mappings from a trai defined in a different file, we may mis-resolve or
// fail to resolve the mapped types.
- let trait_associated_types = learn_associated_types(trait_obj);
+ gen_types.learn_associated_types(trait_obj, types);
let mut impl_associated_types = HashMap::new();
for item in i.items.iter() {
match item {
write!(w, "extern \"C\" fn {}_{}_{}(", ident, trait_obj.ident, $m.sig.ident).unwrap();
gen_types.push_ctx();
assert!(gen_types.learn_generics(&$m.sig.generics, types));
- write_method_params(w, &$m.sig, &trait_associated_types, "c_void", types, Some(&gen_types), true, true);
+ write_method_params(w, &$m.sig, "c_void", types, Some(&gen_types), true, true);
write!(w, " {{\n\t").unwrap();
write_method_var_decl_body(w, &$m.sig, "", types, Some(&gen_types), false);
let mut takes_self = false;
},
_ => {},
}
- write_method_call_params(w, &$m.sig, &trait_associated_types, "", types, Some(&gen_types), &real_type, false);
+ write_method_call_params(w, &$m.sig, "", types, Some(&gen_types), &real_type, false);
gen_types.pop_ctx();
write!(w, "\n}}\n").unwrap();
if let syn::ReturnType::Type(_, rtype) = &$m.sig.output {
};
gen_types.push_ctx();
assert!(gen_types.learn_generics(&m.sig.generics, types));
- write_method_params(w, &m.sig, &HashMap::new(), &ret_type, types, Some(&gen_types), false, true);
+ write_method_params(w, &m.sig, &ret_type, types, Some(&gen_types), false, true);
write!(w, " {{\n\t").unwrap();
write_method_var_decl_body(w, &m.sig, "", types, Some(&gen_types), false);
let mut takes_self = false;
} else {
write!(w, "{}::{}::{}(", types.orig_crate, resolved_path, m.sig.ident).unwrap();
}
- write_method_call_params(w, &m.sig, &HashMap::new(), "", types, Some(&gen_types), &ret_type, false);
+ write_method_call_params(w, &m.sig, "", types, Some(&gen_types), &ret_type, false);
gen_types.pop_ctx();
writeln!(w, "\n}}\n").unwrap();
}
if !gen_types.learn_generics(&f.sig.generics, types) { return; }
write!(w, "#[no_mangle]\npub extern \"C\" fn {}(", f.sig.ident).unwrap();
- write_method_params(w, &f.sig, &HashMap::new(), "", types, Some(&gen_types), false, true);
+ write_method_params(w, &f.sig, "", types, Some(&gen_types), false, true);
write!(w, " {{\n\t").unwrap();
write_method_var_decl_body(w, &f.sig, "", types, Some(&gen_types), false);
write!(w, "{}::{}::{}(", types.orig_crate, types.module_path, f.sig.ident).unwrap();
- write_method_call_params(w, &f.sig, &HashMap::new(), "", types, Some(&gen_types), "", false);
+ write_method_call_params(w, &f.sig, "", types, Some(&gen_types), "", false);
writeln!(w, "\n}}\n").unwrap();
}
} else { None }
}
-pub fn assert_single_path_seg<'a>(p: &'a syn::Path) -> &'a syn::Ident {
- if p.leading_colon.is_some() { unimplemented!(); }
- get_single_remaining_path_seg(&mut p.segments.iter()).unwrap()
-}
-
pub fn single_ident_generic_path_to_ident(p: &syn::Path) -> Option<&syn::Ident> {
if p.segments.len() == 1 {
Some(&p.segments.iter().next().unwrap().ident)
/// Learn the generics in generics in the current context, given a TypeResolver.
pub fn learn_generics<'b, 'c>(&mut self, generics: &'a syn::Generics, types: &'b TypeResolver<'a, 'c>) -> bool {
+ // First learn simple generics...
for generic in generics.params.iter() {
match generic {
syn::GenericParam::Type(type_param) => {
_ => {},
}
}
+ // Then find generics where we are required to pass a Deref<Target=X> and pretend its just X.
if let Some(wh) = &generics.where_clause {
for pred in wh.predicates.iter() {
if let syn::WherePredicate::Type(t) = pred {
true
}
+ /// Learn the associated types from the trait in the current context.
+ pub fn learn_associated_types<'b, 'c>(&mut self, t: &'a syn::ItemTrait, types: &'b TypeResolver<'a, 'c>) {
+ for item in t.items.iter() {
+ match item {
+ &syn::TraitItem::Type(ref t) => {
+ if t.default.is_some() || t.generics.lt_token.is_some() { unimplemented!(); }
+ let mut bounds_iter = t.bounds.iter();
+ match bounds_iter.next().unwrap() {
+ syn::TypeParamBound::Trait(tr) => {
+ assert_simple_bound(&tr);
+ if let Some(mut path) = types.maybe_resolve_path(&tr.path, None) {
+ if types.skip_path(&path) { continue; }
+ // In general we handle Deref<Target=X> as if it were just X (and
+ // implement Deref<Target=Self> for relevant types). We don't
+ // bother to implement it for associated types, however, so we just
+ // ignore such bounds.
+ let new_ident = if path != "std::ops::Deref" {
+ path = "crate::".to_string() + &path;
+ Some(&tr.path)
+ } else { None };
+ self.typed_generics.last_mut().unwrap().insert(&t.ident, (path, new_ident));
+ } else { unimplemented!(); }
+ },
+ _ => unimplemented!(),
+ }
+ if bounds_iter.next().is_some() { unimplemented!(); }
+ },
+ _ => {},
+ }
+ }
+ }
+
/// Attempt to resolve an Ident as a generic parameter and return the full path.
pub fn maybe_resolve_ident<'b>(&'b self, ident: &syn::Ident) -> Option<&'b String> {
for gen in self.typed_generics.iter().rev() {
return Some(res);
}
}
+ } else {
+ // Associated types are usually specified as "Self::Generic", so we check for that
+ // explicitly here.
+ let mut it = path.segments.iter();
+ if path.segments.len() == 2 && format!("{}", it.next().unwrap().ident) == "Self" {
+ let ident = &it.next().unwrap().ident;
+ for gen in self.typed_generics.iter().rev() {
+ if let Some(res) = gen.get(ident).map(|(a, b)| (a, b.unwrap())) {
+ return Some(res);
+ }
+ }
+ }
}
None
}