From 890083d64fc0ee2ccf298be549827e9c0a975628 Mon Sep 17 00:00:00 2001 From: Bryan Henry Date: Sun, 13 Sep 2020 10:34:31 -0700 Subject: [PATCH 01/13] Preserve docs on aliases --- macro/src/expand.rs | 2 ++ syntax/mod.rs | 1 + syntax/parse.rs | 3 ++- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 955ad5f50..c35044b39 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -657,9 +657,11 @@ fn expand_rust_function_shim_impl( } fn expand_type_alias(alias: &TypeAlias) -> TokenStream { + let doc = &alias.doc; let ident = &alias.ident; let ty = &alias.ty; quote! { + #doc pub type #ident = #ty; } } diff --git a/syntax/mod.rs b/syntax/mod.rs index f52a582c1..8ef3c8b9c 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -79,6 +79,7 @@ pub struct ExternFn { } pub struct TypeAlias { + pub doc: Doc, pub type_token: Token![type], pub ident: Ident, pub eq_token: Token![=], diff --git a/syntax/parse.rs b/syntax/parse.rs index 3ac530471..c1c565fd4 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -396,9 +396,10 @@ fn parse_extern_verbatim(cx: &mut Errors, tokens: &TokenStream, lang: Lang) -> R let eq_token: Token![=] = input.parse()?; let ty: RustType = input.parse()?; let semi_token: Token![;] = input.parse()?; - attrs::parse_doc(cx, &attrs); + let doc = attrs::parse_doc(cx, &attrs); Ok(TypeAlias { + doc, type_token, ident, eq_token, From e68faf7d036b1b1ed329ac4c7c0e88ec1930f7f6 Mon Sep 17 00:00:00 2001 From: Bryan Henry Date: Sun, 13 Sep 2020 12:43:18 -0700 Subject: [PATCH 02/13] Rename tests/ffi/module.rs to alias.rs to clarify purpose --- tests/BUCK | 8 ++++---- tests/BUILD | 8 ++++---- tests/ffi/{module.rs => alias.rs} | 0 tests/ffi/build.rs | 2 +- tests/ffi/lib.rs | 2 +- tests/test.rs | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) rename tests/ffi/{module.rs => alias.rs} (100%) diff --git a/tests/BUCK b/tests/BUCK index 47fc557cd..93d68bc22 100644 --- a/tests/BUCK +++ b/tests/BUCK @@ -10,7 +10,7 @@ rust_library( name = "ffi", srcs = [ "ffi/lib.rs", - "ffi/module.rs", + "ffi/alias.rs", ], crate = "cxx_test_suite", deps = [ @@ -23,8 +23,8 @@ cxx_library( name = "impl", srcs = [ "ffi/tests.cc", + ":alias/source", ":bridge/source", - ":module/source", ], header_namespace = "cxx-test-suite", headers = { @@ -40,6 +40,6 @@ rust_cxx_bridge( ) rust_cxx_bridge( - name = "module", - src = "ffi/module.rs", + name = "alias", + src = "ffi/alias.rs", ) diff --git a/tests/BUILD b/tests/BUILD index 68e7be3cb..d9775bded 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -12,8 +12,8 @@ rust_test( rust_library( name = "cxx_test_suite", srcs = [ + "ffi/alias.rs", "ffi/lib.rs", - "ffi/module.rs", ], deps = [ ":impl", @@ -25,8 +25,8 @@ cc_library( name = "impl", srcs = [ "ffi/tests.cc", + ":alias/source", ":bridge/source", - ":module/source", ], hdrs = ["ffi/tests.h"], include_prefix = "cxx-test-suite", @@ -46,8 +46,8 @@ rust_cxx_bridge( ) rust_cxx_bridge( - name = "module", - src = "ffi/module.rs", + name = "alias", + src = "ffi/alias.rs", include_prefix = "cxx-test-suite", strip_include_prefix = "ffi", deps = [":impl"], diff --git a/tests/ffi/module.rs b/tests/ffi/alias.rs similarity index 100% rename from tests/ffi/module.rs rename to tests/ffi/alias.rs diff --git a/tests/ffi/build.rs b/tests/ffi/build.rs index 8042129fa..10078531f 100644 --- a/tests/ffi/build.rs +++ b/tests/ffi/build.rs @@ -3,7 +3,7 @@ fn main() { return; } - let sources = vec!["lib.rs", "module.rs"]; + let sources = vec!["alias.rs", "lib.rs"]; cxx_build::bridges(sources) .file("tests.cc") .flag_if_supported(cxxbridge_flags::STD) diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 9ace1f25f..165df1063 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -4,7 +4,7 @@ clippy::trivially_copy_pass_by_ref )] -pub mod module; +pub mod alias; use cxx::{CxxString, CxxVector, UniquePtr}; use std::fmt::{self, Display}; diff --git a/tests/test.rs b/tests/test.rs index 9c71bd5aa..f866c1e22 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,4 +1,4 @@ -use cxx_test_suite::ffi; +use cxx_test_suite::{alias, ffi}; use std::cell::Cell; use std::ffi::CStr; @@ -89,7 +89,7 @@ fn test_c_take() { check!(ffi::c_take_shared(ffi::Shared { z: 2020 })); check!(ffi::c_take_box(Box::new(2020))); check!(ffi::c_take_ref_c(&unique_ptr)); - check!(cxx_test_suite::module::ffi::c_take_unique_ptr(unique_ptr)); + check!(alias::ffi::c_take_unique_ptr(unique_ptr)); check!(ffi::c_take_str("2020")); check!(ffi::c_take_sliceu8(b"2020")); check!(ffi::c_take_rust_string("2020".to_owned())); From 7608efa687efc841e74113e2d66786b0547ee85d Mon Sep 17 00:00:00 2001 From: Bryan Henry Date: Sat, 19 Sep 2020 16:48:38 -0700 Subject: [PATCH 03/13] Add Kind associated type to ExternType trait --- macro/src/expand.rs | 3 +- src/extern_type.rs | 63 +++++++++++++++++++++-------------- src/lib.rs | 2 +- tests/ui/wrong_type_id.stderr | 4 +-- 4 files changed, 43 insertions(+), 29 deletions(-) diff --git a/macro/src/expand.rs b/macro/src/expand.rs index c35044b39..3d3ef48c2 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -178,6 +178,7 @@ fn expand_cxx_type(namespace: &Namespace, ety: &ExternType) -> TokenStream { } unsafe impl ::cxx::ExternType for #ident { + type Kind = ::cxx::extern_type::KindOpaqueCpp; type Id = #type_id; } } @@ -675,7 +676,7 @@ fn expand_type_alias_verify(namespace: &Namespace, alias: &TypeAlias) -> TokenSt let end = quote_spanned!(end_span=> >); quote! { - const _: fn() = #begin #ident, #type_id #end; + const _: fn() = #begin #ident, ::cxx::extern_type::KindOpaqueCpp, #type_id #end; } } diff --git a/src/extern_type.rs b/src/extern_type.rs index 6701ef591..fe3e9a28a 100644 --- a/src/extern_type.rs +++ b/src/extern_type.rs @@ -1,26 +1,23 @@ -/// A type for which the layout is determined by its C++ definition. +/// A type for which the layout is determined by an external definition. /// -/// This trait serves the following two related purposes. +/// `ExternType` makes it possible for CXX to safely share a consistent Rust type across multiple +/// #\[cxx::bridge\] invocations. This serves multiple related purposes. /// ///
/// -/// ## Safely unifying occurrences of the same extern type -/// -/// `ExternType` makes it possible for CXX to safely share a consistent Rust -/// type across multiple #\[cxx::bridge\] invocations that refer to a common -/// extern C++ type. -/// -/// In the following snippet, two #\[cxx::bridge\] invocations in different -/// files (possibly different crates) both contain function signatures involving -/// the same C++ type `example::Demo`. If both were written just containing -/// `type Demo;`, then both macro expansions would produce their own separate -/// Rust type called `Demo` and thus the compiler wouldn't allow us to take the -/// `Demo` returned by `file1::ffi::create_demo` and pass it as the `Demo` -/// argument accepted by `file2::ffi::take_ref_demo`. Instead, one of the two -/// `Demo`s has been defined as an extern type alias of the other, making them -/// the same type in Rust. The CXX code generator will use an automatically -/// generated `ExternType` impl emitted in file1 to statically verify that in -/// file2 `crate::file1::ffi::Demo` really does refer to the C++ type +/// ## Safely unifying occurrences of the same extern C++ type +/// +/// In the following snippet, two #\[cxx::bridge\] invocations in different files (possibly +/// different crates) both contain function signatures involving the same C++ type `example::Demo`. +/// +/// If both were written just containing `type Demo;`, then both macro expansions would produce +/// their own separate Rust type called `Demo` and thus the compiler wouldn't allow us to take the +/// `Demo` returned by `file1::ffi::create_demo` and pass it as the `Demo` argument accepted by +/// `file2::ffi::take_ref_demo`. Instead, one of the two `Demo`s has been defined as an extern type +/// alias of the other, making them the same type in Rust. +/// +/// The CXX code generator will use an automatically generated `ExternType` impl emitted in file1 to +/// statically verify that in file2 `crate::file1::ffi::Demo` really does refer to the C++ type /// `example::Demo` as expected in file2. /// /// ```no_run @@ -53,12 +50,12 @@ /// /// ## Integrating with bindgen-generated types /// -/// Handwritten `ExternType` impls make it possible to plug in a data structure -/// emitted by bindgen as the definition of an opaque C++ type emitted by CXX. +/// Handwritten `ExternType` impls make it possible to plug in a data structure emitted by bindgen +/// as the definition of an opaque C++ type emitted by CXX. /// -/// By writing the unsafe `ExternType` impl, the programmer asserts that the C++ -/// namespace and type name given in the type id refers to a C++ type that is -/// equivalent to Rust type that is the `Self` type of the impl. +/// By writing the unsafe `ExternType` impl, the programmer asserts that the C++ namespace and type +/// name given in the type id refers to a C++ type that is equivalent to Rust type that is the +/// `Self` type of the impl. /// /// ```no_run /// # const _: &str = stringify! { @@ -70,8 +67,10 @@ /// # } /// /// use cxx::{type_id, ExternType}; +/// use cxx::extern_type; /// /// unsafe impl ExternType for folly_sys::StringPiece { +/// type Kind = extern_type::KindOpaqueCpp; /// type Id = type_id!("folly::StringPiece"); /// } /// @@ -93,6 +92,16 @@ /// # fn main() {} /// ``` pub unsafe trait ExternType { + /// The type's kind. + /// + /// Must be either: + /// * `KindShared` for a shared type declared outside of an extern block in a cxx::bridge, or + /// * `KindOpqaueCpp` for an opaque C++ type declared inside of an `extern "C"` block. + /// + /// Opaque Rust type aliases are unsupported because they can included with a use declaration + /// and aliased more simply outside of the cxx::bridge. + type Kind; + /// A type-level representation of the type's C++ namespace and type name. /// /// This will always be defined using `type_id!` in the following form: @@ -100,11 +109,15 @@ pub unsafe trait ExternType { /// ``` /// # struct TypeName; /// # unsafe impl cxx::ExternType for TypeName { + /// # type Kind = cxx::extern_type::KindOpaqueCpp; /// type Id = cxx::type_id!("name::space::of::TypeName"); /// # } /// ``` type Id; } +pub struct KindOpaqueCpp; +pub struct KindShared; + #[doc(hidden)] -pub fn verify_extern_type, Id>() {} +pub fn verify_extern_type, Kind, Id>() {} diff --git a/src/lib.rs b/src/lib.rs index 5f52f49d4..02f0f451b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -380,7 +380,7 @@ mod macros; mod cxx_string; mod cxx_vector; mod exception; -mod extern_type; +pub mod extern_type; mod function; mod opaque; mod result; diff --git a/tests/ui/wrong_type_id.stderr b/tests/ui/wrong_type_id.stderr index 2d8e50aac..ca4370abf 100644 --- a/tests/ui/wrong_type_id.stderr +++ b/tests/ui/wrong_type_id.stderr @@ -6,8 +6,8 @@ error[E0271]: type mismatch resolving `::Id == (f, o, | ::: $WORKSPACE/src/extern_type.rs | - | pub fn verify_extern_type, Id>() {} - | ------- required by this bound in `verify_extern_type` + | pub fn verify_extern_type, Kind, Id>() {} + | ------- required by this bound in `verify_extern_type` | = note: expected tuple `(f, o, l, l, y, (), B, y, t, e, R, a, n, g, e)` found tuple `(f, o, l, l, y, (), S, t, r, i, n, g, P, i, e, c, e)` From bbb0838d6f554023b39d48f4b96def46dc83f9b4 Mon Sep 17 00:00:00 2001 From: Bryan Henry Date: Sat, 19 Sep 2020 17:04:19 -0700 Subject: [PATCH 04/13] Add alias kind to syntax::TypeAlias --- macro/src/expand.rs | 9 +++++++-- syntax/mod.rs | 6 ++++++ syntax/parse.rs | 5 +++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 3d3ef48c2..ef36f1088 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -4,7 +4,8 @@ use crate::syntax::namespace::Namespace; use crate::syntax::report::Errors; use crate::syntax::symbol::Symbol; use crate::syntax::{ - self, check, mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, TypeAlias, Types, + self, check, mangle, AliasKind, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, + TypeAlias, Types, }; use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote, quote_spanned, ToTokens}; @@ -672,11 +673,15 @@ fn expand_type_alias_verify(namespace: &Namespace, alias: &TypeAlias) -> TokenSt let type_id = type_id(namespace, ident); let begin_span = alias.type_token.span; let end_span = alias.semi_token.span; + let kind = match alias.kind { + AliasKind::Shared => quote!(KindShared), + AliasKind::OpaqueCpp => quote!(KindOpaqueCpp), + }; let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_type::<); let end = quote_spanned!(end_span=> >); quote! { - const _: fn() = #begin #ident, ::cxx::extern_type::KindOpaqueCpp, #type_id #end; + const _: fn() = #begin #ident, ::cxx::extern_type:: #kind, #type_id #end; } } diff --git a/syntax/mod.rs b/syntax/mod.rs index 8ef3c8b9c..780efda65 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -78,7 +78,13 @@ pub struct ExternFn { pub semi_token: Token![;], } +pub enum AliasKind { + Shared, + OpaqueCpp, +} + pub struct TypeAlias { + pub kind: AliasKind, pub doc: Doc, pub type_token: Token![type], pub ident: Ident, diff --git a/syntax/parse.rs b/syntax/parse.rs index c1c565fd4..49372509f 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -3,8 +3,8 @@ use crate::syntax::file::{Item, ItemForeignMod}; use crate::syntax::report::Errors; use crate::syntax::Atom::*; use crate::syntax::{ - attrs, error, Api, Doc, Enum, ExternFn, ExternType, Lang, Receiver, Ref, Signature, Slice, - Struct, Ty1, Type, TypeAlias, Var, Variant, + attrs, error, AliasKind, Api, Doc, Enum, ExternFn, ExternType, Lang, Receiver, Ref, Signature, + Slice, Struct, Ty1, Type, TypeAlias, Var, Variant, }; use proc_macro2::{TokenStream, TokenTree}; use quote::{format_ident, quote, quote_spanned}; @@ -399,6 +399,7 @@ fn parse_extern_verbatim(cx: &mut Errors, tokens: &TokenStream, lang: Lang) -> R let doc = attrs::parse_doc(cx, &attrs); Ok(TypeAlias { + kind: AliasKind::OpaqueCpp, doc, type_token, ident, From dc2f6ed31d79759897ac2c759b75f2af722ba573 Mon Sep 17 00:00:00 2001 From: Bryan Henry Date: Sat, 19 Sep 2020 18:18:51 -0700 Subject: [PATCH 05/13] Add parsing for shared type aliases --- syntax/file.rs | 4 ++- syntax/parse.rs | 79 +++++++++++++++++++++++++++++++------------------ 2 files changed, 53 insertions(+), 30 deletions(-) diff --git a/syntax/file.rs b/syntax/file.rs index 8b86adc73..1adebd8a6 100644 --- a/syntax/file.rs +++ b/syntax/file.rs @@ -3,7 +3,7 @@ use quote::quote; use syn::parse::{Error, Parse, ParseStream, Result}; use syn::{ braced, token, Abi, Attribute, ForeignItem, Ident, Item as RustItem, ItemEnum, ItemStruct, - ItemUse, LitStr, Token, Visibility, + ItemType, ItemUse, LitStr, Token, Visibility, }; pub struct Module { @@ -20,6 +20,7 @@ pub struct Module { pub enum Item { Struct(ItemStruct), Enum(ItemEnum), + Type(ItemType), ForeignMod(ItemForeignMod), Use(ItemUse), Other(RustItem), @@ -92,6 +93,7 @@ impl Parse for Item { match item { RustItem::Struct(item) => Ok(Item::Struct(ItemStruct { attrs, ..item })), RustItem::Enum(item) => Ok(Item::Enum(ItemEnum { attrs, ..item })), + RustItem::Type(item) => Ok(Item::Type(ItemType { attrs, ..item })), RustItem::ForeignMod(item) => Ok(Item::ForeignMod(ItemForeignMod { attrs: item.attrs, unsafety, diff --git a/syntax/parse.rs b/syntax/parse.rs index 49372509f..802374f38 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -12,8 +12,8 @@ use syn::parse::{ParseStream, Parser}; use syn::punctuated::Punctuated; use syn::{ Abi, Attribute, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, - GenericArgument, Ident, ItemEnum, ItemStruct, LitStr, Pat, PathArguments, Result, ReturnType, - Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice, + GenericArgument, Ident, ItemEnum, ItemStruct, ItemType, LitStr, Pat, PathArguments, Result, + ReturnType, Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice, }; pub mod kw { @@ -32,6 +32,10 @@ pub fn parse_items(cx: &mut Errors, items: Vec, trusted: bool) -> Vec Ok(enm) => apis.push(enm), Err(err) => cx.push(err), }, + Item::Type(item) => match parse_alias(cx, item, AliasKind::Shared) { + Ok(alias) => apis.push(alias), + Err(err) => cx.push(err), + }, Item::ForeignMod(foreign_mod) => parse_foreign_mod(cx, foreign_mod, &mut apis, trusted), Item::Use(item) => cx.error(item, error::USE_NOT_ALLOWED), Item::Other(item) => cx.error(item, "unsupported item"), @@ -170,6 +174,33 @@ fn parse_enum(cx: &mut Errors, item: ItemEnum) -> Result { })) } +fn parse_alias(cx: &mut Errors, item: ItemType, kind: AliasKind) -> Result { + let generics = &item.generics; + if !generics.params.is_empty() || generics.where_clause.is_some() { + // TODO: Add ui test for this + let type_token = item.type_token; + let ident = &item.ident; + let where_clause = &generics.where_clause; + let span = quote!(#type_token #ident #generics #where_clause); + return Err(Error::new_spanned( + span, + "aliases with generic parameters are not allowed", + )); + } + + let doc = attrs::parse_doc(cx, &item.attrs); + + Ok(Api::TypeAlias(TypeAlias { + kind, + doc, + type_token: item.type_token, + ident: item.ident, + eq_token: item.eq_token, + ty: *item.ty, + semi_token: item.semi_token, + })) +} + fn parse_foreign_mod( cx: &mut Errors, foreign_mod: ItemForeignMod, @@ -383,37 +414,27 @@ fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> R fn parse_extern_verbatim(cx: &mut Errors, tokens: &TokenStream, lang: Lang) -> Result { // type Alias = crate::path::to::Type; - let parse = |input: ParseStream| -> Result { - let attrs = input.call(Attribute::parse_outer)?; - let type_token: Token![type] = match input.parse()? { - Some(type_token) => type_token, - None => { - let span = input.cursor().token_stream(); - return Err(Error::new_spanned(span, "unsupported foreign item")); - } - }; - let ident: Ident = input.parse()?; - let eq_token: Token![=] = input.parse()?; - let ty: RustType = input.parse()?; - let semi_token: Token![;] = input.parse()?; - let doc = attrs::parse_doc(cx, &attrs); - - Ok(TypeAlias { - kind: AliasKind::OpaqueCpp, - doc, - type_token, - ident, - eq_token, - ty, - semi_token, - }) + let parse = |input: ParseStream| -> Result { + // Test whether this is a type alias. This ends up parsing attributes twice but lets us emit + // more useful errors that differentiate between an unsupported item and other alias parsing + // errors. + // TODO: Add ui test to verify this + let fork = input.fork(); + fork.call(Attribute::parse_outer)?; + fork.parse::().map_err(|_| { + let span = fork.cursor().token_stream(); + Error::new_spanned(span, "unsupported foreign item") + })?; + + // Reuse alias parsing from syn + input.parse() }; - let type_alias = parse.parse2(tokens.clone())?; + let item = parse.parse2(tokens.clone())?; match lang { - Lang::Cxx => Ok(Api::TypeAlias(type_alias)), + Lang::Cxx => parse_alias(cx, item, AliasKind::OpaqueCpp), Lang::Rust => { - let (type_token, semi_token) = (type_alias.type_token, type_alias.semi_token); + let (type_token, semi_token) = (item.type_token, item.semi_token); let span = quote!(#type_token #semi_token); let msg = "type alias in extern \"Rust\" block is not supported"; Err(Error::new_spanned(span, msg)) From 7c7633cd2fa56d8fdd3881951c09aebb152882da Mon Sep 17 00:00:00 2001 From: Bryan Henry Date: Sun, 20 Sep 2020 13:44:23 -0700 Subject: [PATCH 06/13] Add missing tests for c/r_return_mut --- tests/ffi/tests.cc | 7 +++++++ tests/test.rs | 10 +++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 1f88d8b62..7a23f7bf0 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -380,6 +380,13 @@ extern "C" const char *cxx_run_test() noexcept { ASSERT(cxx_test_suite_r_is_correct(&*r_return_box())); ASSERT(r_return_unique_ptr()->get() == 2020); ASSERT(r_return_ref(Shared{2020}) == 2020); + { + Shared shared{2019}; + size_t &mut_z = r_return_mut(shared); + ASSERT(mut_z == 2019); + mut_z = 2020; + ASSERT(r_return_ref(shared) == 2020); + } ASSERT(std::string(r_return_str(Shared{2020})) == "2020"); ASSERT(std::string(r_return_rust_string()) == "2020"); ASSERT(*r_return_unique_ptr_string() == "2020"); diff --git a/tests/test.rs b/tests/test.rs index f866c1e22..30cfb62ac 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -21,13 +21,21 @@ macro_rules! check { #[test] fn test_c_return() { - let shared = ffi::Shared { z: 2020 }; + let mut shared = ffi::Shared { z: 2020 }; assert_eq!(2020, ffi::c_return_primitive()); assert_eq!(2020, ffi::c_return_shared().z); assert_eq!(2020, *ffi::c_return_box()); ffi::c_return_unique_ptr(); assert_eq!(2020, *ffi::c_return_ref(&shared)); + + { + shared.z = 2019; + let mut_z = ffi::c_return_mut(&mut shared); + assert_eq!(2019, *mut_z); + *mut_z = 2020; + } + assert_eq!("2020", ffi::c_return_str(&shared)); assert_eq!(b"2020\0", ffi::c_return_sliceu8(&shared)); assert_eq!("2020", ffi::c_return_rust_string()); From 3ab1a3add361bf04c0b7cb9cf9b14a7e9b5c18ac Mon Sep 17 00:00:00 2001 From: Bryan Henry Date: Fri, 2 Oct 2020 11:49:29 -0700 Subject: [PATCH 07/13] Add expansion of shared type aliases and unit tests --- macro/src/expand.rs | 21 +++++++++++++++++---- syntax/check.rs | 3 +++ syntax/types.rs | 6 ++++-- tests/BUCK | 1 + tests/BUILD | 1 + tests/ffi/alias.rs | 41 +++++++++++++++++++++++++++++++++++++++++ tests/ffi/lib.rs | 12 ++++++------ tests/ffi/tests.cc | 32 +++++++++++++++++++++++++++++--- tests/ffi/tests.h | 11 +++++++++++ tests/test.rs | 44 ++++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 157 insertions(+), 15 deletions(-) diff --git a/macro/src/expand.rs b/macro/src/expand.rs index ef36f1088..6e7545a68 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -41,8 +41,8 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { for api in apis { match api { Api::Include(_) | Api::RustType(_) => {} - Api::Struct(strct) => expanded.extend(expand_struct(strct)), - Api::Enum(enm) => expanded.extend(expand_enum(enm)), + Api::Struct(strct) => expanded.extend(expand_struct(namespace, strct)), + Api::Enum(enm) => expanded.extend(expand_enum(namespace, enm)), Api::CxxType(ety) => { let ident = &ety.ident; if !types.structs.contains_key(ident) && !types.enums.contains_key(ident) { @@ -120,7 +120,7 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { } } -fn expand_struct(strct: &Struct) -> TokenStream { +fn expand_struct(namespace: &Namespace, strct: &Struct) -> TokenStream { let ident = &strct.ident; let doc = &strct.doc; let derives = &strct.derives; @@ -130,6 +130,8 @@ fn expand_struct(strct: &Struct) -> TokenStream { let vis = Token![pub](field.ident.span()); quote!(#vis #field) }); + let type_id = type_id(namespace, ident); + // TODO: Add ui test for mismatched alias kinds quote! { #doc #[derive(#(#derives),*)] @@ -137,10 +139,15 @@ fn expand_struct(strct: &Struct) -> TokenStream { pub struct #ident { #(#fields,)* } + + unsafe impl ::cxx::ExternType for #ident { + type Kind = ::cxx::extern_type::KindShared; + type Id = #type_id; + } } } -fn expand_enum(enm: &Enum) -> TokenStream { +fn expand_enum(namespace: &Namespace, enm: &Enum) -> TokenStream { let ident = &enm.ident; let doc = &enm.doc; let repr = enm.repr; @@ -151,6 +158,7 @@ fn expand_enum(enm: &Enum) -> TokenStream { pub const #variant_ident: Self = #ident { repr: #discriminant }; }) }); + let type_id = type_id(namespace, ident); quote! { #doc #[derive(Copy, Clone, PartialEq, Eq)] @@ -163,6 +171,11 @@ fn expand_enum(enm: &Enum) -> TokenStream { impl #ident { #(#variants)* } + + unsafe impl ::cxx::ExternType for #ident { + type Kind = ::cxx::extern_type::KindShared; + type Id = #type_id; + } } } diff --git a/syntax/check.rs b/syntax/check.rs index 147984e7e..cf5ba2ecb 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -64,6 +64,7 @@ fn check_type_ident(cx: &mut Check, ident: &Ident) { && !cx.types.enums.contains_key(ident) && !cx.types.cxx.contains(ident) && !cx.types.rust.contains(ident) + && !cx.types.aliases.contains_key(ident) { cx.error(ident, "unsupported type"); } @@ -230,6 +231,8 @@ fn check_api_fn(cx: &mut Check, efn: &ExternFn) { && !cx.types.cxx.contains(&receiver.ty) && !cx.types.rust.contains(&receiver.ty) { + // TODO: Add ui test for aliases being disallowed in receiver position since we can't + // tell if it's a struct or enum, unique error message cx.error(span, "unrecognized receiver type"); } diff --git a/syntax/types.rs b/syntax/types.rs index 8a86a4645..bcea90a25 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -1,7 +1,7 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::report::Errors; use crate::syntax::set::OrderedSet as Set; -use crate::syntax::{Api, Derive, Enum, ExternType, Struct, Type, TypeAlias}; +use crate::syntax::{AliasKind, Api, Derive, Enum, ExternType, Struct, Type, TypeAlias}; use proc_macro2::Ident; use quote::ToTokens; use std::collections::{BTreeMap as Map, HashSet as UnorderedSet}; @@ -129,7 +129,9 @@ impl<'a> Types<'a> { if !type_names.insert(ident) { duplicate_name(cx, alias, ident); } - cxx.insert(ident); + if let AliasKind::OpaqueCpp = alias.kind { + cxx.insert(ident); + } aliases.insert(ident, alias); } } diff --git a/tests/BUCK b/tests/BUCK index 93d68bc22..1e3e65880 100644 --- a/tests/BUCK +++ b/tests/BUCK @@ -28,6 +28,7 @@ cxx_library( ], header_namespace = "cxx-test-suite", headers = { + "alias.rs.h": ":alias/header", "lib.rs.h": ":bridge/header", "tests.h": "ffi/tests.h", }, diff --git a/tests/BUILD b/tests/BUILD index d9775bded..f06f332f3 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -32,6 +32,7 @@ cc_library( include_prefix = "cxx-test-suite", strip_include_prefix = "ffi", deps = [ + ":alias/include", ":bridge/include", "//:core", ], diff --git a/tests/ffi/alias.rs b/tests/ffi/alias.rs index 8862dc161..9deb9c5f8 100644 --- a/tests/ffi/alias.rs +++ b/tests/ffi/alias.rs @@ -3,11 +3,52 @@ #[rustfmt::skip] #[cxx::bridge(namespace = tests)] pub mod ffi { + type Shared = crate::ffi::Shared; + type Enum = crate::ffi::Enum; + extern "C" { include!("cxx-test-suite/tests.h"); + // Need complete C++ type declaration generated by other bridge for aliased shared types. + include!("cxx-test-suite/lib.rs.h"); type C = crate::ffi::C; + // TODO: The alias prefix can be removed once these are in their own namespace. + fn alias_c_return_shared() -> Shared; + fn alias_c_return_ref(shared: &Shared) -> &usize; + fn alias_c_return_mut(shared: &mut Shared) -> &mut usize; + fn alias_c_return_enum(n: u16) -> Enum; + fn alias_c_return_unique_ptr_vector_shared() -> UniquePtr>; + + fn alias_c_take_shared(shared: Shared); + // TODO: This don't work yet because both bridges try to emit the rust_vec$tests$Shared + // functions. Need the remote bridge to always emit if this is gonna work. Or can we work + // around ODR by making the rust_vec functions inline? + //fn alias_c_take_rust_vec_shared(v: Vec); + // TODO: Box probably has the same problem, not currently tested + // TODO: Same for UniquePtr, not currently tested + // TODO: Below probably doesn't work if the remote bridge doesn't use CxxVector + fn alias_c_take_unique_ptr_vector_shared(v: UniquePtr>); + fn alias_c_take_enum(e: Enum); + fn c_take_unique_ptr(c: UniquePtr); } + + extern "Rust" { + // TODO: The alias prefix can be removed once these are in their own namespace. + fn alias_r_return_shared() -> Shared; + fn alias_r_return_ref(shared: &Shared) -> &usize; + fn alias_r_return_mut(shared: &mut Shared) -> &mut usize; + fn alias_r_return_enum(n: u32) -> Enum; + + fn alias_r_take_shared(shared: Shared); + fn alias_r_take_enum(e: Enum); + } } + +use crate::r_return_enum as alias_r_return_enum; +use crate::r_return_mut as alias_r_return_mut; +use crate::r_return_ref as alias_r_return_ref; +use crate::r_return_shared as alias_r_return_shared; +use crate::r_take_enum as alias_r_take_enum; +use crate::r_take_shared as alias_r_take_shared; diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index 165df1063..de8f8be84 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -193,7 +193,7 @@ fn r_return_primitive() -> usize { 2020 } -fn r_return_shared() -> ffi::Shared { +pub(crate) fn r_return_shared() -> ffi::Shared { ffi::Shared { z: 2020 } } @@ -208,11 +208,11 @@ fn r_return_unique_ptr() -> UniquePtr { unsafe { UniquePtr::from_raw(cxx_test_suite_get_unique_ptr()) } } -fn r_return_ref(shared: &ffi::Shared) -> &usize { +pub(crate) fn r_return_ref(shared: &ffi::Shared) -> &usize { &shared.z } -fn r_return_mut(shared: &mut ffi::Shared) -> &mut usize { +pub(crate) fn r_return_mut(shared: &mut ffi::Shared) -> &mut usize { &mut shared.z } @@ -258,7 +258,7 @@ fn r_return_sum(n1: usize, n2: usize) -> usize { n1 + n2 } -fn r_return_enum(n: u32) -> ffi::Enum { +pub(crate) fn r_return_enum(n: u32) -> ffi::Enum { if n == 0 { ffi::Enum::AVal } else if n <= 2020 { @@ -272,7 +272,7 @@ fn r_take_primitive(n: usize) { assert_eq!(n, 2020); } -fn r_take_shared(shared: ffi::Shared) { +pub(crate) fn r_take_shared(shared: ffi::Shared) { assert_eq!(shared.z, 2020); } @@ -335,7 +335,7 @@ fn r_take_ref_rust_vec_string(v: &Vec) { let _ = v; } -fn r_take_enum(e: ffi::Enum) { +pub(crate) fn r_take_enum(e: ffi::Enum) { let _ = e; } diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 7a23f7bf0..8b9a9ee21 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -1,4 +1,5 @@ #include "cxx-test-suite/tests.h" +#include "cxx-test-suite/alias.rs.h" #include "cxx-test-suite/lib.rs.h" #include #include @@ -357,15 +358,16 @@ const rust::Vec &c_try_return_ref_rust_vec(const C &c) { throw std::runtime_error("unimplemented"); } -extern "C" C *cxx_test_suite_get_unique_ptr() noexcept { +extern "C" { + +C *cxx_test_suite_get_unique_ptr() noexcept { return std::unique_ptr(new C{2020}).release(); } -extern "C" std::string *cxx_test_suite_get_unique_ptr_string() noexcept { +std::string *cxx_test_suite_get_unique_ptr_string() noexcept { return std::unique_ptr(new std::string("2020")).release(); } -extern "C" const char *cxx_run_test() noexcept { #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) #define ASSERT(x) \ @@ -375,6 +377,7 @@ extern "C" const char *cxx_run_test() noexcept { } \ } while (false) +const char *cxx_run_test() noexcept { ASSERT(r_return_primitive() == 2020); ASSERT(r_return_shared().z == 2020); ASSERT(cxx_test_suite_r_is_correct(&*r_return_box())); @@ -432,4 +435,27 @@ extern "C" const char *cxx_run_test() noexcept { return nullptr; } +const char *cxx_run_alias_test() noexcept { + ASSERT(alias_r_return_shared().z == 2020); + ASSERT(alias_r_return_ref(Shared{2020}) == 2020); + { + Shared shared{2019}; + size_t &mut_z = alias_r_return_mut(shared); + ASSERT(mut_z == 2019); + mut_z = 2020; + ASSERT(alias_r_return_ref(shared) == 2020); + } + ASSERT(alias_r_return_enum(0) == Enum::AVal); + ASSERT(alias_r_return_enum(1) == Enum::BVal); + ASSERT(alias_r_return_enum(2021) == Enum::CVal); + + r_take_shared(Shared{2020}); + r_take_enum(Enum::AVal); + + cxx_test_suite_set_correct(); + return nullptr; +} + +} // extern "C" + } // namespace tests diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index f3bc2dd5f..e691865be 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -101,4 +101,15 @@ rust::Vec c_try_return_rust_vec(); rust::Vec c_try_return_rust_vec_string(); const rust::Vec &c_try_return_ref_rust_vec(const C &c); +const auto alias_c_return_shared = c_return_shared; +const auto alias_c_return_ref = c_return_ref; +const auto alias_c_return_mut = c_return_mut; +const auto alias_c_return_enum = c_return_enum; +const auto alias_c_return_unique_ptr_vector_shared = + c_return_unique_ptr_vector_shared; +const auto alias_c_take_unique_ptr_vector_shared = + c_take_unique_ptr_vector_shared; +const auto alias_c_take_shared = c_take_shared; +const auto alias_c_take_enum = c_take_enum; + } // namespace tests diff --git a/tests/test.rs b/tests/test.rs index 30cfb62ac..bc805e00c 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -73,6 +73,26 @@ fn test_c_return() { } } +#[test] +fn test_alias_c_return() { + let mut shared = ffi::Shared { z: 2020 }; + + assert_eq!(2020, alias::ffi::alias_c_return_shared().z); + assert_eq!(2020, *alias::ffi::alias_c_return_ref(&shared)); + + { + shared.z = 2019; + let mut_z = alias::ffi::alias_c_return_mut(&mut shared); + assert_eq!(2019, *mut_z); + *mut_z = 2020; + } + + match alias::ffi::alias_c_return_enum(1) { + enm @ ffi::Enum::BVal => assert_eq!(2020, enm.repr), + _ => assert!(false), + } +} + #[test] fn test_c_try_return() { assert_eq!((), ffi::c_try_return_void().unwrap()); @@ -129,6 +149,15 @@ fn test_c_take() { check!(ffi::c_take_enum(ffi::Enum::AVal)); } +#[test] +fn test_alias_c_take() { + check!(alias::ffi::alias_c_take_shared(ffi::Shared { z: 2020 })); + check!(alias::ffi::alias_c_take_enum(ffi::Enum::AVal)); + check!(alias::ffi::alias_c_take_unique_ptr_vector_shared( + alias::ffi::alias_c_return_unique_ptr_vector_shared() + )); +} + /* // https://github.com/dtolnay/cxx/issues/232 #[test] @@ -159,6 +188,21 @@ fn test_c_call_r() { check!(cxx_run_test()); } +#[test] +fn test_alias_c_call_r() { + fn cxx_run_alias_test() { + extern "C" { + fn cxx_run_alias_test() -> *const i8; + } + let failure = unsafe { cxx_run_alias_test() }; + if !failure.is_null() { + let msg = unsafe { CStr::from_ptr(failure as *mut std::os::raw::c_char) }; + eprintln!("{}", msg.to_string_lossy()); + } + } + check!(cxx_run_alias_test()); +} + #[test] fn test_c_method_calls() { let mut unique_ptr = ffi::c_return_unique_ptr(); From bf2c451a964fcabf515cc4c7fb3f1ecffd03d555 Mon Sep 17 00:00:00 2001 From: Bryan Henry Date: Sun, 20 Sep 2020 13:53:56 -0700 Subject: [PATCH 08/13] Add docs for shared type aliases --- src/extern_type.rs | 95 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-) diff --git a/src/extern_type.rs b/src/extern_type.rs index fe3e9a28a..6cc6e8e90 100644 --- a/src/extern_type.rs +++ b/src/extern_type.rs @@ -1,7 +1,10 @@ /// A type for which the layout is determined by an external definition. /// /// `ExternType` makes it possible for CXX to safely share a consistent Rust type across multiple -/// #\[cxx::bridge\] invocations. This serves multiple related purposes. +/// #\[cxx::bridge\] invocations, both for shared types defined in another bridge and external C++ +/// definitions. This serves multiple related purposes. +/// +/// TODO: These docs aren't discoverable. Add a link to here from the main crate docs. /// ///
/// @@ -48,6 +51,96 @@ /// ///

/// +/// ## Reusing Rust/C++ shared types across multiple bridges +/// +/// `ExternType` enables reusing a shared Rust/C++ type declared in another bridge module, allowing +/// for the creation of libraries to wrap types used in multiple different bridges. +/// +/// Imagine we have an existing move-only C++ type, file::UniqueFd, that wraps sole ownership of a +/// file descriptor, analogous to Rust's std::fd::File. The example below defines a shared type +/// `File` that allows safely transferring ownership of the file across the interface without Box or +/// UniquePtr and without resource leaks. This type can then be reused in other bridges. +/// +/// ```no_run +/// // file/src/lib.rs +/// # #[cfg(unix)] +/// # mod file { +/// # use std::os::unix::io::{IntoRawFd, FromRawFd}; +/// #[cxx::bridge(namespace = file::ffi)] +/// pub mod ffi { +/// /// A file backed by a file descriptor, which it is the sole owner of. +/// struct File { +/// fd: i32, +/// } +/// } +/// +/// impl From for std::fs::File { +/// fn from(value: ffi::File) -> Self { +/// // Safe because ffi::File owns its file descriptor. +/// unsafe { Self::from_raw_fd(value.fd) } +/// } +/// } +/// +/// impl From for ffi::File { +/// fn from(value: std::fs::File) -> Self { +/// Self { fd: value.into_raw_fd() } +/// } +/// } +/// +/// impl Drop for ffi::File { +/// fn drop(&mut self) { +/// // Safe because ffi::File owns its file descriptor. +/// unsafe { std::fs::File::from_raw_fd(self.fd); } +/// } +/// } +/// # } +/// +/// // file/src/lib.h +/// # /* +/// namespace file { +/// +/// ffi::File TransferToFFI(File file) { +/// // Imagine file::UniqueFd::release() is analogous to from_raw_fd +/// return ffi::File{ .fd = file.release() }; +/// } +/// +/// } +/// # */ +/// +/// // TODO(https://github.com/dtolnay/cxx/pull/298): Currently this bridge must use the same +/// // namespace as any bridge it creates aliases from. +/// +/// // usage.rs +/// # #[cfg(unix)] +/// # mod usage { +/// #[cxx::bridge(namespace = file::ffi)] +/// pub mod ffi { +/// type File = crate::file::ffi::File; +/// +/// extern "C" { +/// type Demo; +/// +/// fn create_demo(file: File) -> UniquePtr; +/// } +/// } +/// # } +/// +/// // usage.cc +/// # /* +/// file::ffi::File ConvertFile(file::UniqueFd file) { +/// } +/// +/// void CreateDemo(file::UniqueFd file) { +/// auto demo = ffi::create_demo(file::TransferToFFI(std::move(file))); +/// // use demo +/// } +/// # */ +/// +/// # fn main() {} +/// ``` +/// +///

+/// /// ## Integrating with bindgen-generated types /// /// Handwritten `ExternType` impls make it possible to plug in a data structure emitted by bindgen From f21cea8b0641b52a402f4906c137688e10d08503 Mon Sep 17 00:00:00 2001 From: Bryan Henry Date: Fri, 2 Oct 2020 13:27:45 -0700 Subject: [PATCH 09/13] Don't generate Vec/Box impls for aliases --- gen/src/write.rs | 14 +++++++++----- macro/src/expand.rs | 4 ++-- tests/ffi/alias.rs | 21 ++++++++++----------- tests/ffi/lib.rs | 4 ++++ tests/ffi/tests.cc | 16 ++++++++++++++++ tests/ffi/tests.h | 12 +++++++++++- tests/test.rs | 21 +++++++++++++++++++-- 7 files changed, 71 insertions(+), 21 deletions(-) diff --git a/gen/src/write.rs b/gen/src/write.rs index 2e2b81f8c..aef8b47b5 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -1007,12 +1007,14 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { for ty in types { if let Type::RustBox(ty) = ty { if let Type::Ident(inner) = &ty.inner { - out.next_section(); - write_rust_box_extern(out, inner); + if Atom::from(inner).is_none() && !types.aliases.contains_key(inner) { + out.next_section(); + write_rust_box_extern(out, inner); + } } } else if let Type::RustVec(ty) = ty { if let Type::Ident(inner) = &ty.inner { - if Atom::from(inner).is_none() { + if Atom::from(inner).is_none() && !types.aliases.contains_key(inner) { out.next_section(); write_rust_vec_extern(out, inner); } @@ -1040,11 +1042,13 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { for ty in types { if let Type::RustBox(ty) = ty { if let Type::Ident(inner) = &ty.inner { - write_rust_box_impl(out, inner); + if Atom::from(inner).is_none() && !types.aliases.contains_key(inner) { + write_rust_box_impl(out, inner); + } } } else if let Type::RustVec(ty) = ty { if let Type::Ident(inner) = &ty.inner { - if Atom::from(inner).is_none() { + if Atom::from(inner).is_none() && !types.aliases.contains_key(inner) { write_rust_vec_impl(out, inner); } } diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 6e7545a68..c3c3fbed5 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -65,13 +65,13 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { for ty in types { if let Type::RustBox(ty) = ty { if let Type::Ident(ident) = &ty.inner { - if Atom::from(ident).is_none() { + if Atom::from(ident).is_none() && !types.aliases.contains_key(ident) { hidden.extend(expand_rust_box(namespace, ident)); } } } else if let Type::RustVec(ty) = ty { if let Type::Ident(ident) = &ty.inner { - if Atom::from(ident).is_none() { + if Atom::from(ident).is_none() && !types.aliases.contains_key(ident) { hidden.extend(expand_rust_vec(namespace, ident)); } } diff --git a/tests/ffi/alias.rs b/tests/ffi/alias.rs index 9deb9c5f8..37934957d 100644 --- a/tests/ffi/alias.rs +++ b/tests/ffi/alias.rs @@ -13,29 +13,28 @@ pub mod ffi { type C = crate::ffi::C; - // TODO: The alias prefix can be removed once these are in their own namespace. + // TODO(https://github.com/dtolnay/cxx/pull/298): The alias prefix can be removed once these + // are in their own namespace. fn alias_c_return_shared() -> Shared; + fn alias_c_return_unique_ptr() -> UniquePtr; fn alias_c_return_ref(shared: &Shared) -> &usize; fn alias_c_return_mut(shared: &mut Shared) -> &mut usize; fn alias_c_return_enum(n: u16) -> Enum; + fn alias_c_return_unique_ptr_shared() -> UniquePtr; fn alias_c_return_unique_ptr_vector_shared() -> UniquePtr>; fn alias_c_take_shared(shared: Shared); - // TODO: This don't work yet because both bridges try to emit the rust_vec$tests$Shared - // functions. Need the remote bridge to always emit if this is gonna work. Or can we work - // around ODR by making the rust_vec functions inline? - //fn alias_c_take_rust_vec_shared(v: Vec); - // TODO: Box probably has the same problem, not currently tested - // TODO: Same for UniquePtr, not currently tested - // TODO: Below probably doesn't work if the remote bridge doesn't use CxxVector + fn alias_c_take_box_shared(shared: Box); + fn alias_c_take_unique_ptr(c: UniquePtr); + fn alias_c_take_unique_ptr_shared(s: UniquePtr); fn alias_c_take_unique_ptr_vector_shared(v: UniquePtr>); + fn alias_c_take_rust_vec_shared(v: Vec); fn alias_c_take_enum(e: Enum); - - fn c_take_unique_ptr(c: UniquePtr); } extern "Rust" { - // TODO: The alias prefix can be removed once these are in their own namespace. + // TODO(https://github.com/dtolnay/cxx/pull/298): The alias prefix can be removed once these + // are in their own namespace. fn alias_r_return_shared() -> Shared; fn alias_r_return_ref(shared: &Shared) -> &usize; fn alias_r_return_mut(shared: &mut Shared) -> &mut usize; diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index de8f8be84..bc5976c8d 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -31,6 +31,7 @@ pub mod ffi { fn c_return_shared() -> Shared; fn c_return_box() -> Box; fn c_return_unique_ptr() -> UniquePtr; + fn c_return_unique_ptr_shared() -> UniquePtr; fn c_return_ref(shared: &Shared) -> &usize; fn c_return_mut(shared: &mut Shared) -> &mut usize; fn c_return_str(shared: &Shared) -> &str; @@ -55,6 +56,9 @@ pub mod ffi { fn c_take_primitive(n: usize); fn c_take_shared(shared: Shared); fn c_take_box(r: Box); + fn c_take_box_shared(shared: Box); + fn c_take_unique_ptr(c: UniquePtr); + fn c_take_unique_ptr_shared(s: UniquePtr); fn c_take_ref_r(r: &R); fn c_take_ref_c(c: &C); fn c_take_str(s: &str); diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 8b9a9ee21..7cb29cfd3 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -67,6 +67,10 @@ rust::Slice c_return_sliceu8(const Shared &shared) { rust::String c_return_rust_string() { return "2020"; } +std::unique_ptr c_return_unique_ptr_shared() { + return std::unique_ptr(new Shared{2020}); +} + std::unique_ptr c_return_unique_ptr_string() { return std::unique_ptr(new std::string("2020")); } @@ -161,12 +165,24 @@ void c_take_box(rust::Box r) { } } +void c_take_box_shared(rust::Box shared) { + if (shared->z == 2020) { + cxx_test_suite_set_correct(); + } +} + void c_take_unique_ptr(std::unique_ptr c) { if (c->get() == 2020) { cxx_test_suite_set_correct(); } } +void c_take_unique_ptr_shared(std::unique_ptr shared) { + if (shared->z == 2020) { + cxx_test_suite_set_correct(); + } +} + void c_take_ref_r(const R &r) { if (cxx_test_suite_r_is_correct(&r)) { cxx_test_suite_set_correct(); diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index e691865be..97bbc5afa 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -40,6 +40,7 @@ size_t &c_return_mut(Shared &shared); rust::Str c_return_str(const Shared &shared); rust::Slice c_return_sliceu8(const Shared &shared); rust::String c_return_rust_string(); +std::unique_ptr c_return_unique_ptr_shared(); std::unique_ptr c_return_unique_ptr_string(); std::unique_ptr> c_return_unique_ptr_vector_u8(); std::unique_ptr> c_return_unique_ptr_vector_f64(); @@ -59,7 +60,9 @@ Enum c_return_enum(uint16_t n); void c_take_primitive(size_t n); void c_take_shared(Shared shared); void c_take_box(rust::Box r); +void c_take_box_shared(rust::Box shared); void c_take_unique_ptr(std::unique_ptr c); +void c_take_unique_ptr_shared(std::unique_ptr shared); void c_take_ref_r(const R &r); void c_take_ref_c(const C &c); void c_take_str(rust::Str s); @@ -102,14 +105,21 @@ rust::Vec c_try_return_rust_vec_string(); const rust::Vec &c_try_return_ref_rust_vec(const C &c); const auto alias_c_return_shared = c_return_shared; +const auto alias_c_return_unique_ptr = c_return_unique_ptr; const auto alias_c_return_ref = c_return_ref; const auto alias_c_return_mut = c_return_mut; const auto alias_c_return_enum = c_return_enum; +const auto alias_c_return_unique_ptr_shared = c_return_unique_ptr_shared; const auto alias_c_return_unique_ptr_vector_shared = c_return_unique_ptr_vector_shared; + +const auto alias_c_take_shared = c_take_shared; +const auto alias_c_take_box_shared = c_take_box_shared; +const auto alias_c_take_unique_ptr = c_take_unique_ptr; +const auto alias_c_take_unique_ptr_shared = c_take_unique_ptr_shared; const auto alias_c_take_unique_ptr_vector_shared = c_take_unique_ptr_vector_shared; -const auto alias_c_take_shared = c_take_shared; +const auto alias_c_take_rust_vec_shared = c_take_rust_vec_shared; const auto alias_c_take_enum = c_take_enum; } // namespace tests diff --git a/tests/test.rs b/tests/test.rs index bc805e00c..565ac837e 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -116,8 +116,12 @@ fn test_c_take() { check!(ffi::c_take_primitive(2020)); check!(ffi::c_take_shared(ffi::Shared { z: 2020 })); check!(ffi::c_take_box(Box::new(2020))); + check!(ffi::c_take_box_shared(Box::new(ffi::Shared { z: 2020 }))); check!(ffi::c_take_ref_c(&unique_ptr)); - check!(alias::ffi::c_take_unique_ptr(unique_ptr)); + check!(ffi::c_take_unique_ptr(unique_ptr)); + check!(ffi::c_take_unique_ptr_shared( + ffi::c_return_unique_ptr_shared() + )); check!(ffi::c_take_str("2020")); check!(ffi::c_take_sliceu8(b"2020")); check!(ffi::c_take_rust_string("2020".to_owned())); @@ -151,11 +155,24 @@ fn test_c_take() { #[test] fn test_alias_c_take() { + let unique_ptr = alias::ffi::alias_c_return_unique_ptr(); + check!(alias::ffi::alias_c_take_shared(ffi::Shared { z: 2020 })); - check!(alias::ffi::alias_c_take_enum(ffi::Enum::AVal)); + check!(alias::ffi::alias_c_take_box_shared(Box::new(ffi::Shared { + z: 2020 + }))); + check!(alias::ffi::alias_c_take_unique_ptr(unique_ptr)); + check!(alias::ffi::alias_c_take_unique_ptr_shared( + alias::ffi::alias_c_return_unique_ptr_shared() + )); check!(alias::ffi::alias_c_take_unique_ptr_vector_shared( alias::ffi::alias_c_return_unique_ptr_vector_shared() )); + let shared_test_vec = vec![ffi::Shared { z: 1010 }, ffi::Shared { z: 1011 }]; + check!(alias::ffi::alias_c_take_rust_vec_shared( + shared_test_vec.clone() + )); + check!(alias::ffi::alias_c_take_enum(ffi::Enum::AVal)); } /* From cb2e9e9fd596c5b0caf2e749ab142e56715a92a4 Mon Sep 17 00:00:00 2001 From: Bryan Henry Date: Fri, 2 Oct 2020 15:19:38 -0700 Subject: [PATCH 10/13] Add ui tests for new and modified errors --- macro/src/expand.rs | 1 - syntax/parse.rs | 10 +++----- tests/ui/alias_parse_errors.rs | 18 ++++++++++++++ tests/ui/alias_parse_errors.stderr | 17 +++++++++++++ tests/ui/alias_wrong_kind.rs | 24 +++++++++++++++++++ tests/ui/alias_wrong_kind.stderr | 21 ++++++++++++++++ ...rong_type_id.rs => alias_wrong_type_id.rs} | 0 ...e_id.stderr => alias_wrong_type_id.stderr} | 2 +- tests/ui/type_alias_rust.rs | 9 ------- tests/ui/type_alias_rust.stderr | 5 ---- 10 files changed, 84 insertions(+), 23 deletions(-) create mode 100644 tests/ui/alias_parse_errors.rs create mode 100644 tests/ui/alias_parse_errors.stderr create mode 100644 tests/ui/alias_wrong_kind.rs create mode 100644 tests/ui/alias_wrong_kind.stderr rename tests/ui/{wrong_type_id.rs => alias_wrong_type_id.rs} (100%) rename tests/ui/{wrong_type_id.stderr => alias_wrong_type_id.stderr} (94%) delete mode 100644 tests/ui/type_alias_rust.rs delete mode 100644 tests/ui/type_alias_rust.stderr diff --git a/macro/src/expand.rs b/macro/src/expand.rs index c3c3fbed5..d721aefd0 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -131,7 +131,6 @@ fn expand_struct(namespace: &Namespace, strct: &Struct) -> TokenStream { quote!(#vis #field) }); let type_id = type_id(namespace, ident); - // TODO: Add ui test for mismatched alias kinds quote! { #doc #[derive(#(#derives),*)] diff --git a/syntax/parse.rs b/syntax/parse.rs index 802374f38..a4b2cb1fd 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -177,7 +177,6 @@ fn parse_enum(cx: &mut Errors, item: ItemEnum) -> Result { fn parse_alias(cx: &mut Errors, item: ItemType, kind: AliasKind) -> Result { let generics = &item.generics; if !generics.params.is_empty() || generics.where_clause.is_some() { - // TODO: Add ui test for this let type_token = item.type_token; let ident = &item.ident; let where_clause = &generics.where_clause; @@ -417,14 +416,11 @@ fn parse_extern_verbatim(cx: &mut Errors, tokens: &TokenStream, lang: Lang) -> R let parse = |input: ParseStream| -> Result { // Test whether this is a type alias. This ends up parsing attributes twice but lets us emit // more useful errors that differentiate between an unsupported item and other alias parsing - // errors. - // TODO: Add ui test to verify this + // errors while still reusing syn's ItemType parsing. let fork = input.fork(); fork.call(Attribute::parse_outer)?; - fork.parse::().map_err(|_| { - let span = fork.cursor().token_stream(); - Error::new_spanned(span, "unsupported foreign item") - })?; + fork.parse::() + .map_err(|_| Error::new_spanned(tokens, "unsupported foreign item"))?; // Reuse alias parsing from syn input.parse() diff --git a/tests/ui/alias_parse_errors.rs b/tests/ui/alias_parse_errors.rs new file mode 100644 index 000000000..955989f67 --- /dev/null +++ b/tests/ui/alias_parse_errors.rs @@ -0,0 +1,18 @@ +// Rustfmt mangles the extern type alias. +// https://github.com/rust-lang/rustfmt/issues/4159 +// ...normally would add #[rustfmt::skip], but that seems to interfere with the error spans. +#[cxx::bridge] +mod ffi { + extern "C" { + fn unsupported_foreign_item() {} + + type BadGeneric = Bad; + } + + extern "Rust" { + /// Incorrect. + type Alias = crate::Type; + } +} + +fn main() {} diff --git a/tests/ui/alias_parse_errors.stderr b/tests/ui/alias_parse_errors.stderr new file mode 100644 index 000000000..be010f508 --- /dev/null +++ b/tests/ui/alias_parse_errors.stderr @@ -0,0 +1,17 @@ +error: unsupported foreign item + --> $DIR/alias_parse_errors.rs:7:9 + | +7 | fn unsupported_foreign_item() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aliases with generic parameters are not allowed + --> $DIR/alias_parse_errors.rs:9:9 + | +9 | type BadGeneric = Bad; + | ^^^^^^^^^^^^^^^^^^ + +error: type alias in extern "Rust" block is not supported + --> $DIR/alias_parse_errors.rs:14:9 + | +14 | type Alias = crate::Type; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/alias_wrong_kind.rs b/tests/ui/alias_wrong_kind.rs new file mode 100644 index 000000000..81cc85fc4 --- /dev/null +++ b/tests/ui/alias_wrong_kind.rs @@ -0,0 +1,24 @@ +#[cxx::bridge] +mod here { + struct Shared { + z: usize, + } + + extern "C" { + type C; + } +} + +// Rustfmt mangles the extern type alias. +// https://github.com/rust-lang/rustfmt/issues/4159 +#[rustfmt::skip] +#[cxx::bridge] +mod there { + type C = crate::here::C; + + extern "C" { + type Shared = crate::here::Shared; + } +} + +fn main() {} diff --git a/tests/ui/alias_wrong_kind.stderr b/tests/ui/alias_wrong_kind.stderr new file mode 100644 index 000000000..d8b223133 --- /dev/null +++ b/tests/ui/alias_wrong_kind.stderr @@ -0,0 +1,21 @@ +error[E0271]: type mismatch resolving `::Kind == KindShared` + --> $DIR/alias_wrong_kind.rs:15:1 + | +15 | #[cxx::bridge] + | ^^^^^^^^^^^^^^ expected struct `KindShared`, found struct `KindOpaqueCpp` + | + ::: $WORKSPACE/src/extern_type.rs + | + | pub fn verify_extern_type, Kind, Id>() {} + | ----------- required by this bound in `verify_extern_type` + +error[E0271]: type mismatch resolving `::Kind == KindOpaqueCpp` + --> $DIR/alias_wrong_kind.rs:15:1 + | +15 | #[cxx::bridge] + | ^^^^^^^^^^^^^^ expected struct `KindOpaqueCpp`, found struct `KindShared` + | + ::: $WORKSPACE/src/extern_type.rs + | + | pub fn verify_extern_type, Kind, Id>() {} + | ----------- required by this bound in `verify_extern_type` diff --git a/tests/ui/wrong_type_id.rs b/tests/ui/alias_wrong_type_id.rs similarity index 100% rename from tests/ui/wrong_type_id.rs rename to tests/ui/alias_wrong_type_id.rs diff --git a/tests/ui/wrong_type_id.stderr b/tests/ui/alias_wrong_type_id.stderr similarity index 94% rename from tests/ui/wrong_type_id.stderr rename to tests/ui/alias_wrong_type_id.stderr index ca4370abf..38a93ea3a 100644 --- a/tests/ui/wrong_type_id.stderr +++ b/tests/ui/alias_wrong_type_id.stderr @@ -1,5 +1,5 @@ error[E0271]: type mismatch resolving `::Id == (f, o, l, l, y, (), B, y, t, e, R, a, n, g, e)` - --> $DIR/wrong_type_id.rs:11:9 + --> $DIR/alias_wrong_type_id.rs:11:9 | 11 | type ByteRange = crate::here::StringPiece; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected a tuple with 15 elements, found one with 17 elements diff --git a/tests/ui/type_alias_rust.rs b/tests/ui/type_alias_rust.rs deleted file mode 100644 index 67df48926..000000000 --- a/tests/ui/type_alias_rust.rs +++ /dev/null @@ -1,9 +0,0 @@ -#[cxx::bridge] -mod ffi { - extern "Rust" { - /// Incorrect. - type Alias = crate::Type; - } -} - -fn main() {} diff --git a/tests/ui/type_alias_rust.stderr b/tests/ui/type_alias_rust.stderr deleted file mode 100644 index 1b08f67cc..000000000 --- a/tests/ui/type_alias_rust.stderr +++ /dev/null @@ -1,5 +0,0 @@ -error: type alias in extern "Rust" block is not supported - --> $DIR/type_alias_rust.rs:5:9 - | -5 | type Alias = crate::Type; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ From fb0ba254f53fef1659e981e80eff65062a1e5452 Mon Sep 17 00:00:00 2001 From: Bryan Henry Date: Fri, 2 Oct 2020 15:37:10 -0700 Subject: [PATCH 11/13] Improve error for shared alias receiver type plus tests --- syntax/check.rs | 12 ++++++++++-- tests/ffi/alias.rs | 3 +++ tests/ffi/tests.cc | 7 +++++++ tests/ffi/tests.h | 2 ++ tests/test.rs | 10 ++++++++++ tests/ui/alias_receiver_type.rs | 27 +++++++++++++++++++++++++++ tests/ui/alias_receiver_type.stderr | 5 +++++ 7 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 tests/ui/alias_receiver_type.rs create mode 100644 tests/ui/alias_receiver_type.stderr diff --git a/syntax/check.rs b/syntax/check.rs index cf5ba2ecb..0c4f59334 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -227,12 +227,20 @@ fn check_api_fn(cx: &mut Check, efn: &ExternFn) { mutability = mutability, ); cx.error(span, msg); + } else if cx.types.aliases.contains_key(&receiver.ty) + && !cx.types.cxx.contains(&receiver.ty) + { + // We cannot add class member functions to the type generated by the other bridge, plus + // we can't tell whether the alias is for a struct or enum and only the former can have + // member functions. + cx.error( + span, + "shared type aliases cannot be used as a receiver type", + ) } else if !cx.types.structs.contains_key(&receiver.ty) && !cx.types.cxx.contains(&receiver.ty) && !cx.types.rust.contains(&receiver.ty) { - // TODO: Add ui test for aliases being disallowed in receiver position since we can't - // tell if it's a struct or enum, unique error message cx.error(span, "unrecognized receiver type"); } diff --git a/tests/ffi/alias.rs b/tests/ffi/alias.rs index 37934957d..7c3b64d87 100644 --- a/tests/ffi/alias.rs +++ b/tests/ffi/alias.rs @@ -30,6 +30,9 @@ pub mod ffi { fn alias_c_take_unique_ptr_vector_shared(v: UniquePtr>); fn alias_c_take_rust_vec_shared(v: Vec); fn alias_c_take_enum(e: Enum); + + fn get3(self: &C) -> usize; + fn set3(self: &mut C, n: usize) -> usize; } extern "Rust" { diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index 7cb29cfd3..33dc84970 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -20,6 +20,8 @@ size_t C::get() const { return this->n; } size_t C::get2() const { return this->n; } +size_t C::get3() const { return this->n; } + size_t C::set(size_t n) { this->n = n; return this->n; @@ -30,6 +32,11 @@ size_t C::set2(size_t n) { return this->n; } +size_t C::set3(size_t n) { + this->n = n; + return this->n; +} + size_t C::set_succeed(size_t n) { return this->set2(n); } size_t C::get_fail() { throw std::runtime_error("unimplemented"); } diff --git a/tests/ffi/tests.h b/tests/ffi/tests.h index 97bbc5afa..eda698504 100644 --- a/tests/ffi/tests.h +++ b/tests/ffi/tests.h @@ -16,6 +16,8 @@ class C { size_t set(size_t n); size_t get2() const; size_t set2(size_t n); + size_t get3() const; + size_t set3(size_t n); size_t set_succeed(size_t n); size_t get_fail(); const std::vector &get_v() const; diff --git a/tests/test.rs b/tests/test.rs index 565ac837e..ae20e90c5 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -234,6 +234,16 @@ fn test_c_method_calls() { assert!(unique_ptr.get_fail().is_err()); } +#[test] +fn test_alias_c_method_calls() { + let mut unique_ptr = alias::ffi::alias_c_return_unique_ptr(); + + let old_value = unique_ptr.get3(); + assert_eq!(2020, old_value); + assert_eq!(2021, unique_ptr.set3(2021)); + assert_eq!(2021, unique_ptr.get3()); +} + #[test] fn test_enum_representations() { assert_eq!(0, ffi::Enum::AVal.repr); diff --git a/tests/ui/alias_receiver_type.rs b/tests/ui/alias_receiver_type.rs new file mode 100644 index 000000000..4131334b8 --- /dev/null +++ b/tests/ui/alias_receiver_type.rs @@ -0,0 +1,27 @@ +#[cxx::bridge] +mod here { + struct Shared { + z: usize, + } + + extern "C" { + type C; + } +} + +// Rustfmt mangles the extern type alias. +// https://github.com/rust-lang/rustfmt/issues/4159 +// ...normally would add #[rustfmt::skip], but that seems to interfere with the error spans. +#[cxx::bridge] +mod there { + type Shared = crate::here::Shared; + + extern "C" { + type C = crate::here::C; + + fn good(self: &C); + fn bad(self: &Shared); + } +} + +fn main() {} diff --git a/tests/ui/alias_receiver_type.stderr b/tests/ui/alias_receiver_type.stderr new file mode 100644 index 000000000..597494ea0 --- /dev/null +++ b/tests/ui/alias_receiver_type.stderr @@ -0,0 +1,5 @@ +error: shared type aliases cannot be used as a receiver type + --> $DIR/alias_receiver_type.rs:23:22 + | +23 | fn bad(self: &Shared); + | ^^^^^^^ From 143753a10e6c9dd9e17bb36b93df0c24a6544717 Mon Sep 17 00:00:00 2001 From: Bryan Henry Date: Fri, 2 Oct 2020 15:50:28 -0700 Subject: [PATCH 12/13] Link to ExternType docs from crate docs --- src/extern_type.rs | 2 -- src/lib.rs | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/extern_type.rs b/src/extern_type.rs index 6cc6e8e90..7923aa669 100644 --- a/src/extern_type.rs +++ b/src/extern_type.rs @@ -4,8 +4,6 @@ /// #\[cxx::bridge\] invocations, both for shared types defined in another bridge and external C++ /// definitions. This serves multiple related purposes. /// -/// TODO: These docs aren't discoverable. Add a link to here from the main crate docs. -/// ///
/// /// ## Safely unifying occurrences of the same extern C++ type diff --git a/src/lib.rs b/src/lib.rs index 02f0f451b..29ba5f056 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -346,6 +346,21 @@ //! tbdstd::shared_ptr<T> //! //! +//!
+//! +//! # Working with multiple bridge modules +//! +//! Multiple cxx::bridge modules can coexist in the same project. There are multiple reasons for +//! wanting to do so, such as for code organization reasons or to share a common library of FFI +//! types across many different bridges. Type aliases can be used inside the cxx::bridge to safely +//! unify the same opaque C++ type across multiple bridges or to reuse a shared type declared in +//! another bridge. +// +//! See the [documentation for the ExternType trait](trait.ExternType.html) for more information +//! and examples. +//! +//! +//! [ExternType trait]: trait.ExternType.html //! [https://github.com/dtolnay/cxx]: https://github.com/dtolnay/cxx #![no_std] From 6ecde4fb8177fe45db9d03fd6797af83834d929f Mon Sep 17 00:00:00 2001 From: Bryan Henry Date: Fri, 2 Oct 2020 16:04:17 -0700 Subject: [PATCH 13/13] Don't export whole extern_type module, just export kinds --- macro/src/expand.rs | 12 ++++++------ src/extern_type.rs | 21 ++++++++++----------- src/lib.rs | 4 ++-- tests/ui/alias_wrong_kind.stderr | 8 ++++---- 4 files changed, 22 insertions(+), 23 deletions(-) diff --git a/macro/src/expand.rs b/macro/src/expand.rs index d721aefd0..ff6394ba6 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -140,7 +140,7 @@ fn expand_struct(namespace: &Namespace, strct: &Struct) -> TokenStream { } unsafe impl ::cxx::ExternType for #ident { - type Kind = ::cxx::extern_type::KindShared; + type Kind = ::cxx::ExternTypeKindShared; type Id = #type_id; } } @@ -172,7 +172,7 @@ fn expand_enum(namespace: &Namespace, enm: &Enum) -> TokenStream { } unsafe impl ::cxx::ExternType for #ident { - type Kind = ::cxx::extern_type::KindShared; + type Kind = ::cxx::ExternTypeKindShared; type Id = #type_id; } } @@ -191,7 +191,7 @@ fn expand_cxx_type(namespace: &Namespace, ety: &ExternType) -> TokenStream { } unsafe impl ::cxx::ExternType for #ident { - type Kind = ::cxx::extern_type::KindOpaqueCpp; + type Kind = ::cxx::ExternTypeKindOpaqueCpp; type Id = #type_id; } } @@ -686,14 +686,14 @@ fn expand_type_alias_verify(namespace: &Namespace, alias: &TypeAlias) -> TokenSt let begin_span = alias.type_token.span; let end_span = alias.semi_token.span; let kind = match alias.kind { - AliasKind::Shared => quote!(KindShared), - AliasKind::OpaqueCpp => quote!(KindOpaqueCpp), + AliasKind::Shared => quote!(ExternTypeKindShared), + AliasKind::OpaqueCpp => quote!(ExternTypeKindOpaqueCpp), }; let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_type::<); let end = quote_spanned!(end_span=> >); quote! { - const _: fn() = #begin #ident, ::cxx::extern_type:: #kind, #type_id #end; + const _: fn() = #begin #ident, ::cxx:: #kind, #type_id #end; } } diff --git a/src/extern_type.rs b/src/extern_type.rs index 7923aa669..fe65abc6d 100644 --- a/src/extern_type.rs +++ b/src/extern_type.rs @@ -157,12 +157,9 @@ /// # pub struct StringPiece([usize; 2]); /// # } /// -/// use cxx::{type_id, ExternType}; -/// use cxx::extern_type; -/// -/// unsafe impl ExternType for folly_sys::StringPiece { -/// type Kind = extern_type::KindOpaqueCpp; -/// type Id = type_id!("folly::StringPiece"); +/// unsafe impl cxx::ExternType for folly_sys::StringPiece { +/// type Kind = cxx::ExternTypeKindOpaqueCpp; +/// type Id = cxx::type_id!("folly::StringPiece"); /// } /// /// #[cxx::bridge(namespace = folly)] @@ -186,8 +183,10 @@ pub unsafe trait ExternType { /// The type's kind. /// /// Must be either: - /// * `KindShared` for a shared type declared outside of an extern block in a cxx::bridge, or - /// * `KindOpqaueCpp` for an opaque C++ type declared inside of an `extern "C"` block. + /// * `ExternTypeKindShared` for a shared type declared outside of an extern block in a + /// cxx::bridge, or + /// * `ExternTypeKindOpqaueCpp` for an opaque C++ type declared inside of an `extern "C"` + /// block. /// /// Opaque Rust type aliases are unsupported because they can included with a use declaration /// and aliased more simply outside of the cxx::bridge. @@ -200,15 +199,15 @@ pub unsafe trait ExternType { /// ``` /// # struct TypeName; /// # unsafe impl cxx::ExternType for TypeName { - /// # type Kind = cxx::extern_type::KindOpaqueCpp; + /// # type Kind = cxx::ExternTypeKindOpaqueCpp; /// type Id = cxx::type_id!("name::space::of::TypeName"); /// # } /// ``` type Id; } -pub struct KindOpaqueCpp; -pub struct KindShared; +pub struct ExternTypeKindOpaqueCpp; +pub struct ExternTypeKindShared; #[doc(hidden)] pub fn verify_extern_type, Kind, Id>() {} diff --git a/src/lib.rs b/src/lib.rs index 29ba5f056..d80dd2c8b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -395,7 +395,7 @@ mod macros; mod cxx_string; mod cxx_vector; mod exception; -pub mod extern_type; +mod extern_type; mod function; mod opaque; mod result; @@ -410,7 +410,7 @@ mod unwind; pub use crate::cxx_string::CxxString; pub use crate::cxx_vector::CxxVector; pub use crate::exception::Exception; -pub use crate::extern_type::ExternType; +pub use crate::extern_type::{ExternType, ExternTypeKindOpaqueCpp, ExternTypeKindShared}; pub use crate::unique_ptr::UniquePtr; pub use cxxbridge_macro::bridge; diff --git a/tests/ui/alias_wrong_kind.stderr b/tests/ui/alias_wrong_kind.stderr index d8b223133..c25f45dbd 100644 --- a/tests/ui/alias_wrong_kind.stderr +++ b/tests/ui/alias_wrong_kind.stderr @@ -1,19 +1,19 @@ -error[E0271]: type mismatch resolving `::Kind == KindShared` +error[E0271]: type mismatch resolving `::Kind == ExternTypeKindShared` --> $DIR/alias_wrong_kind.rs:15:1 | 15 | #[cxx::bridge] - | ^^^^^^^^^^^^^^ expected struct `KindShared`, found struct `KindOpaqueCpp` + | ^^^^^^^^^^^^^^ expected struct `ExternTypeKindShared`, found struct `ExternTypeKindOpaqueCpp` | ::: $WORKSPACE/src/extern_type.rs | | pub fn verify_extern_type, Kind, Id>() {} | ----------- required by this bound in `verify_extern_type` -error[E0271]: type mismatch resolving `::Kind == KindOpaqueCpp` +error[E0271]: type mismatch resolving `::Kind == ExternTypeKindOpaqueCpp` --> $DIR/alias_wrong_kind.rs:15:1 | 15 | #[cxx::bridge] - | ^^^^^^^^^^^^^^ expected struct `KindOpaqueCpp`, found struct `KindShared` + | ^^^^^^^^^^^^^^ expected struct `ExternTypeKindOpaqueCpp`, found struct `ExternTypeKindShared` | ::: $WORKSPACE/src/extern_type.rs |