diff --git a/gen/src/write.rs b/gen/src/write.rs index 4276e9b09..71aa3658a 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -154,6 +154,9 @@ fn write_includes(out: &mut OutFile, types: &Types) { _ => {} } } + if !types.unique_ptr_impls.is_empty() { + out.include.memory = true; + } } fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { @@ -1054,13 +1057,6 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { write_rust_vec_extern(out, inner); } } - } else if let Type::UniquePtr(ptr) = ty { - if let Type::Ident(inner) = &ptr.inner { - if Atom::from(inner).is_none() && !types.aliases.contains_key(inner) { - out.next_section(); - write_unique_ptr(out, inner, types); - } - } } else if let Type::CxxVector(ptr) = ty { if let Type::Ident(inner) = &ptr.inner { if Atom::from(inner).is_none() && !types.aliases.contains_key(inner) { @@ -1070,6 +1066,12 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { } } } + + for inner in types.unique_ptr_impls.into_iter() { + out.next_section(); + write_unique_ptr(out, inner, types); + } + out.end_block("extern \"C\""); out.begin_block("namespace rust"); diff --git a/macro/src/expand.rs b/macro/src/expand.rs index e7b28b984..c7e01b95d 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -39,7 +39,7 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { for api in apis { match api { - Api::Include(_) | Api::RustType(_) => {} + Api::Include(_) | Api::RustType(_) | Api::Impl(_) => {} Api::Struct(strct) => expanded.extend(expand_struct(strct)), Api::Enum(enm) => expanded.extend(expand_enum(enm)), Api::CxxType(ety) => { @@ -74,12 +74,6 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { hidden.extend(expand_rust_vec(namespace, ident)); } } - } else if let Type::UniquePtr(ptr) = ty { - if let Type::Ident(ident) = &ptr.inner { - if Atom::from(ident).is_none() && !types.aliases.contains_key(ident) { - expanded.extend(expand_unique_ptr(namespace, ident, types)); - } - } } else if let Type::CxxVector(ptr) = ty { if let Type::Ident(ident) = &ptr.inner { if Atom::from(ident).is_none() && !types.aliases.contains_key(ident) { @@ -92,6 +86,10 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { } } + for ident in types.unique_ptr_impls.into_iter() { + expanded.extend(expand_unique_ptr(namespace, ident, types)); + } + // Work around https://github.com/rust-lang/rust/issues/67851. if !hidden.is_empty() { expanded.extend(quote! { diff --git a/syntax/file.rs b/syntax/file.rs index 8b86adc73..931ce6e8d 100644 --- a/syntax/file.rs +++ b/syntax/file.rs @@ -2,8 +2,8 @@ use crate::syntax::namespace::Namespace; use quote::quote; use syn::parse::{Error, Parse, ParseStream, Result}; use syn::{ - braced, token, Abi, Attribute, ForeignItem, Ident, Item as RustItem, ItemEnum, ItemStruct, - ItemUse, LitStr, Token, Visibility, + braced, token, Abi, Attribute, ForeignItem, Ident, Item as RustItem, ItemEnum, ItemImpl, + ItemStruct, ItemUse, LitStr, Token, Visibility, }; pub struct Module { @@ -22,6 +22,7 @@ pub enum Item { Enum(ItemEnum), ForeignMod(ItemForeignMod), Use(ItemUse), + Impl(ItemImpl), Other(RustItem), } @@ -99,6 +100,7 @@ impl Parse for Item { brace_token: item.brace_token, items: item.items, })), + RustItem::Impl(item) => Ok(Item::Impl(ItemImpl { attrs, ..item })), RustItem::Use(item) => Ok(Item::Use(ItemUse { attrs, ..item })), other => Ok(Item::Other(other)), } diff --git a/syntax/ident.rs b/syntax/ident.rs index 7545e92ce..74e779903 100644 --- a/syntax/ident.rs +++ b/syntax/ident.rs @@ -20,7 +20,7 @@ pub(crate) fn check_all(cx: &mut Check, namespace: &Namespace, apis: &[Api]) { for api in apis { match api { - Api::Include(_) => {} + Api::Include(_) | Api::Impl(_) => {} Api::Struct(strct) => { check(cx, &strct.ident); for field in &strct.fields { diff --git a/syntax/mod.rs b/syntax/mod.rs index 8ef3c8b9c..18854973e 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -42,6 +42,7 @@ pub enum Api { RustType(ExternType), RustFunction(ExternFn), TypeAlias(TypeAlias), + Impl(Impl), } pub struct ExternType { @@ -87,6 +88,13 @@ pub struct TypeAlias { pub semi_token: Token![;], } +pub struct Impl { + pub impl_token: Token![impl], + pub trait_ident: Ident, + pub for_token: Token![for], + pub ident: Ident, +} + pub struct Signature { pub unsafety: Option, pub fn_token: Token![fn], diff --git a/syntax/parse.rs b/syntax/parse.rs index cec818d37..e9fac2fd6 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -3,8 +3,8 @@ use crate::syntax::file::{Item, ItemForeignMod}; use crate::syntax::report::Errors; use crate::syntax::Atom::*; use crate::syntax::{ - attrs, error, Api, Doc, Enum, ExternFn, ExternType, Lang, Receiver, Ref, Signature, Slice, - Struct, Ty1, Type, TypeAlias, Var, Variant, + attrs, error, Api, Doc, Enum, ExternFn, ExternType, Impl, Lang, Receiver, Ref, Signature, + Slice, Struct, Ty1, Type, TypeAlias, Var, Variant, }; use proc_macro2::{TokenStream, TokenTree}; use quote::{format_ident, quote, quote_spanned}; @@ -12,8 +12,8 @@ use syn::parse::{ParseStream, Parser}; use syn::punctuated::Punctuated; use syn::{ Abi, Attribute, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, - GenericArgument, Ident, ItemEnum, ItemStruct, LitStr, Pat, PathArguments, Result, ReturnType, - Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice, + GenericArgument, Ident, ItemEnum, ItemImpl, ItemStruct, LitStr, Pat, PathArguments, Result, + ReturnType, Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice, }; pub mod kw { @@ -34,6 +34,7 @@ pub fn parse_items(cx: &mut Errors, items: Vec, trusted: bool) -> Vec }, Item::ForeignMod(foreign_mod) => parse_foreign_mod(cx, foreign_mod, &mut apis, trusted), Item::Use(item) => cx.error(item, error::USE_NOT_ALLOWED), + Item::Impl(item) => parse_impl(cx, item, &mut apis), Item::Other(item) => cx.error(item, "unsupported item"), } } @@ -621,3 +622,48 @@ fn parse_return_type( ty => Ok(Some(ty)), } } + +fn parse_impl(cx: &mut Errors, imp: ItemImpl, api: &mut Vec) { + // At present we only support impl UniquePtrTarget for ; + if !imp.items.is_empty() { + cx.error(imp, "impl must be empty"); + return; + } + match &imp.trait_ { + None => cx.error(imp, "impl must be for a trait"), + Some((_, ty_path, for_token)) => { + let ident = ty_path.get_ident(); + match ident { + None => { + cx.error(imp, "path incomplete"); + return; + } + Some(trait_ident) if trait_ident.to_string() == "UniquePtrTarget" => { + let ty = parse_type(&imp.self_ty); + match ty { + Err(_) => { + cx.error(imp, "unable to parse type"); + return; + } + Ok(ty) => match ty { + Type::Ident(ty) => { + api.push(Api::Impl(Impl { + for_token: for_token.clone(), + trait_ident: trait_ident.clone(), + impl_token: imp.impl_token, + ident: ty, + })); + } + _ => cx + .error(imp, "can only impl UniquePtrTarget for a plain identifier"), + }, + } + } + Some(_) => cx.error( + imp, + "only supported trait which can be implemented is UniquePtrTarget", + ), + } + } + } +} diff --git a/syntax/set.rs b/syntax/set.rs index b553169f3..c6f9429f7 100644 --- a/syntax/set.rs +++ b/syntax/set.rs @@ -30,6 +30,10 @@ where pub fn contains(&self, value: &T) -> bool { self.set.contains(value) } + + pub fn is_empty(&self) -> bool { + self.set.is_empty() + } } impl<'s, 'a, T> IntoIterator for &'s OrderedSet<&'a T> { diff --git a/syntax/types.rs b/syntax/types.rs index 6924a780b..e7bf3dc00 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -15,6 +15,7 @@ pub struct Types<'a> { pub aliases: Map<&'a Ident, &'a TypeAlias>, pub untrusted: Map<&'a Ident, &'a ExternType>, pub required_trivial: Map<&'a Ident, TrivialReason<'a>>, + pub unique_ptr_impls: Set<&'a Ident>, } impl<'a> Types<'a> { @@ -26,6 +27,7 @@ impl<'a> Types<'a> { let mut rust = Set::new(); let mut aliases = Map::new(); let mut untrusted = Map::new(); + let mut unique_ptr_impls = Set::new(); fn visit<'a>(all: &mut Set<&'a Type>, ty: &'a Type) { all.insert(ty); @@ -133,6 +135,9 @@ impl<'a> Types<'a> { cxx.insert(ident); aliases.insert(ident, alias); } + Api::Impl(imp) => { + unique_ptr_impls.insert(&imp.ident); + } } } @@ -170,6 +175,16 @@ impl<'a> Types<'a> { } } + for ty in &all { + if let Type::UniquePtr(ptr) = ty { + if let Type::Ident(ident) = &ptr.inner { + if Atom::from(ident).is_none() && !aliases.contains_key(ident) { + unique_ptr_impls.insert(ident); + } + } + } + } + Types { all, structs, @@ -179,6 +194,7 @@ impl<'a> Types<'a> { aliases, untrusted, required_trivial, + unique_ptr_impls, } }