Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
212 changes: 94 additions & 118 deletions book/package-lock.json

Large diffs are not rendered by default.

43 changes: 43 additions & 0 deletions book/src/extern-rust.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,46 @@ mod ffi {

Bounds on a lifetime (like `<'a, 'b: 'a>`) are not currently supported. Nor are
type parameters or where-clauses.

## Reusing existing binding types

Extern Rust types support a syntax for declaring that a C++ binding of the
given Rust type already exists outside of the current bridge module. This
avoids generating a separate binding which would clash with the first one
(e.g. one possible error is: "conflicting implementations of trait `RustType`").

The snippet below illustrates how to unify references to a Rust type
across bridges:

```rs
// file1.rs
#[cxx::bridge(namespace = "example")]
pub mod ffi {
extern "Rust" {
type Demo;

fn create_demo() -> Box<Demo>;
}
}

pub struct Demo;

pub fn create_demo() -> Box<Demo> { todo!() }
```

```rs
// file2.rs
#[cxx::bridge(namespace = "example")]
pub mod ffi {
extern "C++" {
include!("file1.rs.h");
}
extern "Rust" {
type Demo = crate::file1::Demo;

fn take_ref_demo(demo: &Demo);
}
}

pub fn take_ref_demo(demo: &crate::file1::Demo) { todo!() }
```
57 changes: 22 additions & 35 deletions gen/src/file.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use crate::syntax::attrs::find_cxx_bridge_attr;
use crate::syntax::file::Module;
use crate::syntax::namespace::Namespace;
use syn::parse::discouraged::Speculative;
use syn::parse::{Error, Parse, ParseStream, Result};
use syn::{braced, Attribute, Ident, Item, Meta, Token, Visibility};
use syn::{braced, Attribute, Ident, Item, Token, Visibility};

pub(crate) struct File {
pub modules: Vec<Module>,
Expand All @@ -20,55 +21,41 @@ fn parse(input: ParseStream, modules: &mut Vec<Module>) -> Result<()> {
input.call(Attribute::parse_inner)?;

while !input.is_empty() {
let mut cxx_bridge = false;
let mut namespace = Namespace::ROOT;
let mut attrs = input.call(Attribute::parse_outer)?;
for attr in &attrs {
let path = &attr.path().segments;
if path.len() == 2 && path[0].ident == "cxx" && path[1].ident == "bridge" {
cxx_bridge = true;
namespace = parse_args(attr)?;
break;
}
}
let cxx_bridge_attr = find_cxx_bridge_attr(&attrs);

let ahead = input.fork();
ahead.parse::<Visibility>()?;
ahead.parse::<Option<Token![unsafe]>>()?;
if !ahead.peek(Token![mod]) {
let item: Item = input.parse()?;
if cxx_bridge {
if cxx_bridge_attr.is_some() {
return Err(Error::new_spanned(item, "expected a module"));
}
continue;
}

if cxx_bridge {
let mut module: Module = input.parse()?;
module.namespace = namespace;
attrs.extend(module.attrs);
module.attrs = attrs;
modules.push(module);
} else {
input.advance_to(&ahead);
input.parse::<Token![mod]>()?;
input.parse::<Ident>()?;
let semi: Option<Token![;]> = input.parse()?;
if semi.is_none() {
let content;
braced!(content in input);
parse(&content, modules)?;
match cxx_bridge_attr {
Some(cxx_bridge_attr) => {
let mut module: Module = input.parse()?;
module.namespace = Namespace::parse_attr(cxx_bridge_attr)?;
attrs.extend(module.attrs);
module.attrs = attrs;
modules.push(module);
}
None => {
input.advance_to(&ahead);
input.parse::<Token![mod]>()?;
input.parse::<Ident>()?;
let semi: Option<Token![;]> = input.parse()?;
if semi.is_none() {
let content;
braced!(content in input);
parse(&content, modules)?;
}
}
}
}

Ok(())
}

fn parse_args(attr: &Attribute) -> Result<Namespace> {
if let Meta::Path(_) = attr.meta {
Ok(Namespace::ROOT)
} else {
attr.parse_args_with(Namespace::parse_bridge_attr_namespace)
}
}
13 changes: 3 additions & 10 deletions gen/src/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,26 +148,19 @@ pub(super) fn generate(syntax: File, opt: &Opt) -> Result<GeneratedCode> {
let ref mut apis = Vec::new();
let ref mut errors = Errors::new();
let ref mut cfg_errors = Set::new();
for bridge in syntax.modules {
for mut bridge in syntax.modules {
let mut cfg = CfgExpr::Unconditional;
attrs::parse(
errors,
bridge.attrs,
std::mem::take(&mut bridge.attrs),
attrs::Parser {
cfg: Some(&mut cfg),
ignore_unrecognized: true,
..Default::default()
},
);
if cfg::eval(errors, cfg_errors, opt.cfg_evaluator.as_ref(), &cfg) {
let ref namespace = bridge.namespace;
let trusted = bridge.unsafety.is_some();
apis.extend(syntax::parse_items(
errors,
bridge.content,
trusted,
namespace,
));
apis.extend(syntax::parse_items(errors, &mut bridge));
}
}

Expand Down
44 changes: 26 additions & 18 deletions macro/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,7 @@ pub(crate) fn bridge(mut ffi: Module) -> Result<TokenStream> {
},
);

let content = mem::take(&mut ffi.content);
let trusted = ffi.unsafety.is_some();
let namespace = &ffi.namespace;
let ref mut apis = syntax::parse_items(errors, content, trusted, namespace);
let ref mut apis = syntax::parse_items(errors, &mut ffi);
#[cfg(feature = "experimental-enum-variants-from-header")]
crate::load::load(errors, apis);
let ref types = Types::collect(errors, apis);
Expand Down Expand Up @@ -1273,26 +1270,37 @@ fn expand_type_alias(alias: &TypeAlias) -> TokenStream {
fn expand_type_alias_verify(alias: &TypeAlias, types: &Types) -> TokenStream {
let attrs = &alias.attrs;
let ident = &alias.name.rust;
let type_id = type_id(&alias.name);
let begin_span = alias.type_token.span;
let end_span = alias.semi_token.span;
let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_type::<);
let end = quote_spanned!(end_span=> >);

let mut verify = quote! {
#attrs
const _: fn() = #begin #ident, #type_id #end;
};
match alias.lang {
Lang::Cxx | Lang::CxxUnwind => {
let type_id = type_id(&alias.name);
let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_type::<);
let mut verify = quote! {
#attrs
const _: fn() = #begin #ident, #type_id #end;
};

if types.required_trivial.contains_key(&alias.name.rust) {
let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_kind::<);
verify.extend(quote! {
#attrs
const _: fn() = #begin #ident, ::cxx::kind::Trivial #end;
});
}
if types.required_trivial.contains_key(&alias.name.rust) {
let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_kind::<);
verify.extend(quote! {
#attrs
const _: fn() = #begin #ident, ::cxx::kind::Trivial #end;
});
}

verify
verify
}
Lang::Rust => {
let begin = quote_spanned!(begin_span=> ::cxx::private::verify_rust_type::<);
quote! {
#attrs
const _: fn() = #begin #ident #end;
}
}
}
}

fn type_id(name: &Pair) -> TokenStream {
Expand Down
2 changes: 1 addition & 1 deletion macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ use syn::parse_macro_input;
pub fn bridge(args: TokenStream, input: TokenStream) -> TokenStream {
let _ = syntax::error::ERRORS;

let namespace = match Namespace::parse_bridge_attr_namespace.parse(args) {
let namespace = match Namespace::parse_stream.parse(args) {
Ok(namespace) => namespace,
Err(err) => return err.to_compile_error().into(),
};
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,7 @@ pub mod private {
pub use crate::rust_str::RustStr;
#[cfg(feature = "alloc")]
pub use crate::rust_string::RustString;
pub use crate::rust_type::{ImplBox, ImplVec, RustType};
pub use crate::rust_type::{verify_rust_type, ImplBox, ImplVec, RustType};
#[cfg(feature = "alloc")]
pub use crate::rust_vec::RustVec;
pub use crate::shared_ptr::SharedPtrTarget;
Expand Down
3 changes: 3 additions & 0 deletions src/rust_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@
pub unsafe trait RustType {}
pub unsafe trait ImplBox {}
pub unsafe trait ImplVec {}

#[doc(hidden)]
pub fn verify_rust_type<T: RustType>() {}
12 changes: 12 additions & 0 deletions syntax/attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,3 +310,15 @@ impl ToTokens for OtherAttrs {
}
}
}

/// This finds `#[cxx::bridge]` attribute if it has been spelled in exactly this way. This means
/// that it is appropriate to use from `gen/...` and tests, but not from `macro/...` (it seems
/// somewhat prudent for the latter to handle the case where the attribute was imported under a
/// different name).
#[allow(dead_code)] // Only used from tests and cxx-build, but not from cxxbridge-macro
pub(crate) fn find_cxx_bridge_attr(attrs: &[Attribute]) -> Option<&Attribute> {
attrs.iter().find(|attr| {
let path = &attr.path().segments;
path.len() == 2 && path[0].ident == "cxx" && path[1].ident == "bridge"
})
}
15 changes: 9 additions & 6 deletions syntax/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ fn check_type_ident(cx: &mut Check, name: &NamedType) {
fn check_type_box(cx: &mut Check, ptr: &Ty1) {
if let Type::Ident(ident) = &ptr.inner {
if cx.types.cxx.contains(&ident.rust)
&& !cx.types.aliases.contains_key(&ident.rust)
&& !cx.types.cxx_aliases.contains_key(&ident.rust)
&& !cx.types.structs.contains_key(&ident.rust)
&& !cx.types.enums.contains_key(&ident.rust)
{
Expand All @@ -114,7 +114,7 @@ fn check_type_rust_vec(cx: &mut Check, ty: &Ty1) {
match &ty.inner {
Type::Ident(ident) => {
if cx.types.cxx.contains(&ident.rust)
&& !cx.types.aliases.contains_key(&ident.rust)
&& !cx.types.cxx_aliases.contains_key(&ident.rust)
&& !cx.types.structs.contains_key(&ident.rust)
&& !cx.types.enums.contains_key(&ident.rust)
{
Expand Down Expand Up @@ -273,7 +273,8 @@ fn check_type_slice_ref(cx: &mut Check, ty: &SliceRef) {
let supported = !is_unsized(cx, &ty.inner)
|| match &ty.inner {
Type::Ident(ident) => {
cx.types.rust.contains(&ident.rust) || cx.types.aliases.contains_key(&ident.rust)
cx.types.rust.contains(&ident.rust)
|| cx.types.cxx_aliases.contains_key(&ident.rust)
}
_ => false,
};
Expand Down Expand Up @@ -664,7 +665,7 @@ fn is_opaque_cxx(cx: &mut Check, ty: &Ident) -> bool {
cx.types.cxx.contains(ty)
&& !cx.types.structs.contains_key(ty)
&& !cx.types.enums.contains_key(ty)
&& !(cx.types.aliases.contains_key(ty) && cx.types.required_trivial.contains_key(ty))
&& !(cx.types.cxx_aliases.contains_key(ty) && cx.types.required_trivial.contains_key(ty))
}

fn span_for_struct_error(strct: &Struct) -> TokenStream {
Expand Down Expand Up @@ -708,8 +709,10 @@ fn describe(cx: &mut Check, ty: &Type) -> String {
"struct".to_owned()
} else if cx.types.enums.contains_key(&ident.rust) {
"enum".to_owned()
} else if cx.types.aliases.contains_key(&ident.rust) {
"C++ type".to_owned()
} else if cx.types.cxx_aliases.contains_key(&ident.rust) {
"aliased C++ type".to_owned()
} else if cx.types.rust_aliases.contains_key(&ident.rust) {
"aliased Rust type".to_owned()
} else if cx.types.cxx.contains(&ident.rust) {
"opaque C++ type".to_owned()
} else if cx.types.rust.contains(&ident.rust) {
Expand Down
2 changes: 1 addition & 1 deletion syntax/improper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ impl<'a> Types<'a> {
} else if let Some(strct) = self.structs.get(ident) {
Depends(&strct.name.rust) // iterate to fixed-point
} else {
Definite(self.rust.contains(ident) || self.aliases.contains_key(ident))
Definite(self.rust.contains(ident) || self.cxx_aliases.contains_key(ident))
}
}
Type::RustBox(_)
Expand Down
3 changes: 3 additions & 0 deletions syntax/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ pub(crate) mod report;
pub(crate) mod resolve;
pub(crate) mod set;
pub(crate) mod symbol;
#[cfg(test)]
pub(crate) mod test_support;
mod tokens;
mod toposort;
pub(crate) mod trivial;
Expand Down Expand Up @@ -168,6 +170,7 @@ pub(crate) struct ExternFn {
pub(crate) struct TypeAlias {
#[allow(dead_code)] // only used by cxx-build, not cxxbridge-macro
pub cfg: CfgExpr,
pub lang: Lang,
#[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build
pub doc: Doc,
pub derives: Vec<Derive>,
Expand Down
Loading
Loading