Skip to content

Commit 8dbb7b2

Browse files
committed
Support for type aliases in extern "Rust" section.
Fixes https://crbug.com/433254489
1 parent 1686225 commit 8dbb7b2

File tree

14 files changed

+242
-158
lines changed

14 files changed

+242
-158
lines changed

book/package-lock.json

Lines changed: 94 additions & 118 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

book/src/extern-rust.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,3 +163,46 @@ mod ffi {
163163

164164
Bounds on a lifetime (like `<'a, 'b: 'a>`) are not currently supported. Nor are
165165
type parameters or where-clauses.
166+
167+
## Reusing existing binding types
168+
169+
Extern Rust types support a syntax for declaring that a C++ binding of the
170+
given Rust type already exists outside of the current bridge module. This
171+
avoids generating a separate binding which would clash with the first one
172+
(e.g. one possible error is: "conflicting implementations of trait `RustType`").
173+
174+
The snippet below illustrates how to unify references to a Rust type
175+
across bridges:
176+
177+
```rs
178+
// file1.rs
179+
#[cxx::bridge(namespace = "example")]
180+
pub mod ffi {
181+
extern "Rust" {
182+
type Demo;
183+
184+
fn create_demo() -> Box<Demo>;
185+
}
186+
}
187+
188+
pub struct Demo;
189+
190+
pub fn create_demo() -> Box<Demo> { todo!() }
191+
```
192+
193+
```rs
194+
// file2.rs
195+
#[cxx::bridge(namespace = "example")]
196+
pub mod ffi {
197+
extern "C++" {
198+
include!("file1.rs.h");
199+
}
200+
extern "Rust" {
201+
type Demo = crate::file1::Demo;
202+
203+
fn take_ref_demo(demo: &Demo);
204+
}
205+
}
206+
207+
pub fn take_ref_demo(demo: &crate::file1::Demo) { todo!() }
208+
```

macro/src/expand.rs

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1270,26 +1270,37 @@ fn expand_type_alias(alias: &TypeAlias) -> TokenStream {
12701270
fn expand_type_alias_verify(alias: &TypeAlias, types: &Types) -> TokenStream {
12711271
let attrs = &alias.attrs;
12721272
let ident = &alias.name.rust;
1273-
let type_id = type_id(&alias.name);
12741273
let begin_span = alias.type_token.span;
12751274
let end_span = alias.semi_token.span;
1276-
let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_type::<);
12771275
let end = quote_spanned!(end_span=> >);
12781276

1279-
let mut verify = quote! {
1280-
#attrs
1281-
const _: fn() = #begin #ident, #type_id #end;
1282-
};
1277+
match alias.lang {
1278+
Lang::Cxx | Lang::CxxUnwind => {
1279+
let type_id = type_id(&alias.name);
1280+
let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_type::<);
1281+
let mut verify = quote! {
1282+
#attrs
1283+
const _: fn() = #begin #ident, #type_id #end;
1284+
};
12831285

1284-
if types.required_trivial.contains_key(&alias.name.rust) {
1285-
let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_kind::<);
1286-
verify.extend(quote! {
1287-
#attrs
1288-
const _: fn() = #begin #ident, ::cxx::kind::Trivial #end;
1289-
});
1290-
}
1286+
if types.required_trivial.contains_key(&alias.name.rust) {
1287+
let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_kind::<);
1288+
verify.extend(quote! {
1289+
#attrs
1290+
const _: fn() = #begin #ident, ::cxx::kind::Trivial #end;
1291+
});
1292+
}
12911293

1292-
verify
1294+
verify
1295+
}
1296+
Lang::Rust => {
1297+
let begin = quote_spanned!(begin_span=> ::cxx::private::verify_rust_type::<);
1298+
quote! {
1299+
#attrs
1300+
const _: fn() = #begin #ident #end;
1301+
}
1302+
}
1303+
}
12931304
}
12941305

