diff --git a/Cargo.toml b/Cargo.toml index 1455e557d..dd7c18fab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ cc = "1.0.49" cxx-build = { version = "=0.3.0", path = "gen/build" } cxx-test-suite = { version = "0", path = "tests/ffi" } rustversion = "1.0" -trybuild = { version = "1.0.21", features = ["diff"] } +trybuild = { version = "1.0.27", features = ["diff"] } [workspace] members = ["demo-rs", "gen/build", "gen/cmd", "macro", "tests/ffi"] diff --git a/gen/build/Cargo.toml b/gen/build/Cargo.toml index 1cbcced59..16aeba3d1 100644 --- a/gen/build/Cargo.toml +++ b/gen/build/Cargo.toml @@ -13,7 +13,7 @@ cc = "1.0.49" codespan-reporting = "0.9" proc-macro2 = { version = "1.0.12", features = ["span-locations"] } quote = "1.0" -syn = { version = "1.0", features = ["full"] } +syn = { version = "1.0.19", features = ["full"] } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/gen/cmd/Cargo.toml b/gen/cmd/Cargo.toml index 57f2e39c7..92adb89f2 100644 --- a/gen/cmd/Cargo.toml +++ b/gen/cmd/Cargo.toml @@ -17,7 +17,7 @@ codespan-reporting = "0.9" proc-macro2 = { version = "1.0.12", features = ["span-locations"] } quote = "1.0" structopt = "0.3" -syn = { version = "1.0", features = ["full"] } +syn = { version = "1.0.19", features = ["full"] } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/gen/src/write.rs b/gen/src/write.rs index f28a58e0b..8f54bd7c6 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -971,10 +971,6 @@ fn to_mangled(namespace: &Namespace, ty: &Type) -> String { } fn write_generic_instantiations(out: &mut OutFile, types: &Types) { - fn allow_unique_ptr(ident: &Ident) -> bool { - Atom::from(ident).is_none() - } - out.begin_block("extern \"C\""); for ty in types { if let Type::RustBox(ty) = ty { @@ -991,14 +987,14 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { } } else if let Type::UniquePtr(ptr) = ty { if let Type::Ident(inner) = &ptr.inner { - if allow_unique_ptr(inner) { + if Atom::from(inner).is_none() && !types.aliases.contains_key(inner) { out.next_section(); write_unique_ptr(out, inner, types); } } } else if let Type::CxxVector(ptr) = ty { if let Type::Ident(inner) = &ptr.inner { - if Atom::from(inner).is_none() { + if Atom::from(inner).is_none() && !types.aliases.contains_key(inner) { out.next_section(); write_cxx_vector(out, ty, inner, types); } diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 4c2d42370..8ade852f2 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -13,8 +13,8 @@ proc-macro = true [dependencies] proc-macro2 = "1.0" -quote = "1.0" -syn = { version = "1.0", features = ["full"] } +quote = "1.0.4" +syn = { version = "1.0.19", features = ["full"] } [dev-dependencies] cxx = { version = "0.3", path = ".." } diff --git a/macro/src/expand.rs b/macro/src/expand.rs index fcb845c5c..fc129c6d4 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -3,7 +3,7 @@ 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, Types, + self, check, mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, TypeAlias, Types, }; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned, ToTokens}; @@ -46,7 +46,7 @@ fn expand(namespace: &Namespace, ffi: ItemMod, apis: &[Api], types: &Types) -> T Api::Enum(enm) => expanded.extend(expand_enum(enm)), Api::CxxType(ety) => { if !types.enums.contains_key(&ety.ident) { - expanded.extend(expand_cxx_type(ety)); + expanded.extend(expand_cxx_type(namespace, ety)); } } Api::CxxFunction(efn) => { @@ -55,6 +55,10 @@ fn expand(namespace: &Namespace, ffi: ItemMod, apis: &[Api], types: &Types) -> T Api::RustFunction(efn) => { hidden.extend(expand_rust_function_shim(namespace, efn, types)) } + Api::TypeAlias(alias) => { + expanded.extend(expand_type_alias(alias)); + hidden.extend(expand_type_alias_verify(namespace, alias)); + } } } @@ -73,13 +77,13 @@ fn expand(namespace: &Namespace, ffi: ItemMod, apis: &[Api], types: &Types) -> T } } else if let Type::UniquePtr(ptr) = ty { if let Type::Ident(ident) = &ptr.inner { - if Atom::from(ident).is_none() { + if Atom::from(ident).is_none() && !types.aliases.contains_key(ident) { expanded.extend(expand_unique_ptr(namespace, ident, types)); } } } else if let Type::CxxVector(ptr) = ty { if let Type::Ident(ident) = &ptr.inner { - if Atom::from(ident).is_none() { + if Atom::from(ident).is_none() && !types.aliases.contains_key(ident) { // Generate impl for CxxVector if T is a struct or opaque // C++ type. Impl for primitives is already provided by cxx // crate. @@ -161,15 +165,21 @@ fn expand_enum(enm: &Enum) -> TokenStream { } } -fn expand_cxx_type(ety: &ExternType) -> TokenStream { +fn expand_cxx_type(namespace: &Namespace, ety: &ExternType) -> TokenStream { let ident = &ety.ident; let doc = &ety.doc; + let type_id = type_id(namespace, ident); + quote! { #doc #[repr(C)] pub struct #ident { _private: ::cxx::private::Opaque, } + + unsafe impl ::cxx::ExternType for #ident { + type Id = #type_id; + } } } @@ -554,6 +564,40 @@ fn expand_rust_function_shim_impl( } } +fn expand_type_alias(alias: &TypeAlias) -> TokenStream { + let ident = &alias.ident; + let ty = &alias.ty; + quote! { + pub type #ident = #ty; + } +} + +fn expand_type_alias_verify(namespace: &Namespace, alias: &TypeAlias) -> TokenStream { + let ident = &alias.ident; + let type_id = type_id(namespace, ident); + 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=> >); + + quote! { + const _: fn() = #begin #ident, #type_id #end; + } +} + +fn type_id(namespace: &Namespace, ident: &Ident) -> TokenStream { + let mut path = String::new(); + for name in namespace { + path += &name.to_string(); + path += "::"; + } + path += &ident.to_string(); + + quote! { + ::cxx::type_id!(#path) + } +} + fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream { let link_prefix = format!("cxxbridge03$box${}{}$", namespace, ident); let link_uninit = format!("{}uninit", link_prefix); diff --git a/macro/src/lib.rs b/macro/src/lib.rs index b56f58e46..bc8d19bbc 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -11,10 +11,11 @@ extern crate proc_macro; mod expand; mod syntax; +mod type_id; use crate::syntax::namespace::Namespace; use proc_macro::TokenStream; -use syn::{parse_macro_input, ItemMod}; +use syn::{parse_macro_input, ItemMod, LitStr}; /// `#[cxx::bridge] mod ffi { ... }` /// @@ -44,3 +45,9 @@ pub fn bridge(args: TokenStream, input: TokenStream) -> TokenStream { .unwrap_or_else(|err| err.to_compile_error()) .into() } + +#[proc_macro] +pub fn type_id(input: TokenStream) -> TokenStream { + let arg = parse_macro_input!(input as LitStr); + type_id::expand(arg).into() +} diff --git a/macro/src/type_id.rs b/macro/src/type_id.rs new file mode 100644 index 000000000..445da2ba7 --- /dev/null +++ b/macro/src/type_id.rs @@ -0,0 +1,29 @@ +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; +use syn::LitStr; + +// "folly::File" => `(f, o, l, l, y, (), F, i, l, e)` +pub fn expand(arg: LitStr) -> TokenStream { + let mut ids = Vec::new(); + + for word in arg.value().split("::") { + if !ids.is_empty() { + ids.push(quote!(())); + } + for ch in word.chars() { + ids.push(match ch { + 'A'..='Z' | 'a'..='z' => { + let t = format_ident!("{}", ch); + quote!(::cxx::#t) + } + '0'..='9' | '_' => { + let t = format_ident!("_{}", ch); + quote!(::cxx::#t) + } + _ => quote!([(); #ch as _]), + }); + } + } + + quote! { (#(#ids,)*) } +} diff --git a/src/extern_type.rs b/src/extern_type.rs new file mode 100644 index 000000000..6701ef591 --- /dev/null +++ b/src/extern_type.rs @@ -0,0 +1,110 @@ +/// A type for which the layout is determined by its C++ definition. +/// +/// This trait serves the following two 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 +/// `example::Demo` as expected in file2. +/// +/// ```no_run +/// // file1.rs +/// # mod file1 { +/// #[cxx::bridge(namespace = example)] +/// pub mod ffi { +/// extern "C" { +/// type Demo; +/// +/// fn create_demo() -> UniquePtr; +/// } +/// } +/// # } +/// +/// // file2.rs +/// #[cxx::bridge(namespace = example)] +/// pub mod ffi { +/// extern "C" { +/// type Demo = crate::file1::ffi::Demo; +/// +/// fn take_ref_demo(demo: &Demo); +/// } +/// } +/// # +/// # fn main() {} +/// ``` +/// +///

+/// +/// ## 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. +/// +/// 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! { +/// mod folly_sys; // the bindgen-generated bindings +/// # }; +/// # mod folly_sys { +/// # #[repr(transparent)] +/// # pub struct StringPiece([usize; 2]); +/// # } +/// +/// use cxx::{type_id, ExternType}; +/// +/// unsafe impl ExternType for folly_sys::StringPiece { +/// type Id = type_id!("folly::StringPiece"); +/// } +/// +/// #[cxx::bridge(namespace = folly)] +/// pub mod ffi { +/// extern "C" { +/// include!("rust_cxx_bindings.h"); +/// +/// type StringPiece = crate::folly_sys::StringPiece; +/// +/// fn print_string_piece(s: &StringPiece); +/// } +/// } +/// +/// // Now if we construct a StringPiece or obtain one through one +/// // of the bindgen-generated signatures, we are able to pass it +/// // along to ffi::print_string_piece. +/// # +/// # fn main() {} +/// ``` +pub unsafe trait ExternType { + /// 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: + /// + /// ``` + /// # struct TypeName; + /// # unsafe impl cxx::ExternType for TypeName { + /// type Id = cxx::type_id!("name::space::of::TypeName"); + /// # } + /// ``` + type Id; +} + +#[doc(hidden)] +pub fn verify_extern_type, Id>() {} diff --git a/src/lib.rs b/src/lib.rs index cc0dcf223..fc0e6828c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -345,6 +345,7 @@ #![doc(html_root_url = "https://docs.rs/cxx/0.3.0")] #![deny(improper_ctypes)] +#![allow(non_camel_case_types)] #![allow( clippy::cognitive_complexity, clippy::declare_interior_mutable_const, @@ -369,6 +370,7 @@ mod macros; mod cxx_string; mod cxx_vector; mod exception; +mod extern_type; mod function; mod opaque; mod result; @@ -385,13 +387,18 @@ mod symbols; 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::unique_ptr::UniquePtr; -pub use cxxbridge_macro::bridge; +pub use cxxbridge_macro::{bridge}; + +/// For use in impls of the `ExternType` trait. See [`ExternType`]. +pub use cxxbridge_macro::type_id; // Not public API. #[doc(hidden)] pub mod private { pub use crate::cxx_vector::VectorElement; + pub use crate::extern_type::verify_extern_type; pub use crate::function::FatFunction; pub use crate::opaque::Opaque; pub use crate::result::{r#try, Result}; @@ -402,3 +409,19 @@ pub mod private { pub use crate::unique_ptr::UniquePtrTarget; pub use crate::unwind::catch_unwind; } + +macro_rules! chars { + ($($ch:ident)*) => { + $( + #[doc(hidden)] + pub enum $ch {} + )* + }; +} + +chars! { + _0 _1 _2 _3 _4 _5 _6 _7 _8 _9 + A B C D E F G H I J K L M N O P Q R S T U V W X Y Z + a b c d e f g h i j k l m n o p q r s t u v w x y z + __ // underscore +} diff --git a/syntax/ident.rs b/syntax/ident.rs index cec424c01..7545e92ce 100644 --- a/syntax/ident.rs +++ b/syntax/ident.rs @@ -42,6 +42,9 @@ pub(crate) fn check_all(cx: &mut Check, namespace: &Namespace, apis: &[Api]) { check(cx, &arg.ident); } } + Api::TypeAlias(alias) => { + check(cx, &alias.ident); + } } } } diff --git a/syntax/mod.rs b/syntax/mod.rs index fd8db73d0..3eb6c6e6f 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -20,7 +20,7 @@ use self::parse::kw; use proc_macro2::{Ident, Span}; use syn::punctuated::Punctuated; use syn::token::{Brace, Bracket, Paren}; -use syn::{Lifetime, LitStr, Token}; +use syn::{Lifetime, LitStr, Token, Type as RustType}; pub use self::atom::Atom; pub use self::doc::Doc; @@ -35,6 +35,7 @@ pub enum Api { CxxFunction(ExternFn), RustType(ExternType), RustFunction(ExternFn), + TypeAlias(TypeAlias), } pub struct ExternType { @@ -68,6 +69,14 @@ pub struct ExternFn { pub semi_token: Token![;], } +pub struct TypeAlias { + pub type_token: Token![type], + pub ident: Ident, + pub eq_token: Token![=], + pub ty: RustType, + pub semi_token: Token![;], +} + pub struct Signature { pub fn_token: Token![fn], pub receiver: Option, diff --git a/syntax/parse.rs b/syntax/parse.rs index b794ba925..0640e7f8a 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -2,17 +2,19 @@ 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, Var, Variant, + Struct, Ty1, Type, TypeAlias, Var, Variant, }; +use proc_macro2::TokenStream; use quote::{format_ident, quote}; use std::collections::HashSet; use std::u32; +use syn::parse::{ParseStream, Parser}; use syn::punctuated::Punctuated; use syn::{ - Abi, Error, Expr, ExprLit, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, - GenericArgument, Ident, Item, ItemEnum, ItemForeignMod, ItemStruct, Lit, Pat, PathArguments, - Result, ReturnType, Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice, - Variant as RustVariant, + Abi, Attribute, Error, Expr, ExprLit, Fields, FnArg, ForeignItem, ForeignItemFn, + ForeignItemType, GenericArgument, Ident, Item, ItemEnum, ItemForeignMod, ItemStruct, Lit, Pat, + PathArguments, Result, ReturnType, Token, Type as RustType, TypeBareFn, TypePath, + TypeReference, TypeSlice, Variant as RustVariant, }; pub mod kw { @@ -182,16 +184,21 @@ fn parse_foreign_mod(cx: &mut Errors, foreign_mod: ItemForeignMod, out: &mut Vec Err(err) => cx.push(err), } } + ForeignItem::Verbatim(tokens) => match parse_extern_verbatim(tokens, lang) { + Ok(api) => items.push(api), + Err(err) => cx.push(err), + }, _ => cx.error(foreign, "unsupported foreign item"), } } let mut types = items.iter().filter_map(|item| match item { - Api::CxxType(ty) | Api::RustType(ty) => Some(ty), + Api::CxxType(ty) | Api::RustType(ty) => Some(&ty.ident), + Api::TypeAlias(alias) => Some(&alias.ident), _ => None, }); if let (Some(single_type), None) = (types.next(), types.next()) { - let single_type = single_type.ident.clone(); + let single_type = single_type.clone(); for item in &mut items { if let Api::CxxFunction(efn) | Api::RustFunction(efn) = item { if let Some(receiver) = &mut efn.receiver { @@ -336,6 +343,44 @@ fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result { })) } +fn parse_extern_verbatim(tokens: &TokenStream, lang: Lang) -> Result { + // type Alias = crate::path::to::Type; + fn 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()?; + attrs::parse_doc(&attrs)?; + + Ok(TypeAlias { + type_token, + ident, + eq_token, + ty, + semi_token, + }) + } + + let type_alias = parse.parse2(tokens.clone())?; + match lang { + Lang::Cxx => Ok(Api::TypeAlias(type_alias)), + Lang::Rust => { + let (type_token, semi_token) = (type_alias.type_token, type_alias.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)) + } + } +} + fn parse_type(ty: &RustType) -> Result { match ty { RustType::Reference(ty) => parse_type_reference(ty), diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 934c85bfe..6dd60738d 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -1,6 +1,7 @@ use crate::syntax::atom::Atom::*; use crate::syntax::{ - Derive, Enum, ExternFn, ExternType, Receiver, Ref, Signature, Slice, Struct, Ty1, Type, Var, + Derive, Enum, ExternFn, ExternType, Receiver, Ref, Signature, Slice, Struct, Ty1, Type, + TypeAlias, Var, }; use proc_macro2::{Ident, Span, TokenStream}; use quote::{quote_spanned, ToTokens}; @@ -86,6 +87,14 @@ impl ToTokens for ExternType { } } +impl ToTokens for TypeAlias { + fn to_tokens(&self, tokens: &mut TokenStream) { + // Notional token range for error reporting purposes. + self.type_token.to_tokens(tokens); + self.ident.to_tokens(tokens); + } +} + impl ToTokens for Struct { fn to_tokens(&self, tokens: &mut TokenStream) { // Notional token range for error reporting purposes. diff --git a/syntax/types.rs b/syntax/types.rs index 580051393..ba9bc7890 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, Struct, Type}; +use crate::syntax::{Api, Derive, Enum, Struct, Type, TypeAlias}; use proc_macro2::Ident; use quote::ToTokens; use std::collections::{BTreeMap as Map, HashSet as UnorderedSet}; @@ -12,6 +12,7 @@ pub struct Types<'a> { pub enums: Map, pub cxx: Set<'a, Ident>, pub rust: Set<'a, Ident>, + pub aliases: Map, } impl<'a> Types<'a> { @@ -21,6 +22,7 @@ impl<'a> Types<'a> { let mut enums = Map::new(); let mut cxx = Set::new(); let mut rust = Set::new(); + let mut aliases = Map::new(); fn visit<'a>(all: &mut Set<'a, Type>, ty: &'a Type) { all.insert(ty); @@ -96,6 +98,14 @@ impl<'a> Types<'a> { visit(&mut all, ret); } } + Api::TypeAlias(alias) => { + let ident = &alias.ident; + if !type_names.insert(ident) { + duplicate_name(cx, alias, ident); + } + cxx.insert(ident); + aliases.insert(ident.clone(), alias); + } } } @@ -105,6 +115,7 @@ impl<'a> Types<'a> { enums, cxx, rust, + aliases, } } diff --git a/tests/BUCK b/tests/BUCK index 659223ca6..41781c844 100644 --- a/tests/BUCK +++ b/tests/BUCK @@ -6,7 +6,10 @@ rust_test( rust_library( name = "ffi", - srcs = ["ffi/lib.rs"], + srcs = [ + "ffi/lib.rs", + "ffi/module.rs", + ], crate = "cxx_test_suite", deps = [ ":impl", @@ -18,25 +21,33 @@ cxx_library( name = "impl", srcs = [ "ffi/tests.cc", - ":gen-source", + ":gen-lib-source", + ":gen-module-source", ], headers = { - "ffi/lib.rs.h": ":gen-header", + "ffi/lib.rs.h": ":gen-lib-header", "ffi/tests.h": "ffi/tests.h", }, deps = ["//:core"], ) genrule( - name = "gen-header", + name = "gen-lib-header", srcs = ["ffi/lib.rs"], cmd = "$(exe //:codegen) --header ${SRCS} > ${OUT}", - out = "generated.h", + out = "lib.rs.h", ) genrule( - name = "gen-source", + name = "gen-lib-source", srcs = ["ffi/lib.rs"], cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", - out = "generated.cc", + out = "lib.rs.cc", +) + +genrule( + name = "gen-module-source", + srcs = ["ffi/module.rs"], + cmd = "$(exe //:codegen) ${SRCS} > ${OUT}", + out = "module.rs.cc", ) diff --git a/tests/BUILD b/tests/BUILD index 65c5b4153..e1f163718 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -8,7 +8,10 @@ rust_test( rust_library( name = "cxx_test_suite", - srcs = ["ffi/lib.rs"], + srcs = [ + "ffi/lib.rs", + "ffi/module.rs", + ], deps = [ ":impl", "//:cxx", @@ -19,17 +22,18 @@ cc_library( name = "impl", srcs = [ "ffi/tests.cc", - ":gen-source", + ":gen-lib-source", + ":gen-module-source", ], hdrs = ["ffi/tests.h"], deps = [ - ":include", + ":lib-include", "//:core", ], ) genrule( - name = "gen-header", + name = "gen-lib-header", srcs = ["ffi/lib.rs"], outs = ["lib.rs.h"], cmd = "$(location //:codegen) --header $< > $@", @@ -37,15 +41,23 @@ genrule( ) genrule( - name = "gen-source", + name = "gen-lib-source", srcs = ["ffi/lib.rs"], - outs = ["generated.cc"], + outs = ["lib.rs.cc"], cmd = "$(location //:codegen) $< > $@", tools = ["//:codegen"], ) cc_library( - name = "include", - hdrs = [":gen-header"], + name = "lib-include", + hdrs = [":gen-lib-header"], include_prefix = "tests/ffi", ) + +genrule( + name = "gen-module-source", + srcs = ["ffi/module.rs"], + outs = ["module.rs.cc"], + cmd = "$(location //:codegen) $< > $@", + tools = ["//:codegen"], +) diff --git a/tests/ffi/build.rs b/tests/ffi/build.rs index b970362ae..2c96d7325 100644 --- a/tests/ffi/build.rs +++ b/tests/ffi/build.rs @@ -3,7 +3,8 @@ fn main() { return; } - cxx_build::bridge("lib.rs") + let sources = vec!["lib.rs", "module.rs"]; + cxx_build::bridges(sources) .file("tests.cc") .flag("-std=c++11") .compile("cxx-test-suite"); diff --git a/tests/ffi/lib.rs b/tests/ffi/lib.rs index e298e881c..94572299d 100644 --- a/tests/ffi/lib.rs +++ b/tests/ffi/lib.rs @@ -4,6 +4,8 @@ clippy::trivially_copy_pass_by_ref )] +pub mod module; + use cxx::{CxxString, UniquePtr}; use std::fmt::{self, Display}; @@ -47,7 +49,6 @@ pub mod ffi { fn c_take_primitive(n: usize); fn c_take_shared(shared: Shared); fn c_take_box(r: Box); - fn c_take_unique_ptr(c: 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/module.rs b/tests/ffi/module.rs new file mode 100644 index 000000000..77bae0664 --- /dev/null +++ b/tests/ffi/module.rs @@ -0,0 +1,13 @@ +// Rustfmt mangles the extern type alias. +// https://github.com/rust-lang/rustfmt/issues/4159 +#[rustfmt::skip] +#[cxx::bridge(namespace = tests)] +pub mod ffi { + extern "C" { + include!("tests/ffi/tests.h"); + + type C = crate::ffi::C; + + fn c_take_unique_ptr(c: UniquePtr); + } +} diff --git a/tests/test.rs b/tests/test.rs index b8593c587..ddf8f70a6 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -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!(ffi::c_take_unique_ptr(unique_ptr)); + check!(cxx_test_suite::module::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())); diff --git a/tests/ui/type_alias_rust.rs b/tests/ui/type_alias_rust.rs new file mode 100644 index 000000000..67df48926 --- /dev/null +++ b/tests/ui/type_alias_rust.rs @@ -0,0 +1,9 @@ +#[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 new file mode 100644 index 000000000..1b08f67cc --- /dev/null +++ b/tests/ui/type_alias_rust.stderr @@ -0,0 +1,5 @@ +error: type alias in extern "Rust" block is not supported + --> $DIR/type_alias_rust.rs:5:9 + | +5 | type Alias = crate::Type; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/wrong_type_id.rs b/tests/ui/wrong_type_id.rs new file mode 100644 index 000000000..81a9b3f87 --- /dev/null +++ b/tests/ui/wrong_type_id.rs @@ -0,0 +1,15 @@ +#[cxx::bridge(namespace = folly)] +mod here { + extern "C" { + type StringPiece; + } +} + +#[cxx::bridge(namespace = folly)] +mod there { + extern "C" { + type ByteRange = crate::here::StringPiece; + } +} + +fn main() {} diff --git a/tests/ui/wrong_type_id.stderr b/tests/ui/wrong_type_id.stderr new file mode 100644 index 000000000..5b4f6c660 --- /dev/null +++ b/tests/ui/wrong_type_id.stderr @@ -0,0 +1,13 @@ +error[E0271]: type mismatch resolving `::Id == (cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::B, cxx::y, cxx::t, cxx::e, cxx::R, cxx::a, cxx::n, cxx::g, cxx::e)` + --> $DIR/wrong_type_id.rs:11:9 + | +11 | type ByteRange = crate::here::StringPiece; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected a tuple with 15 elements, found one with 17 elements + | + ::: $WORKSPACE/src/extern_type.rs:110:41 + | +110 | pub fn verify_extern_type, Id>() {} + | ------- required by this bound in `cxx::extern_type::verify_extern_type` + | + = note: expected tuple `(cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::B, cxx::y, cxx::t, cxx::e, cxx::R, cxx::a, cxx::n, cxx::g, cxx::e)` + found tuple `(cxx::f, cxx::o, cxx::l, cxx::l, cxx::y, (), cxx::S, cxx::t, cxx::r, cxx::i, cxx::n, cxx::g, cxx::P, cxx::i, cxx::e, cxx::c, cxx::e)` diff --git a/third-party/BUCK b/third-party/BUCK index 2de784b2d..e16bad297 100644 --- a/third-party/BUCK +++ b/third-party/BUCK @@ -131,7 +131,7 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.18/src/**"]), + srcs = glob(["vendor/syn-1.0.19/src/**"]), visibility = ["PUBLIC"], features = [ "clone-impls", diff --git a/third-party/BUILD b/third-party/BUILD index 4d8fd9f95..45b1163c4 100644 --- a/third-party/BUILD +++ b/third-party/BUILD @@ -136,7 +136,7 @@ rust_library( rust_library( name = "syn", - srcs = glob(["vendor/syn-1.0.18/src/**"]), + srcs = glob(["vendor/syn-1.0.19/src/**"]), crate_features = [ "clone-impls", "derive", diff --git a/third-party/Cargo.lock b/third-party/Cargo.lock index 3397192f5..b1227f977 100644 --- a/third-party/Cargo.lock +++ b/third-party/Cargo.lock @@ -307,9 +307,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "410a7488c0a728c7ceb4ad59b9567eb4053d02e8cc7f5c0e0eeeb39518369213" +checksum = "e8e5aa70697bb26ee62214ae3288465ecec0000f05182f039b477001f08f5ae7" dependencies = [ "proc-macro2", "quote", @@ -356,9 +356,9 @@ dependencies = [ [[package]] name = "trybuild" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e5696e4fd793743fbcc29943fe965ea3993b6c3d2a6a3a35c6680d926fd3a49" +checksum = "744665442556a91933cee5e75b0371376eb03498c4d0bfbcebd2a9882b4fb5ef" dependencies = [ "dissimilar", "glob",