Skip to content

Commit 064c67e

Browse files
committed
Allow aliases and C++ opaque types to be trivial.
This change allows aliases: type Foo = bindgen::Bar; and C++ opaque types: type Foo; to declare that they're 'trivial' in the sense that: * They have trivial move constructors * They have no destructors and therefore may be passed and owned by value in Rust. A subsequent commit will add C++ static assertions. This commit is a BREAKING CHANGE as it requires existing ExternTypes to gain a new associated type
1 parent d22baba commit 064c67e

File tree

5 files changed

+82
-1
lines changed

5 files changed

+82
-1
lines changed

macro/src/expand.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream {
5757
Api::TypeAlias(alias) => {
5858
expanded.extend(expand_type_alias(alias));
5959
hidden.extend(expand_type_alias_verify(namespace, alias));
60+
let ident = &alias.ident;
61+
if types.required_trivial_aliases.contains(ident) {
62+
hidden.extend(expand_type_alias_kind_trivial_verify(alias));
63+
}
6064
}
6165
}
6266
}
@@ -179,6 +183,7 @@ fn expand_cxx_type(namespace: &Namespace, ety: &ExternType) -> TokenStream {
179183

180184
unsafe impl ::cxx::ExternType for #ident {
181185
type Id = #type_id;
186+
type Kind = ::cxx::Opaque;
182187
}
183188
}
184189
}
@@ -677,6 +682,18 @@ fn expand_type_alias_verify(namespace: &Namespace, alias: &TypeAlias) -> TokenSt
677682
}
678683
}
679684

685+
fn expand_type_alias_kind_trivial_verify(type_alias: &TypeAlias) -> TokenStream {
686+
let ident = &type_alias.ident;
687+
let begin_span = type_alias.type_token.span;
688+
let end_span = type_alias.semi_token.span;
689+
let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_kind::<);
690+
let end = quote_spanned!(end_span=> >);
691+
692+
quote! {
693+
const _: fn() = #begin #ident, ::cxx::Trivial #end;
694+
}
695+
}
696+
680697
fn type_id(namespace: &Namespace, ident: &Ident) -> TokenStream {
681698
let mut path = String::new();
682699
for name in namespace {

src/extern_type.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,28 @@ pub unsafe trait ExternType {
104104
/// # }
105105
/// ```
106106
type Id;
107+
108+
/// Either `kind::Opaque` or `kind::Trivial`. If in doubt, use
109+
/// `kind::Opaque`.
110+
type Kind;
111+
}
112+
113+
pub(crate) mod kind {
114+
115+
/// An opaque type which can't be passed or held by value within Rust.
116+
/// For example, a C++ type with a destructor, or a non-trivial move
117+
/// constructor. Rust's strict move semantics mean that we can't own
118+
/// these by value in Rust, but they can still be owned by a
119+
/// `UniquePtr`...
120+
pub struct Opaque;
121+
122+
/// A type with trivial move constructors and no destructor, which
123+
/// can therefore be owned and moved around in Rust code directly.
124+
pub struct Trivial;
107125
}
108126

109127
#[doc(hidden)]
110128
pub fn verify_extern_type<T: ExternType<Id = Id>, Id>() {}
129+
130+
#[doc(hidden)]
131+
pub fn verify_extern_kind<T: ExternType<Kind = Kind>, Kind>() {}

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,8 @@ pub use crate::cxx_string::CxxString;
396396
pub use crate::cxx_vector::CxxVector;
397397
pub use crate::exception::Exception;
398398
pub use crate::extern_type::ExternType;
399+
pub use crate::extern_type::kind::Opaque;
400+
pub use crate::extern_type::kind::Trivial;
399401
pub use crate::unique_ptr::UniquePtr;
400402
pub use cxxbridge_macro::bridge;
401403

@@ -422,6 +424,7 @@ pub type Vector<T> = CxxVector<T>;
422424
#[doc(hidden)]
423425
pub mod private {
424426
pub use crate::cxx_vector::VectorElement;
427+
pub use crate::extern_type::verify_extern_kind;
425428
pub use crate::extern_type::verify_extern_type;
426429
pub use crate::function::FatFunction;
427430
pub use crate::opaque::Opaque;

syntax/check.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,7 @@ fn is_unsized(cx: &mut Check, ty: &Type) -> bool {
338338
|| cx.types.cxx.contains(ident)
339339
&& !cx.types.structs.contains_key(ident)
340340
&& !cx.types.enums.contains_key(ident)
341+
&& !cx.types.required_trivial_aliases.contains(ident)
341342
|| cx.types.rust.contains(ident)
342343
}
343344

@@ -376,7 +377,11 @@ fn describe(cx: &mut Check, ty: &Type) -> String {
376377
} else if cx.types.enums.contains_key(ident) {
377378
"enum".to_owned()
378379
} else if cx.types.cxx.contains(ident) {
379-
"C++ type".to_owned()
380+
if cx.types.required_trivial_aliases.contains(ident) {
381+
"trivial C++ type".to_owned()
382+
} else {
383+
"non-trivial C++ type".to_owned()
384+
}
380385
} else if cx.types.rust.contains(ident) {
381386
"opaque Rust type".to_owned()
382387
} else if Atom::from(ident) == Some(CxxString) {

syntax/types.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ pub struct Types<'a> {
1414
pub rust: Set<&'a Ident>,
1515
pub aliases: Map<&'a Ident, &'a TypeAlias>,
1616
pub untrusted: Map<&'a Ident, &'a ExternType>,
17+
pub required_trivial_aliases: Set<&'a Ident>,
1718
}
1819

1920
impl<'a> Types<'a> {
@@ -135,6 +136,39 @@ impl<'a> Types<'a> {
135136
}
136137
}
137138

139+
// All these APIs may contain types passed by value. We need to ensure
140+
// we check that this is permissible. We do this _after_ scanning all
141+
// the APIs above, in case some function or struct references a type
142+
// which is declared subsequently.
143+
let mut required_trivial_aliases = Set::new();
144+
145+
fn insist_alias_types_are_trivial<'c>(required_trivial_aliases: &mut Set<&'c Ident>, aliases: &Map<&'c Ident, &'c TypeAlias>, ty: &'c Type) {
146+
if let Type::Ident(ident) = ty {
147+
if aliases.contains_key(ident) {
148+
required_trivial_aliases.insert(ident);
149+
}
150+
}
151+
}
152+
153+
for api in apis {
154+
match api {
155+
Api::Struct(strct) => {
156+
for field in &strct.fields {
157+
insist_alias_types_are_trivial(&mut required_trivial_aliases, &aliases, &field.ty);
158+
}
159+
},
160+
Api::CxxFunction(efn) | Api::RustFunction(efn) => {
161+
for arg in &efn.args {
162+
insist_alias_types_are_trivial(&mut required_trivial_aliases, &aliases, &arg.ty);
163+
}
164+
if let Some(ret) = &efn.ret {
165+
insist_alias_types_are_trivial(&mut required_trivial_aliases, &aliases, &ret);
166+
}
167+
},
168+
_ => {}
169+
}
170+
}
171+
138172
Types {
139173
all,
140174
structs,
@@ -143,6 +177,7 @@ impl<'a> Types<'a> {
143177
rust,
144178
aliases,
145179
untrusted,
180+
required_trivial_aliases,
146181
}
147182
}
148183

0 commit comments

Comments
 (0)