From 3267913184b15d03f43c97b69b69d6d6cd6bf80d Mon Sep 17 00:00:00 2001 From: Daniel Paoliello Date: Fri, 24 Mar 2023 14:24:49 -0700 Subject: [PATCH] Add support for renamed type aliases --- book/src/extern-c++.md | 27 +++++++++++++++++++++++++++ macro/src/expand.rs | 13 ++++++++++--- src/extern_type.rs | 3 +++ src/lib.rs | 4 +++- syntax/attrs.rs | 9 +++++++++ syntax/mod.rs | 1 + syntax/parse.rs | 3 +++ tests/ffi/lib.rs | 1 + tests/ffi/module.rs | 6 ++++++ tests/ffi/tests.cc | 6 ++++++ tests/ffi/tests.h | 4 ++++ tests/test.rs | 6 ++++++ 12 files changed, 79 insertions(+), 4 deletions(-) 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 ea5af66a4..ff87fe5db 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 4c2a8527b..b87cbdf09 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 46d010e0a..fe6e6d395 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 @@ -152,6 +153,14 @@ 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") { + if let Err(err) = Nothing::parse.parse2(attr.tokens.clone()) { + cx.push(err); + } + if let Some(is_renamed) = &mut parser.is_renamed { + **is_renamed = true; + continue; + } } else if attr.path.is_ident("allow") || attr.path.is_ident("warn") || attr.path.is_ident("deny") 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 1754c6006..53d6fb177 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -860,6 +860,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, @@ -870,6 +871,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() }, )); @@ -895,6 +897,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)); +}