diff --git a/book/src/extern-c++.md b/book/src/extern-c++.md index 11ed7b54e..86b7b2958 100644 --- a/book/src/extern-c++.md +++ b/book/src/extern-c++.md @@ -286,6 +286,33 @@ value, and include it in `struct`s that you have declared to `cxx::bridge`. Your claim about the triviality of the C++ type will be checked by a `static_assert` in the generated C++ side of the binding. +### Handling renamed or aliased types + +By default, the name of an Extern C++ type must match the `Id` of the Rust +type's `ExternType` impl. For example, a `i32` in Rust may only be used as a +`std::int32_t` in C++. + +In the case where a single Rust type maps to multiple C++ types, or a different +type than in its `ExternType` impl, then you can use the `#[renamed]` attribute +in the `extern "C++"` block to indicate that the type is intentionally renamed. + +For example, if we wanted to call an extern C++ function called `may_fail` that +returns a Windows-style `HRESULT` and we wanted to use the name `HRESULT` in the +Rust definition to note that the return value is a result and not a general +number, then we can add `#[renamed]` to the type alias: + +```rust,noplayground +#[cxx::bridge] +mod ffi { + extern "C++" { + #[renamed] + type HRESULT = i32; + + fn may_fail() -> HRESULT; + } +} +``` + ## Explicit shim trait impls This is a somewhat niche feature, but important when you need it. diff --git a/macro/src/expand.rs b/macro/src/expand.rs index bd0a20637..3feb3a86d 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -1222,11 +1222,18 @@ fn expand_type_alias_verify(alias: &TypeAlias, types: &Types) -> TokenStream { 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! { - const _: fn() = #begin #ident, #type_id #end; + let mut verify = if alias.is_renamed { + let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_type_renamed::<); + quote! { + const _: fn() = #begin #ident #end; + } + } else { + let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_type::<); + quote! { + const _: fn() = #begin #ident, #type_id #end; + } }; if types.required_trivial.contains_key(&alias.name.rust) { diff --git a/src/extern_type.rs b/src/extern_type.rs index d131ae127..92af9ea96 100644 --- a/src/extern_type.rs +++ b/src/extern_type.rs @@ -183,6 +183,9 @@ mod private { #[doc(hidden)] pub fn verify_extern_type, Id>() {} +#[doc(hidden)] +pub fn verify_extern_type_renamed() {} + #[doc(hidden)] pub fn verify_extern_kind, Kind: self::Kind>() {} diff --git a/src/lib.rs b/src/lib.rs index 2c448ae95..91c3201e7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -496,7 +496,9 @@ pub type Vector = CxxVector; pub mod private { pub use crate::c_char::c_char; pub use crate::cxx_vector::VectorElement; - pub use crate::extern_type::{verify_extern_kind, verify_extern_type}; + pub use crate::extern_type::{ + verify_extern_kind, verify_extern_type, verify_extern_type_renamed, + }; pub use crate::function::FatFunction; pub use crate::hash::hash; pub use crate::opaque::Opaque; diff --git a/syntax/attrs.rs b/syntax/attrs.rs index 4ff700a84..4eb0089d0 100644 --- a/syntax/attrs.rs +++ b/syntax/attrs.rs @@ -36,6 +36,7 @@ pub struct Parser<'a> { pub cxx_name: Option<&'a mut Option>, pub rust_name: Option<&'a mut Option>, pub variants_from_header: Option<&'a mut Option>, + pub is_renamed: Option<&'a mut bool>, pub ignore_unrecognized: bool, // Suppress clippy needless_update lint ("struct update has no effect, all @@ -153,6 +154,19 @@ pub fn parse(cx: &mut Errors, attrs: Vec, mut parser: Parser) -> Othe **variants_from_header = Some(attr); continue; } + } else if attr_path.is_ident("renamed") { + match parse_renamed_attribute(&attr.meta) { + Ok(_) => { + if let Some(is_renamed) = &mut parser.is_renamed { + **is_renamed = true; + continue; + } + } + Err(err) => { + cx.push(err); + break; + } + } } else if attr_path.is_ident("allow") || attr_path.is_ident("warn") || attr_path.is_ident("deny") @@ -282,6 +296,14 @@ fn parse_rust_name_attribute(meta: &Meta) -> Result { Err(Error::new_spanned(meta, "unsupported rust_name attribute")) } +fn parse_renamed_attribute(meta: &Meta) -> Result<()> { + if let Meta::Path(_) = meta { + Ok(()) + } else { + Err(Error::new_spanned(meta, "unsupported renamed attribute")) + } +} + #[derive(Clone)] pub struct OtherAttrs(Vec); diff --git a/syntax/mod.rs b/syntax/mod.rs index 4f19d9641..c3eea8624 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -158,6 +158,7 @@ pub struct TypeAlias { pub eq_token: Token![=], pub ty: RustType, pub semi_token: Token![;], + pub is_renamed: bool, } pub struct Impl { diff --git a/syntax/parse.rs b/syntax/parse.rs index c6fee5f86..e9b3ed84c 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -859,6 +859,7 @@ fn parse_type_alias( let mut cxx_name = None; let mut rust_name = None; let mut attrs = attrs.clone(); + let mut is_renamed = false; attrs.extend(attrs::parse( cx, unparsed_attrs, @@ -869,6 +870,7 @@ fn parse_type_alias( namespace: Some(&mut namespace), cxx_name: Some(&mut cxx_name), rust_name: Some(&mut rust_name), + is_renamed: Some(&mut is_renamed), ..Default::default() }, )); @@ -894,6 +896,7 @@ fn parse_type_alias( eq_token, ty, semi_token, + is_renamed, })) } diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index d6a5f0286..662bd1d28 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -347,6 +347,7 @@ mod other { pub struct D { pub d: u64, } + pub type DRenamed = D; #[repr(C)] pub struct E { diff --git a/tests/ffi/module.rs b/tests/ffi/module.rs index 21a86206d..42c9a511e 100644 --- a/tests/ffi/module.rs +++ b/tests/ffi/module.rs @@ -21,6 +21,8 @@ pub mod ffi2 { include!("tests/ffi/tests.h"); type D = crate::other::D; + #[renamed] + type DRenamed = crate::other::DRenamed; type E = crate::other::E; #[namespace = "F"] type F = crate::other::f::F; @@ -30,6 +32,9 @@ pub mod ffi2 { #[namespace = "H"] type H; + #[renamed] + type Int64Alias = i64; + fn c_take_trivial_ptr(d: UniquePtr); fn c_take_trivial_ref(d: &D); fn c_take_trivial_mut_ref(d: &mut D); @@ -56,6 +61,7 @@ pub mod ffi2 { fn c_return_ns_opaque_ptr() -> UniquePtr; fn c_return_ns_unique_ptr() -> UniquePtr; fn c_take_ref_ns_c(h: &H); + fn c_take_renamed(d: DRenamed, val: Int64Alias); #[namespace = "other"] fn ns_c_take_trivial(d: D); diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 8cf74bebb..a54ddaca7 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -717,6 +717,12 @@ void c_take_opaque_ns_ref(const ::F::F &f) { } } +void c_take_renamed(DRenamed d, Int64Alias val) { + if (d.d == val) { + cxx_test_suite_set_correct(); + } +} + std::unique_ptr c_return_trivial_ptr() { auto d = std::unique_ptr(new D()); d->d = 30; diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index dc02e4ff8..94e0ff3cb 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -64,6 +64,8 @@ struct D { void c_take_trivial_ref_method() const; void c_take_trivial_mut_ref_method(); }; +using DRenamed = D; +using Int64Alias = uint64_t; struct E { uint64_t e; @@ -197,6 +199,8 @@ void c_take_trivial_pin_ref(const D &d); void c_take_trivial_pin_mut_ref(D &d); void c_take_trivial(D d); +void c_take_renamed(D d, Int64Alias val); + void c_take_trivial_ns_ptr(std::unique_ptr<::G::G> g); void c_take_trivial_ns_ref(const ::G::G &g); void c_take_trivial_ns(::G::G g); diff --git a/tests/test.rs b/tests/test.rs index bcf0a2cd1..3556d614e 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -378,3 +378,9 @@ fn test_raw_ptr() { assert_eq!(2025, unsafe { ffi::c_take_const_ptr(c3) }); assert_eq!(2025, unsafe { ffi::c_take_mut_ptr(c3 as *mut ffi::C) }); // deletes c3 } + +#[test] +fn test_renamed() { + let d = ffi2::DRenamed { d: 42 }; + check!(ffi2::c_take_renamed(d, 42)); +}