Skip to content

Commit

Permalink
Add support for renamed type aliases
Browse files Browse the repository at this point in the history
  • Loading branch information
dpaoliello committed Mar 24, 2023
1 parent 8f822ad commit 3267913
Show file tree
Hide file tree
Showing 12 changed files with 79 additions and 4 deletions.
27 changes: 27 additions & 0 deletions book/src/extern-c++.md
Expand Up @@ -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.
Expand Down
13 changes: 10 additions & 3 deletions macro/src/expand.rs
Expand Up @@ -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) {
Expand Down
3 changes: 3 additions & 0 deletions src/extern_type.rs
Expand Up @@ -183,6 +183,9 @@ mod private {
#[doc(hidden)]
pub fn verify_extern_type<T: ExternType<Id = Id>, Id>() {}

#[doc(hidden)]
pub fn verify_extern_type_renamed<T: ExternType>() {}

#[doc(hidden)]
pub fn verify_extern_kind<T: ExternType<Kind = Kind>, Kind: self::Kind>() {}

Expand Down
4 changes: 3 additions & 1 deletion src/lib.rs
Expand Up @@ -496,7 +496,9 @@ pub type Vector<T> = CxxVector<T>;
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;
Expand Down
9 changes: 9 additions & 0 deletions syntax/attrs.rs
Expand Up @@ -36,6 +36,7 @@ pub struct Parser<'a> {
pub cxx_name: Option<&'a mut Option<ForeignName>>,
pub rust_name: Option<&'a mut Option<Ident>>,
pub variants_from_header: Option<&'a mut Option<Attribute>>,
pub is_renamed: Option<&'a mut bool>,
pub ignore_unrecognized: bool,

// Suppress clippy needless_update lint ("struct update has no effect, all
Expand Down Expand Up @@ -152,6 +153,14 @@ pub fn parse(cx: &mut Errors, attrs: Vec<Attribute>, 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")
Expand Down
1 change: 1 addition & 0 deletions syntax/mod.rs
Expand Up @@ -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 {
Expand Down
3 changes: 3 additions & 0 deletions syntax/parse.rs
Expand Up @@ -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,
Expand All @@ -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()
},
));
Expand All @@ -895,6 +897,7 @@ fn parse_type_alias(
eq_token,
ty,
semi_token,
is_renamed,
}))
}

Expand Down
1 change: 1 addition & 0 deletions tests/ffi/lib.rs
Expand Up @@ -347,6 +347,7 @@ mod other {
pub struct D {
pub d: u64,
}
pub type DRenamed = D;

#[repr(C)]
pub struct E {
Expand Down
6 changes: 6 additions & 0 deletions tests/ffi/module.rs
Expand Up @@ -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;
Expand All @@ -30,6 +32,9 @@ pub mod ffi2 {
#[namespace = "H"]
type H;

#[renamed]
type Int64Alias = i64;

fn c_take_trivial_ptr(d: UniquePtr<D>);
fn c_take_trivial_ref(d: &D);
fn c_take_trivial_mut_ref(d: &mut D);
Expand All @@ -56,6 +61,7 @@ pub mod ffi2 {
fn c_return_ns_opaque_ptr() -> UniquePtr<F>;
fn c_return_ns_unique_ptr() -> UniquePtr<H>;
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);
Expand Down
6 changes: 6 additions & 0 deletions tests/ffi/tests.cc
Expand Up @@ -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<D> c_return_trivial_ptr() {
auto d = std::unique_ptr<D>(new D());
d->d = 30;
Expand Down
4 changes: 4 additions & 0 deletions tests/ffi/tests.h
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
6 changes: 6 additions & 0 deletions tests/test.rs
Expand Up @@ -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));
}

0 comments on commit 3267913

Please sign in to comment.