12951306
fn type_id(name: &Pair) -> TokenStream {

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,7 @@ pub mod private {
503503
pub use crate::rust_str::RustStr;
504504
#[cfg(feature = "alloc")]
505505
pub use crate::rust_string::RustString;
506-
pub use crate::rust_type::{ImplBox, ImplVec, RustType};
506+
pub use crate::rust_type::{verify_rust_type, ImplBox, ImplVec, RustType};
507507
#[cfg(feature = "alloc")]
508508
pub use crate::rust_vec::RustVec;
509509
pub use crate::shared_ptr::SharedPtrTarget;

src/rust_type.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@
33
pub unsafe trait RustType {}
44
pub unsafe trait ImplBox {}
55
pub unsafe trait ImplVec {}
6+
7+
#[doc(hidden)]
8+
pub fn verify_rust_type<T: RustType>() {}

syntax/check.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -710,7 +710,9 @@ fn describe(cx: &mut Check, ty: &Type) -> String {
710710
} else if cx.types.enums.contains_key(&ident.rust) {
711711
"enum".to_owned()
712712
} else if cx.types.cxx_aliases.contains_key(&ident.rust) {
713-
"C++ type".to_owned()
713+
"aliased C++ type".to_owned()
714+
} else if cx.types.rust_aliases.contains_key(&ident.rust) {
715+
"aliased Rust type".to_owned()
714716
} else if cx.types.cxx.contains(&ident.rust) {
715717
"opaque C++ type".to_owned()
716718
} else if cx.types.rust.contains(&ident.rust) {

syntax/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ pub(crate) struct ExternFn {
170170
pub(crate) struct TypeAlias {
171171
#[allow(dead_code)] // only used by cxx-build, not cxxbridge-macro
172172
pub cfg: CfgExpr,
173+
pub lang: Lang,
173174
#[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build
174175
pub doc: Doc,
175176
pub derives: Vec<Derive>,

syntax/parse.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -870,17 +870,12 @@ fn parse_type_alias(
870870
},
871871
));
872872

873-
if lang == Lang::Rust {
874-
let span = quote!(#type_token #semi_token);
875-
let msg = "type alias in extern \"Rust\" block is not supported";
876-
return Err(Error::new_spanned(span, msg));
877-
}
878-
879873
let visibility = visibility_pub(&visibility, type_token.span);
880874
let name = pair(namespace, &ident, cxx_name, rust_name);
881875

882876
Ok(Api::TypeAlias(TypeAlias {
883877
cfg,
878+
lang,
884879
doc,
885880
derives,
886881
attrs,

syntax/types.rs

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ use crate::syntax::set::{OrderedSet, UnorderedSet};
77
use crate::syntax::trivial::{self, TrivialReason};
88
use crate::syntax::visit::{self, Visit};
99
use crate::syntax::{
10-
toposort, Api, Atom, Enum, EnumRepr, ExternType, Impl, Lifetimes, Pair, Struct, Type, TypeAlias,
10+
toposort, Api, Atom, Enum, EnumRepr, ExternType, Impl, Lang, Lifetimes, Pair, Struct, Type,
11+
TypeAlias,
1112
};
1213
use proc_macro2::Ident;
1314
use quote::ToTokens;
@@ -20,6 +21,8 @@ pub(crate) struct Types<'a> {
2021
pub rust: UnorderedSet<&'a Ident>,
2122
/// Type aliases defined in the `extern "C++"` section of `#[cxx::bridge]`.
2223
pub cxx_aliases: UnorderedMap<&'a Ident, &'a TypeAlias>,
24+
/// Type aliases defined in the `extern "Rust"` section of `#[cxx::bridge]`.
25+
pub rust_aliases: UnorderedMap<&'a Ident, &'a TypeAlias>,
2326
pub untrusted: UnorderedMap<&'a Ident, &'a ExternType>,
2427
pub required_trivial: UnorderedMap<&'a Ident, Vec<TrivialReason<'a>>>,
2528
pub impls: OrderedMap<ImplKey<'a>, Option<&'a Impl>>,
@@ -36,6 +39,7 @@ impl<'a> Types<'a> {
3639
let mut cxx = UnorderedSet::new();
3740
let mut rust = UnorderedSet::new();
3841
let mut cxx_aliases = UnorderedMap::new();
42+
let mut rust_aliases = UnorderedMap::new();
3943
let mut untrusted = UnorderedMap::new();
4044
let mut impls = OrderedMap::new();
4145
let mut resolutions = UnorderedMap::new();
@@ -158,8 +162,16 @@ impl<'a> Types<'a> {
158162
if !type_names.insert(ident) {
159163
duplicate_name(cx, alias, ident);
160164
}
161-
cxx.insert(ident);
162-
cxx_aliases.insert(ident, alias);
165+
match alias.lang {
166+
Lang::Cxx | Lang::CxxUnwind => {
167+
cxx.insert(ident);
168+
cxx_aliases.insert(ident, alias);
169+
}
170+
Lang::Rust => {
171+
rust.insert(ident);
172+
rust_aliases.insert(ident, alias);
173+
}
174+
}
163175
add_resolution(&alias.name, &alias.generics);
164176
}
165177
Api::Impl(imp) => {
@@ -182,7 +194,9 @@ impl<'a> Types<'a> {
182194
| ImplKey::SharedPtr(ident)
183195
| ImplKey::WeakPtr(ident)
184196
| ImplKey::CxxVector(ident) => {
185-
Atom::from(ident.rust).is_none() && !cxx_aliases.contains_key(ident.rust)
197+
Atom::from(ident.rust).is_none()
198+
&& !cxx_aliases.contains_key(ident.rust)
199+
&& !rust_aliases.contains_key(ident.rust)
186200
}
187201
};
188202
if implicit_impl && !impls.contains_key(&impl_key) {
@@ -204,6 +218,7 @@ impl<'a> Types<'a> {
204218
cxx,
205219
rust,
206220
cxx_aliases,
221+
rust_aliases,
207222
untrusted,
208223
required_trivial,
209224
impls,
@@ -310,4 +325,19 @@ mod test {
310325
let types = collect_types(&apis).unwrap();
311326
assert!(types.cxx_aliases.contains_key(&format_ident!("Alias")));
312327
}
328+
329+
#[test]
330+
fn test_parsing_of_rust_type_aliases() {
331+
let apis = parse_apis(quote! {
332+
#[cxx::bridge]
333+
mod ffi {
334+
extern "Rust" {
335+
type Alias = crate::another_module::Foo;
336+
}
337+
}
338+
})
339+
.unwrap();
340+
let types = collect_types(&apis).unwrap();
341+
assert!(types.rust_aliases.contains_key(&format_ident!("Alias")));
342+
}
313343
}

tests/ffi/lib.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,13 @@ pub mod ffi {
342342

343343
impl Box<Shared> {}
344344
impl CxxVector<SharedString> {}
345+
346+
extern "Rust" {
347+
type CrossModuleRustType = crate::module::CrossModuleRustType;
348+
fn r_get_value_from_cross_module_rust_type(
349+
value: &CrossModuleRustType,
350+
) -> i32;
351+
}
345352
}
346353

347354
mod other {
@@ -659,3 +666,9 @@ fn r_try_return_mutsliceu8(slice: &mut [u8]) -> Result<&mut [u8], Error> {
659666
fn r_aliased_function(x: i32) -> String {
660667
x.to_string()
661668
}
669+
670+
fn r_get_value_from_cross_module_rust_type(
671+
value: &crate::module::CrossModuleRustType,
672+
) -> i32 {
673+
value.0
674+
}

0 commit comments

Comments
 (0)