Skip to content

Commit 9c92af4

Browse files
committed
Merge commit '5ffc29abebf0b308f35c28d67a41e8d82d8f130c' into schreter_rust_type_alias
2 parents 76940e9 + 5ffc29a commit 9c92af4

18 files changed

+178
-21
lines changed

book/src/extern-rust.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,3 +163,48 @@ 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+
## Type equivalence across bridges
168+
169+
Similar to type aliases for C++ types, it is possible to create type aliases for
170+
previously-exported types via the `extern "Rust"` block in another bridge.
171+
However, current support is very limited:
172+
173+
- The type name must be the same as that of the target type.
174+
- If the target is in a different C++ namespace, then the namespace must be
175+
explicitly specified on the alias, otherwise C++ won't consider the types as
176+
equivalent.
177+
178+
Basically, this is enough to import the type from another bridge and nothing more.
179+
180+
In the first module `crate::mod1`, you export the type:
181+
182+
```rust,noplayground
183+
pub struct MyType {
184+
...
185+
}
186+
187+
#[cxx::bridge(namespace = "mod1")]
188+
mod ffi {
189+
extern "Rust" {
190+
type MyType;
191+
}
192+
}
193+
```
194+
195+
And in another crate/module `mod2`, you can now import the type and use it as
196+
a parameter or a return value in C++ and Rust functions:
197+
198+
```rust,noplayground
199+
#[cxx::bridge(namespace = "mod2")]
200+
mod ffi {
201+
extern "Rust" {
202+
#[namespace = "mod1"]
203+
type MyType = crate::mod1::MyType;
204+
}
205+
206+
extern "C++" {
207+
fn c_func(param: &MyType);
208+
}
209+
}
210+
```

gen/src/namespace.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ pub fn namespace(api: &Api) -> &Namespace {
55
match api {
66
Api::CxxFunction(efn) | Api::RustFunction(efn) => &efn.name.namespace,
77
Api::CxxType(ety) | Api::RustType(ety) => &ety.name.namespace,
8+
Api::TypeAlias(ety) => &ety.name.namespace,
89
Api::Enum(enm) => &enm.name.namespace,
910
Api::Struct(strct) => &strct.name.namespace,
10-
Api::Impl(_) | Api::Include(_) | Api::TypeAlias(_) => Default::default(),
11+
Api::Impl(_) | Api::Include(_) => Default::default(),
1112
}
1213
}

gen/src/write.rs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@ use syntax::map::UnorderedMap as Map;
99
use syntax::set::UnorderedSet;
1010
use syntax::symbol::{self, Symbol};
1111
use syntax::trivial::{self, TrivialReason};
12-
use syntax::Lang;
1312
use syntax::{
14-
derive, mangle, Api, Doc, Enum, EnumRepr, ExternFn, ExternType, Pair, Signature, Struct, Trait,
15-
Type, TypeAlias, Types, Var,
13+
derive, mangle, Api, Doc, Enum, EnumRepr, ExternFn, ExternType, Lang, Pair, Signature, Struct,
14+
Trait, Type, TypeAlias, Types, Var,
1615
};
1716

1817
pub fn gen(apis: &[Api], types: &Types, opt: &Opt, header: bool) -> Vec<u8> {
@@ -36,6 +35,7 @@ pub fn gen(apis: &[Api], types: &Types, opt: &Opt, header: bool) -> Vec<u8> {
3635
fn write_forward_declarations(out: &mut OutFile, apis: &[Api]) {
3736
let needs_forward_declaration = |api: &&Api| match api {
3837
Api::Struct(_) | Api::CxxType(_) | Api::RustType(_) => true,
38+
Api::TypeAlias(ety) => ety.lang == Lang::Rust,
3939
Api::Enum(enm) => !out.types.cxx.contains(&enm.name.rust),
4040
_ => false,
4141
};
@@ -55,6 +55,7 @@ fn write_forward_declarations(out: &mut OutFile, apis: &[Api]) {
5555
Api::Enum(enm) => write_enum_decl(out, enm),
5656
Api::CxxType(ety) => write_struct_using(out, &ety.name),
5757
Api::RustType(ety) => write_struct_decl(out, &ety.name),
58+
Api::TypeAlias(ety) => write_struct_decl(out, &ety.name),
5859
_ => unreachable!(),
5960
}
6061
}
@@ -129,8 +130,17 @@ fn write_data_structures<'a>(out: &mut OutFile<'a>, apis: &'a [Api]) {
129130
out.next_section();
130131
for api in apis {
131132
if let Api::TypeAlias(ety) = api {
132-
if let Some(reasons) = out.types.required_trivial.get(&ety.name.rust) {
133-
check_trivial_extern_type(out, ety, reasons);
133+
match ety.lang {
134+
Lang::Cxx => {
135+
if let Some(reasons) = out.types.required_trivial.get(&ety.name.rust) {
136+
check_trivial_extern_type(out, ety, reasons);
137+
}
138+
}
139+
Lang::Rust => {
140+
// nothing to write here, the alias is only used to generate
141+
// forward declaration in C++ (so C++ shims for Rust functions
142+
// using the type compile correctly).
143+
}
134144
}
135145
}
136146
}

macro/src/expand.rs

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,16 @@ fn expand(ffi: Module, doc: Doc, attrs: OtherAttrs, apis: &[Api], types: &Types)
9595
}
9696
hidden.extend(expand_rust_function_shim(efn, types));
9797
}
98-
Api::TypeAlias(alias) => {
99-
expanded.extend(expand_type_alias(alias));
100-
hidden.extend(expand_type_alias_verify(alias, types));
101-
}
98+
Api::TypeAlias(alias) => match alias.lang {
99+
syntax::Lang::Cxx => {
100+
expanded.extend(expand_type_alias(alias));
101+
hidden.extend(expand_type_alias_verify(alias, types));
102+
}
103+
syntax::Lang::Rust => {
104+
expanded.extend(expand_type_alias_rust(alias));
105+
hidden.extend(expand_type_alias_verify_rust(alias));
106+
}
107+
},
102108
}
103109
}
104110

