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 955ad5f50..ff6394ba6 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};
@@ -40,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) {
@@ -64,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));
}
}
@@ -119,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;
@@ -129,6 +130,7 @@ fn expand_struct(strct: &Struct) -> TokenStream {
let vis = Token![pub](field.ident.span());
quote!(#vis #field)
});
+ let type_id = type_id(namespace, ident);
quote! {
#doc
#[derive(#(#derives),*)]
@@ -136,10 +138,15 @@ fn expand_struct(strct: &Struct) -> TokenStream {
pub struct #ident {
#(#fields,)*
}
+
+ unsafe impl ::cxx::ExternType for #ident {
+ type Kind = ::cxx::ExternTypeKindShared;
+ 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;
@@ -150,6 +157,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)]
@@ -162,6 +170,11 @@ fn expand_enum(enm: &Enum) -> TokenStream {
impl #ident {
#(#variants)*
}
+
+ unsafe impl ::cxx::ExternType for #ident {
+ type Kind = ::cxx::ExternTypeKindShared;
+ type Id = #type_id;
+ }
}
}
@@ -178,6 +191,7 @@ fn expand_cxx_type(namespace: &Namespace, ety: &ExternType) -> TokenStream {
}
unsafe impl ::cxx::ExternType for #ident {
+ type Kind = ::cxx::ExternTypeKindOpaqueCpp;
type Id = #type_id;
}
}
@@ -657,9 +671,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;
}
}
@@ -669,11 +685,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!(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, #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 6701ef591..fe65abc6d 100644
--- a/src/extern_type.rs
+++ b/src/extern_type.rs
@@ -1,26 +1,24 @@
-/// 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, both for shared types defined in another bridge and external C++
+/// definitions. 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
@@ -51,14 +49,104 @@
///
///
///
+/// ## 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 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! {
@@ -69,10 +157,9 @@
/// # pub struct StringPiece([usize; 2]);
/// # }
///
-/// use cxx::{type_id, ExternType};
-///
-/// unsafe impl ExternType for folly_sys::StringPiece {
-/// 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)]
@@ -93,6 +180,18 @@
/// # fn main() {}
/// ```
pub unsafe trait ExternType {
+ /// The type's kind.
+ ///
+ /// Must be either:
+ /// * `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.
+ 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 +199,15 @@ pub unsafe trait ExternType {
/// ```
/// # struct TypeName;
/// # unsafe impl cxx::ExternType for TypeName {
+ /// # type Kind = cxx::ExternTypeKindOpaqueCpp;
/// type Id = cxx::type_id!("name::space::of::TypeName");
/// # }
/// ```
type Id;
}
+pub struct ExternTypeKindOpaqueCpp;
+pub struct ExternTypeKindShared;
+
#[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..d80dd2c8b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -346,6 +346,21 @@
//! tbd | std::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]
@@ -395,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/syntax/check.rs b/syntax/check.rs
index 147984e7e..0c4f59334 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");
}
@@ -226,6 +227,16 @@ 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)
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/mod.rs b/syntax/mod.rs
index f52a582c1..780efda65 100644
--- a/syntax/mod.rs
+++ b/syntax/mod.rs
@@ -78,7 +78,14 @@ 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,
pub eq_token: Token![=],
diff --git a/syntax/parse.rs b/syntax/parse.rs
index 3ac530471..a4b2cb1fd 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};
@@ -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,32 @@ 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() {
+ 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,35 +413,24 @@ 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()?;
- attrs::parse_doc(cx, &attrs);
-
- Ok(TypeAlias {
- 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 while still reusing syn's ItemType parsing.
+ let fork = input.fork();
+ fork.call(Attribute::parse_outer)?;
+ fork.parse::()
+ .map_err(|_| Error::new_spanned(tokens, "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))
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 47fc557cd..1e3e65880 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,11 +23,12 @@ cxx_library(
name = "impl",
srcs = [
"ffi/tests.cc",
+ ":alias/source",
":bridge/source",
- ":module/source",
],
header_namespace = "cxx-test-suite",
headers = {
+ "alias.rs.h": ":alias/header",
"lib.rs.h": ":bridge/header",
"tests.h": "ffi/tests.h",
},
@@ -40,6 +41,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..f06f332f3 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,13 +25,14 @@ cc_library(
name = "impl",
srcs = [
"ffi/tests.cc",
+ ":alias/source",
":bridge/source",
- ":module/source",
],
hdrs = ["ffi/tests.h"],
include_prefix = "cxx-test-suite",
strip_include_prefix = "ffi",
deps = [
+ ":alias/include",
":bridge/include",
"//:core",
],
@@ -46,8 +47,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/alias.rs b/tests/ffi/alias.rs
new file mode 100644
index 000000000..7c3b64d87
--- /dev/null
+++ b/tests/ffi/alias.rs
@@ -0,0 +1,56 @@
+// Rustfmt mangles the extern type alias.
+// https://github.com/rust-lang/rustfmt/issues/4159
+#[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(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);
+ 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 get3(self: &C) -> usize;
+ fn set3(self: &mut C, n: usize) -> usize;
+ }
+
+ extern "Rust" {
+ // 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;
+ 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/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..bc5976c8d 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};
@@ -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);
@@ -193,7 +197,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 +212,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 +262,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 +276,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 +339,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/module.rs b/tests/ffi/module.rs
deleted file mode 100644
index 8862dc161..000000000
--- a/tests/ffi/module.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-// 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!("cxx-test-suite/tests.h");
-
- type C = crate::ffi::C;
-
- fn c_take_unique_ptr(c: UniquePtr);
- }
-}
diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc
index 1f88d8b62..33dc84970 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
@@ -19,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;
@@ -29,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"); }
@@ -66,6 +74,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"));
}
@@ -160,12 +172,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();
@@ -357,15 +381,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,11 +400,19 @@ 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()));
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");
@@ -425,4 +458,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..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;
@@ -40,6 +42,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 +62,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);
@@ -101,4 +106,22 @@ 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_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_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 9c71bd5aa..ae20e90c5 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;
@@ -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());
@@ -65,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());
@@ -88,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!(cxx_test_suite::module::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()));
@@ -121,6 +153,28 @@ fn test_c_take() {
check!(ffi::c_take_enum(ffi::Enum::AVal));
}
+#[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_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));
+}
+
/*
// https://github.com/dtolnay/cxx/issues/232
#[test]
@@ -151,6 +205,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();
@@ -165,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_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_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);
+ | ^^^^^^^
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..c25f45dbd
--- /dev/null
+++ b/tests/ui/alias_wrong_kind.stderr
@@ -0,0 +1,21 @@
+error[E0271]: type mismatch resolving `::Kind == ExternTypeKindShared`
+ --> $DIR/alias_wrong_kind.rs:15:1
+ |
+15 | #[cxx::bridge]
+ | ^^^^^^^^^^^^^^ 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 == ExternTypeKindOpaqueCpp`
+ --> $DIR/alias_wrong_kind.rs:15:1
+ |
+15 | #[cxx::bridge]
+ | ^^^^^^^^^^^^^^ expected struct `ExternTypeKindOpaqueCpp`, found struct `ExternTypeKindShared`
+ |
+ ::: $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 68%
rename from tests/ui/wrong_type_id.stderr
rename to tests/ui/alias_wrong_type_id.stderr
index 2d8e50aac..38a93ea3a 100644
--- a/tests/ui/wrong_type_id.stderr
+++ b/tests/ui/alias_wrong_type_id.stderr
@@ -1,13 +1,13 @@
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
|
::: $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)`
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;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^