Skip to content

Commit b45c13f

Browse files
committed
Allow impl UniquePtrTarget for T;
cxx allows UniquePtr<T> only if T implements a trait, UniquePtrTarget. This trait implementation is automatically generated for type T only if: * UniquePtr<T> is used in a struct or a function parameter/return type; AND * the type T is a C++ type. Until now, it was not possible to generate UniquePtrTarget implementations for type aliases where a cxx type refers to an externally defined type (e.g. from bindgen). There were also legitimate uses for UniquePtr<T> even if none of the structs/functions within the cxx::bridge actually used it; for example storing a UniquePtr<T> in some other struct outside the cxx::bridge mod. This change allows any cxx::bridge block to contain impl UniquePtrTarget for T {} which will force cxx to generate the UniquePtrTarget trait (and associated C++ code) for that type. It's not desirable to do this for _every_ type since it does generate additional C++ code as well as Rust code, and thus in some toolchains may cause binary bloat. Future changes could allow similar syntax for types which should be containable in a C++ vector, e.g.: impl VectorElement for T {} but that's not yet done here.
1 parent feb0dc1 commit b45c13f

File tree

8 files changed

+97
-21
lines changed

8 files changed

+97
-21
lines changed

gen/src/write.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,9 @@ fn write_includes(out: &mut OutFile, types: &Types) {
148148
_ => {}
149149
}
150150
}
151+
if !types.unique_ptr_impls.is_empty() {
152+
out.include.memory = true;
153+
}
151154
}
152155

153156
fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) {
@@ -1030,13 +1033,6 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) {
10301033
write_rust_vec_extern(out, inner);
10311034
}
10321035
}
1033-
} else if let Type::UniquePtr(ptr) = ty {
1034-
if let Type::Ident(inner) = &ptr.inner {
1035-
if Atom::from(inner).is_none() && !types.aliases.contains_key(inner) {
1036-
out.next_section();
1037-
write_unique_ptr(out, inner, types);
1038-
}
1039-
}
10401036
} else if let Type::CxxVector(ptr) = ty {
10411037
if let Type::Ident(inner) = &ptr.inner {
10421038
if Atom::from(inner).is_none() && !types.aliases.contains_key(inner) {
@@ -1046,6 +1042,12 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) {
10461042
}
10471043
}
10481044
}
1045+
1046+
for inner in types.unique_ptr_impls.into_iter() {
1047+
out.next_section();
1048+
write_unique_ptr(out, inner, types);
1049+
}
1050+
10491051
out.end_block("extern \"C\"");
10501052

10511053
out.begin_block("namespace rust");