@@ -1336,6 +1342,21 @@ fn expand_type_alias(alias: &TypeAlias) -> TokenStream {
13361342
}
13371343
}
13381344

1345+
fn expand_type_alias_rust(alias: &TypeAlias) -> TokenStream {
1346+
let doc = &alias.doc;
1347+
let attrs = &alias.attrs;
1348+
let visibility = alias.visibility;
1349+
let ident = &alias.name.rust;
1350+
let ty = &alias.ty;
1351+
let semi_token = alias.semi_token;
1352+
1353+
quote! {
1354+
#doc
1355+
#attrs
1356+
#visibility use #ty as #ident #semi_token
1357+
}
1358+
}
1359+
13391360
fn expand_type_alias_verify(alias: &TypeAlias, types: &Types) -> TokenStream {
13401361
let attrs = &alias.attrs;
13411362
let ident = &alias.name.rust;
@@ -1361,6 +1382,15 @@ fn expand_type_alias_verify(alias: &TypeAlias, types: &Types) -> TokenStream {
13611382
verify
13621383
}
13631384

1385+
fn expand_type_alias_verify_rust(alias: &TypeAlias) -> TokenStream {
1386+
let mut ident = alias.name.rust.clone();
1387+
let span = alias.ty.span();
1388+
ident.set_span(span);
1389+
quote_spanned! {span=>
1390+
const _: fn() = ::cxx::private::verify_rust_type::< #ident >;
1391+
}
1392+
}
1393+
13641394
fn type_id(name: &Pair) -> TokenStream {
13651395
let namespace_segments = name.namespace.iter();
13661396
let mut segments = Vec::with_capacity(namespace_segments.len() + 1);

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,7 @@ pub mod private {
501501
pub use crate::rust_str::RustStr;
502502
#[cfg(feature = "alloc")]
503503
pub use crate::rust_string::RustString;
504-
pub use crate::rust_type::{ImplBox, ImplVec, RustType};
504+
pub use crate::rust_type::{verify_rust_type, ImplBox, ImplVec, RustType};
505505
#[cfg(feature = "alloc")]
506506
pub use crate::rust_vec::RustVec;
507507
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: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ use crate::report::Errors;
33
use crate::visit::{self, Visit};
44
use crate::{
55
error, ident, trivial, Api, Array, Enum, ExternFn, ExternType, Future, Impl, Lang, Lifetimes,
6-
NamedType, Ptr, Receiver, Ref, Signature, SliceRef, Struct, Trait, Ty1, Type, TypeAlias, Types,
6+
NamedType, Ptr, Receiver, Ref, RustType, Signature, SliceRef, Struct, Trait, Ty1, Type,
7+
TypeAlias, Types,
78
};
89
use proc_macro2::{Delimiter, Group, Ident, TokenStream};
910
use quote::{quote, ToTokens};
@@ -537,6 +538,25 @@ fn check_api_type_alias(cx: &mut Check, alias: &TypeAlias) {
537538
let msg = format!("derive({}) on extern type alias is not supported", derive);
538539
cx.error(derive, msg);
539540
}
541+
542+
if alias.lang == Lang::Rust {
543+
let ty = &alias.ty;
544+
if let RustType::Path(path) = &ty {
545+
// OK, we support path
546+
if let Some(last) = path.path.segments.last() {
547+
if last.ident != alias.name.rust {
548+
cx.error(
549+
&alias.name.rust,
550+
"`extern \"Rust\"` alias must have the same name as the target type",
551+
);
552+
}
553+
} else {
554+
cx.error(ty, "unsupported `extern \"Rust\"` alias target type");
555+
}
556+
} else {
557+
cx.error(ty, "unsupported `extern \"Rust\"` alias target");
558+
}
559+
}
540560
}
541561

542562
fn check_api_impl(cx: &mut Check, imp: &Impl) {

syntax/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ pub struct TypeAlias {
202202
pub visibility: Token![pub],
203203
pub type_token: Token![type],
204204
pub name: Pair,
205+
pub lang: Lang,
205206
pub generics: Lifetimes,
206207
#[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build
207208
pub eq_token: Token![=],

syntax/parse.rs

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

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

@@ -888,6 +882,7 @@ fn parse_type_alias(
888882
visibility,
889883
type_token,
890884
name,
885+
lang,
891886
generics,
892887
eq_token,
893888
ty,

tests/ffi/module.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ pub mod ffi {
1919

2020
#[cxx::bridge(namespace = "tests")]
2121
pub mod ffi2 {
22+
extern "Rust" {
23+
type R = crate::R;
24+
}
25+
2226
unsafe extern "C++" {
2327
include!("tests/ffi/tests.h");
2428

@@ -71,6 +75,8 @@ pub mod ffi2 {
7175

7276
#[namespace = "I"]
7377
fn ns_c_return_unique_ptr_ns() -> UniquePtr<I>;
78+
79+
fn c_return_box_from_aliased_rust_type() -> Box<R>;
7480
}
7581

7682
impl UniquePtr<D> {}

0 commit comments

Comments
 (0)