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 Aug 21, 2023
1 parent eb06f52 commit 3c1ce2a
Show file tree
Hide file tree
Showing 12 changed files with 92 additions and 4 deletions.
27 changes: 27 additions & 0 deletions book/src/extern-c++.md
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
Expand Up @@ -1223,11 +1223,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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,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
22 changes: 22 additions & 0 deletions syntax/attrs.rs
Original file line number Diff line number Diff line change
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 @@ -153,6 +154,19 @@ 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") {
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")
Expand Down Expand Up @@ -282,6 +296,14 @@ fn parse_rust_name_attribute(meta: &Meta) -> Result<Ident> {
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<Attribute>);

Expand Down
1 change: 1 addition & 0 deletions syntax/mod.rs
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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()
},
));
Expand All @@ -894,6 +896,7 @@ fn parse_type_alias(
eq_token,
ty,
semi_token,
is_renamed,
}))
}

Expand Down
1 change: 1 addition & 0 deletions tests/ffi/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
Expand Up @@ -379,3 +379,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 3c1ce2a

Please sign in to comment.