Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for renamed type aliases #1203

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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));
}