macro/src/expand.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream {
3939

4040
for api in apis {
4141
match api {
42-
Api::Include(_) | Api::RustType(_) => {}
42+
Api::Include(_) | Api::RustType(_) | Api::Impl(_) => {}
4343
Api::Struct(strct) => expanded.extend(expand_struct(strct)),
4444
Api::Enum(enm) => expanded.extend(expand_enum(enm)),
4545
Api::CxxType(ety) => {
@@ -78,12 +78,6 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream {
7878
hidden.extend(expand_rust_vec(namespace, ident));
7979
}
8080
}
81-
} else if let Type::UniquePtr(ptr) = ty {
82-
if let Type::Ident(ident) = &ptr.inner {
83-
if Atom::from(ident).is_none() && !types.aliases.contains_key(ident) {
84-
expanded.extend(expand_unique_ptr(namespace, ident, types));
85-
}
86-
}
8781
} else if let Type::CxxVector(ptr) = ty {
8882
if let Type::Ident(ident) = &ptr.inner {
8983
if Atom::from(ident).is_none() && !types.aliases.contains_key(ident) {
@@ -96,6 +90,10 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream {
9690
}
9791
}
9892

93+
for ident in types.unique_ptr_impls.into_iter() {
94+
expanded.extend(expand_unique_ptr(namespace, ident, types));
95+
}
96+
9997
// Work around https://github.com/rust-lang/rust/issues/67851.
10098
if !hidden.is_empty() {
10199
expanded.extend(quote! {

syntax/file.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use crate::syntax::namespace::Namespace;
22
use quote::quote;
33
use syn::parse::{Error, Parse, ParseStream, Result};
44
use syn::{
5-
braced, token, Abi, Attribute, ForeignItem, Ident, Item as RustItem, ItemEnum, ItemStruct,
6-
ItemUse, LitStr, Token, Visibility,
5+
braced, token, Abi, Attribute, ForeignItem, Ident, Item as RustItem, ItemEnum, ItemImpl,
6+
ItemStruct, ItemUse, LitStr, Token, Visibility,
77
};
88

99
pub struct Module {
@@ -22,6 +22,7 @@ pub enum Item {
2222
Enum(ItemEnum),
2323
ForeignMod(ItemForeignMod),
2424
Use(ItemUse),
25+
Impl(ItemImpl),
2526
Other(RustItem),
2627
}
2728

@@ -99,6 +100,7 @@ impl Parse for Item {
99100
brace_token: item.brace_token,
100101
items: item.items,
101102
})),
103+
RustItem::Impl(item) => Ok(Item::Impl(ItemImpl { attrs, ..item })),
102104
RustItem::Use(item) => Ok(Item::Use(ItemUse { attrs, ..item })),
103105
other => Ok(Item::Other(other)),
104106
}

syntax/ident.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ pub(crate) fn check_all(cx: &mut Check, namespace: &Namespace, apis: &[Api]) {
2020

2121
for api in apis {
2222
match api {
23-
Api::Include(_) => {}
23+
Api::Include(_) | Api::Impl(_) => {}
2424
Api::Struct(strct) => {
2525
check(cx, &strct.ident);
2626
for field in &strct.fields {

syntax/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ pub enum Api {
4242
RustType(ExternType),
4343
RustFunction(ExternFn),
4444
TypeAlias(TypeAlias),
45+
Impl(Impl),
4546
}
4647

4748
pub struct ExternType {
@@ -86,6 +87,13 @@ pub struct TypeAlias {
8687
pub semi_token: Token![;],
8788
}
8889

90+
pub struct Impl {
91+
pub impl_token: Token![impl],
92+
pub trait_ident: Ident,
93+
pub for_token: Token![for],
94+
pub ident: Ident,
95+
}
96+
8997
pub struct Signature {
9098
pub unsafety: Option<Token![unsafe]>,
9199
pub fn_token: Token![fn],

syntax/parse.rs

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,17 @@ use crate::syntax::file::{Item, ItemForeignMod};
33
use crate::syntax::report::Errors;
44
use crate::syntax::Atom::*;
55
use crate::syntax::{
6-
attrs, error, Api, Doc, Enum, ExternFn, ExternType, Lang, Receiver, Ref, Signature, Slice,
7-
Struct, Ty1, Type, TypeAlias, Var, Variant,
6+
attrs, error, Api, Doc, Enum, ExternFn, ExternType, Impl, Lang, Receiver, Ref, Signature,
7+
Slice, Struct, Ty1, Type, TypeAlias, Var, Variant,
88
};
99
use proc_macro2::{TokenStream, TokenTree};
1010
use quote::{format_ident, quote, quote_spanned};
1111
use syn::parse::{ParseStream, Parser};
1212
use syn::punctuated::Punctuated;
1313
use syn::{
1414
Abi, Attribute, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType,
15-
GenericArgument, Ident, ItemEnum, ItemStruct, LitStr, Pat, PathArguments, Result, ReturnType,
16-
Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice,
15+
GenericArgument, Ident, ItemEnum, ItemImpl, ItemStruct, LitStr, Pat, PathArguments, Result,
16+
ReturnType, Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice,
1717
};
1818

1919
pub mod kw {
@@ -34,6 +34,7 @@ pub fn parse_items(cx: &mut Errors, items: Vec<Item>, trusted: bool) -> Vec<Api>
3434
},
3535
Item::ForeignMod(foreign_mod) => parse_foreign_mod(cx, foreign_mod, &mut apis, trusted),
3636
Item::Use(item) => cx.error(item, error::USE_NOT_ALLOWED),
37+
Item::Impl(item) => parse_impl(cx, item, &mut apis),
3738
Item::Other(item) => cx.error(item, "unsupported item"),
3839
}
3940
}
@@ -620,3 +621,48 @@ fn parse_return_type(
620621
ty => Ok(Some(ty)),
621622
}
622623
}
624+
625+
fn parse_impl(cx: &mut Errors, imp: ItemImpl, api: &mut Vec<Api>) {
626+
// At present we only support impl UniquePtrTarget for <ty>;
627+
if !imp.items.is_empty() {
628+
cx.error(imp, "impl must be empty");
629+
return;
630+
}
631+
match &imp.trait_ {
632+
None => cx.error(imp, "impl must be for a trait"),
633+
Some((_, ty_path, for_token)) => {
634+
let ident = ty_path.get_ident();
635+
match ident {
636+
None => {
637+
cx.error(imp, "path incomplete");
638+
return;
639+
}
640+
Some(trait_ident) if trait_ident.to_string() == "UniquePtrTarget" => {
641+
let ty = parse_type(&imp.self_ty);
642+
match ty {
643+
Err(_) => {
644+
cx.error(imp, "unable to parse type");
645+
return;
646+
}
647+
Ok(ty) => match ty {
648+
Type::Ident(ty) => {
649+
api.push(Api::Impl(Impl {
650+
for_token: for_token.clone(),
651+
trait_ident: trait_ident.clone(),
652+
impl_token: imp.impl_token,
653+
ident: ty,
654+
}));
655+
}
656+
_ => cx
657+
.error(imp, "can only impl UniquePtrTarget for a plain identifier"),
658+
},
659+
}
660+
}
661+
Some(_) => cx.error(
662+
imp,
663+
"only supported trait which can be implemented is UniquePtrTarget",
664+
),
665+
}
666+
}
667+
}
668+
}

syntax/set.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ where
2929
pub fn contains(&self, value: &T) -> bool {
3030
self.set.contains(value)
3131
}
32+
33+
pub fn is_empty(&self) -> bool {
34+
self.set.is_empty()
35+
}
3236
}
3337

3438
impl<'s, 'a, T> IntoIterator for &'s OrderedSet<&'a T> {

syntax/types.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub struct Types<'a> {
1515
pub aliases: Map<&'a Ident, &'a TypeAlias>,
1616
pub untrusted: Map<&'a Ident, &'a ExternType>,
1717
pub required_trivial_aliases: Set<&'a Ident>,
18+
pub unique_ptr_impls: Set<&'a Ident>,
1819
}
1920

2021
impl<'a> Types<'a> {
@@ -26,6 +27,7 @@ impl<'a> Types<'a> {
2627
let mut rust = Set::new();
2728
let mut aliases = Map::new();
2829
let mut untrusted = Map::new();
30+
let mut unique_ptr_impls = Set::new();
2931

3032
fn visit<'a>(all: &mut Set<&'a Type>, ty: &'a Type) {
3133
all.insert(ty);
@@ -133,6 +135,9 @@ impl<'a> Types<'a> {
133135
cxx.insert(ident);
134136
aliases.insert(ident, alias);
135137
}
138+
Api::Impl(imp) => {
139+
unique_ptr_impls.insert(&imp.ident);
140+
}
136141
}
137142
}
138143

@@ -185,6 +190,16 @@ impl<'a> Types<'a> {
185190
}
186191
}
187192

193+
for ty in &all {
194+
if let Type::UniquePtr(ptr) = ty {
195+
if let Type::Ident(ident) = &ptr.inner {
196+
if Atom::from(ident).is_none() && !aliases.contains_key(ident) {
197+
unique_ptr_impls.insert(ident);
198+
}
199+
}
200+
}
201+
}
202+
188203
Types {
189204
all,
190205
structs,
@@ -194,6 +209,7 @@ impl<'a> Types<'a> {
194209
aliases,
195210
untrusted,
196211
required_trivial_aliases,
212+
unique_ptr_impls,
197213
}
198214
}
199215

0 commit comments

Comments
 (0)