Skip to content

Commit 174d48b

Browse files
adetaylordtolnay
authored andcommitted
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 fabca77 commit 174d48b

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
@@ -154,6 +154,9 @@ fn write_includes(out: &mut OutFile, types: &Types) {
154154
_ => {}
155155
}
156156
}
157+
if !types.unique_ptr_impls.is_empty() {
158+
out.include.memory = true;
159+
}
157160
}
158161

159162
fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) {
@@ -1054,13 +1057,6 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) {
10541057
write_rust_vec_extern(out, inner);
10551058
}
10561059
}
1057-
} else if let Type::UniquePtr(ptr) = ty {
1058-
if let Type::Ident(inner) = &ptr.inner {
1059-
if Atom::from(inner).is_none() && !types.aliases.contains_key(inner) {
1060-
out.next_section();
1061-
write_unique_ptr(out, inner, types);
1062-
}
1063-
}
10641060
} else if let Type::CxxVector(ptr) = ty {
10651061
if let Type::Ident(inner) = &ptr.inner {
10661062
if Atom::from(inner).is_none() && !types.aliases.contains_key(inner) {
@@ -1070,6 +1066,12 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) {
10701066
}
10711067
}
10721068
}
1069+
1070+
for inner in types.unique_ptr_impls.into_iter() {
1071+
out.next_section();
1072+
write_unique_ptr(out, inner, types);
1073+
}
1074+
10731075
out.end_block("extern \"C\"");
10741076

10751077
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) => {
@@ -74,12 +74,6 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream {
7474
hidden.extend(expand_rust_vec(namespace, ident));
7575
}
7676
}
77-
} else if let Type::UniquePtr(ptr) = ty {
78-
if let Type::Ident(ident) = &ptr.inner {
79-
if Atom::from(ident).is_none() && !types.aliases.contains_key(ident) {
80-
expanded.extend(expand_unique_ptr(namespace, ident, types));
81-
}
82-
}
8377
} else if let Type::CxxVector(ptr) = ty {
8478
if let Type::Ident(ident) = &ptr.inner {
8579
if Atom::from(ident).is_none() && !types.aliases.contains_key(ident) {
@@ -92,6 +86,10 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream {
9286
}
9387
}
9488

89+
for ident in types.unique_ptr_impls.into_iter() {
90+
expanded.extend(expand_unique_ptr(namespace, ident, types));
91+
}
92+
9593
// Work around https://github.com/rust-lang/rust/issues/67851.
9694
if !hidden.is_empty() {
9795
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 {
@@ -87,6 +88,13 @@ pub struct TypeAlias {
8788
pub semi_token: Token![;],
8889
}
8990

91+
pub struct Impl {
92+
pub impl_token: Token![impl],
93+
pub trait_ident: Ident,
94+
pub for_token: Token![for],
95+
pub ident: Ident,
96+
}
97+
9098
pub struct Signature {
9199
pub unsafety: Option<Token![unsafe]>,
92100
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
}
@@ -621,3 +622,48 @@ fn parse_return_type(
621622
ty => Ok(Some(ty)),
622623
}
623624
}
625+
626+
fn parse_impl(cx: &mut Errors, imp: ItemImpl, api: &mut Vec<Api>) {
627+
// At present we only support impl UniquePtrTarget for <ty>;
628+
if !imp.items.is_empty() {
629+
cx.error(imp, "impl must be empty");
630+
return;
631+
}
632+
match &imp.trait_ {
633+
None => cx.error(imp, "impl must be for a trait"),
634+
Some((_, ty_path, for_token)) => {
635+
let ident = ty_path.get_ident();
636+
match ident {
637+
None => {
638+
cx.error(imp, "path incomplete");
639+
return;
640+
}
641+
Some(trait_ident) if trait_ident.to_string() == "UniquePtrTarget" => {
642+
let ty = parse_type(&imp.self_ty);
643+
match ty {
644+
Err(_) => {
645+
cx.error(imp, "unable to parse type");
646+
return;
647+
}
648+
Ok(ty) => match ty {
649+
Type::Ident(ty) => {
650+
api.push(Api::Impl(Impl {
651+
for_token: for_token.clone(),
652+
trait_ident: trait_ident.clone(),
653+
impl_token: imp.impl_token,
654+
ident: ty,
655+
}));
656+
}
657+
_ => cx
658+
.error(imp, "can only impl UniquePtrTarget for a plain identifier"),
659+
},
660+
}
661+
}
662+
Some(_) => cx.error(
663+
imp,
664+
"only supported trait which can be implemented is UniquePtrTarget",
665+
),
666+
}
667+
}
668+
}
669+
}

syntax/set.rs

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

3539
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: Map<&'a Ident, TrivialReason<'a>>,
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

@@ -170,6 +175,16 @@ impl<'a> Types<'a> {
170175
}
171176
}
172177

178+
for ty in &all {
179+
if let Type::UniquePtr(ptr) = ty {
180+
if let Type::Ident(ident) = &ptr.inner {
181+
if Atom::from(ident).is_none() && !aliases.contains_key(ident) {
182+
unique_ptr_impls.insert(ident);
183+
}
184+
}
185+
}
186+
}
187+
173188
Types {
174189
all,
175190
structs,
@@ -179,6 +194,7 @@ impl<'a> Types<'a> {
179194
aliases,
180195
untrusted,
181196
required_trivial,
197+
unique_ptr_impls,
182198
}
183199
}
184200

0 commit comments

Comments
 (0)