| // Part of the Crubit project, under the Apache License v2.0 with LLVM |
| // Exceptions. See /LICENSE for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| #![feature(negative_impls, vec_into_raw_parts, extern_types)] |
| //! This crate provides facilities for safely casting across multiple Rust types |
| //! that represent the same C++ type. This is needed because: |
| //! |
| //! - In C++ one can interchangably use references to a forward-declared |
| //! `struct` and references to the complete definition of the `struct`, but |
| //! Rust bindings represent them using separate types (see `Incomplete<Name, |
| //! Declarer>`). |
| //! |
| //! - In C++ one can interchangably use multiple independent C++ class template |
| //! instantiations, but Rust bindings may represent them using separate types. |
| //! This happens when the same class template is instantiated with the same |
| //! template arguments in 2 different Crubit-generated crates with C++ |
| //! bindings. |
| //! |
| //! ## Forward declarations |
| //! |
| //! Given this C++: |
| //! |
| //! ```c++ |
| //! namespace foo { |
| //! class Foo; |
| //! } // namespace foo |
| //! |
| //! void MyFunction(foo::Foo& foo) { |
| //! // can pass Foo around, but cannot access members, copy it, delete it, etc. |
| //! some_other_library::OtherFunction(foo); |
| //! } |
| //! ``` |
| //! |
| //! We can translate it to this equivalent Rust: |
| //! |
| //! ``` |
| //! use forward_declare::{forward_declare, CcCast}; |
| //! |
| //! forward_declare!(pub Foo = symbol!("foo::Foo")); |
| //! fn MyFunction(foo: &mut Foo) { |
| //! some_other_library::OtherFunction(foo.cc_cast()); |
| //! } |
| //! ``` |
| //! |
| //! On the reverse end, to **define** a type which may be forward-declared in |
| //! other crates, use the `unsafe_define!` macro: |
| //! |
| //! ``` |
| //! pub struct Foo { ... } |
| //! unsafe_define!(symbol!("foo::Foo"), Foo); |
| //! ``` |
| //! |
| //! The C++ forward declaration of a type `class Foo;` declares the name `Foo` |
| //! to be a type, but does not provide details such size and contents; such type |
| //! declaration is said to be incomplete. This is used in C++ to speed up build |
| //! performance (by omitting the real definition from the dependencies) and to |
| //! break up dependency cycles (by having one or both sides of the cycle |
| //! forward-declare pieces from the other side). |
| //! |
| //! In the general case, forward declarations in existing C++ code cannot be |
| //! removed without substantial changes. For example, a library can |
| //! forward-declare a class that is only defined in the binary. So a forward |
| //! declaration might not even have a single defining type, but one for |
| //! every binary that uses the library! |
| //! |
| //! Rust has close-enough equivalents to forward-declaration for constants and |
| //! functions, but not for types. This crate can be used wherever forward |
| //! declarations are used in C++, for those cases where forward declarations |
| //! cannot be avoided. |
| //! |
| //! ## Forward Declaring |
| //! |
| //! To forward declare a type which supports forward declarations, use the |
| //! `forward_declare` macro. |
| //! |
| //! ``` |
| //! forward_declare!(pub YourType = symbol!("foo::YourType")); |
| //! ``` |
| //! |
| //! This creates a public alias `pub YourType = |
| //! Incomplete<symbol!("foo::YourType"), ???>;`, where `???` is some type unique |
| //! to this scope. In particular, this means that different calls |
| //! to `forward_declare` produce different (but otherwise interchangeable) |
| //! types. |
| //! |
| //! Most user code won't need to directly use the `forward_declare!` macro and |
| //! can instead rely on forward declarations provided in the `..._rs_api` crates |
| //! generated by `rs_bindings_from_cc`. |
| //! |
| //! ## Providing a full definition |
| //! |
| //! For each type you wish to allow to be forward declared by others, give it a |
| //! globally unique name such as `"foo::YourType"`, and call |
| //! `unsafe_define!(symbol!("foo::YourType"), YourType)`. |
| //! |
| //! Most user code won't need to directly use the `unsafe_define!` macro and can |
| //! instead rely on definitions provided in the `..._rs_api` crates generated by |
| //! `rs_bindings_from_cc`. |
| //! |
| //! ## Supported casts |
| //! |
| //! Casting is supported between |
| //! |
| //! * Two forward-declarations of the same C++ type, |
| //! * A forward-declaration and a complete definition of the same C++ type, |
| //! * Two complete definitions of the same C++ type (e.g. two identical class |
| //! template instantiations). |
| //! |
| //! ### Pointers and references |
| //! |
| //! Casting is also supported for all of the following common pointer types |
| //! (e.g. a references to a forward-declared type can be cast to a reference to |
| //! a fully-defined type): |
| //! |
| //! * `& _` |
| //! * `&mut _` |
| //! * `Pin<& _>` |
| //! * `Pin<&mut _>` |
| //! * `* const _` |
| //! * `* mut _` |
| //! |
| //! `Box`, `Rc`, and `Arc` are excluded, because they require knowledge of the |
| //! `Drop` impl, and incomplete (e.g. forward-declared) types do not know how to |
| //! drop. |
| //! |
| //! Additionally, references can be cast to/from a corresponding `Pin`: |
| //! |
| //! * `& T` <-> `Pin<&T>` |
| //! * `&mut T` <-> `Pin<&mut T>` (only if `T: Unpin`). |
| //! |
| //! ### Containers |
| //! |
| //! If container's element type supports casting, then casting is also supported |
| //! for the following container types (e.g. a slice of references to a |
| //! forward-declared type can be cast to a slice of references to a |
| //! fully-defined type): |
| //! |
| //! * Slices - `&[T]` |
| //! * Arrays - `[T; N]` |
| //! * `Vec<T>` |
| //! * TODO: Add support for more containers as needed (HashSet? bindings for |
| //! std::vector?) |
| //! |
| //! ## Passing around values |
| //! |
| //! Compound data types implement the `CcCast` trait, which allows for |
| //! converting between complete and incomplete types. To convert from complete |
| //! to incomplete, incomplete to complete, or incomplete to incomplete when |
| //! crossing crate boundaries, use `.cc_cast()` to perform the conversion. |
| //! |
| //! ``` |
| //! forward_declare!(pub Foo = symbol!("foo::Foo")); |
| //! |
| //! fn takes_incomplete(foo: &mut Foo) { |
| //! some_other_library::other_function(foo.cc_cast()); |
| //! } |
| //! |
| //! fn takes_complete(foo: &mut RealFoo) { |
| //! takes_incomplete(foo.cc_cast()); |
| //! } |
| //! ``` |
| //! |
| //! ## Crossing Crate Boundaries |
| //! |
| //! **Warning:** Users of incomplete types from other crates should not assume |
| //! that currently-incomplete types stay incomplete forever; code owners should |
| //! feel free to replace an incomplete type with a complete type. |
| //! |
| //! In Rust, unlike C++, the incomplete type and incomplete type are different |
| //! types. It is in theory a backwards-incompatible change to change from |
| //! incomplete to complete. Callers must try as much as possible to ease that |
| //! transition, since forward declarations are expected to be removed over time. |
| //! |
| //! This is partially enforced by the type system: every forward declaration of |
| //! a type is unique, and every crate should have its own forward declaration, |
| //! not reusing the forward declaration of another crate. As a result, you must |
| //! call `cc_cast()` when crossing crate boundaries, and failure to do |
| //! so is an error: |
| //! |
| //! |
| //! ```compile_fail |
| //! # mod other_crate { |
| //! # forward_declare!(pub ForwardDeclared = symbol!("foo::Type")); |
| //! # pub fn function(x: &ForwardDeclared) {panic!()} |
| //! # } |
| //! |
| //! forward_declare!(pub ForwardDeclared = symbol!("foo::Type")); |
| //! let x: &ForwardDeclared = unimplemented!(); |
| //! other_crate::function(x); // ERROR |
| //! ``` |
| //! |
| //! If that example were valid, then removing a forward declaration and |
| //! replacing it with the real type would break the caller, requiring a change |
| //! to call `cc_cast()`. But since we always require the caller to call` |
| //! cc_cast()` *anyway*, even if it already has an incomplete |
| //! type, `other_crate::function` is free to remove the forward declaration |
| //! without breaking this caller. |
| //! |
| //! ```no_run |
| //! # mod other_crate { |
| //! # forward_declare!(pub ForwardDeclared = symbol!("foo::Type")); |
| //! # pub fn function(x: &ForwardDeclared) {panic!()} |
| //! # } |
| //! # |
| //! # forward_declare!(pub ForwardDeclared = symbol!("foo::Type")); |
| //! # let x: &ForwardDeclared = unimplemented!(); |
| //! other_crate::function(x.cc_cast()); // Works whether `function` takes incomplete or complete type. |
| //! ``` |
| |
| pub use forward_declare_proc_macros::*; |
| |
| /// Public to be exposed to macros, but otherwise for internal use only. |
| pub mod internal { |
| /// `Symbol` type, equivalent to const &'static str values, but usable in |
| /// generics in stable Rust. |
| /// |
| /// Every symbol is a tuple of `C`: for example, `symbol!("xy")` is |
| /// `Symbol((C<'x'>, C<'y'>))` |
| #[repr(C)] |
| pub struct Symbol<T>(std::marker::PhantomData<T>); |
| |
| /// A character in a symbol string. |
| #[repr(C)] |
| pub struct C<const CHAR: char>; |
| |
| /// Types that implement the `CcType` trait with the same `Name` can be |
| /// safely transmuted between each other, because they either provide |
| /// bindings for the same C++ type, or point/refer to the same C++ type, |
| /// or contain the same C++ type. |
| /// |
| /// Even though this trait is public, implementations of this trait should |
| /// only be provided by Crubit itself: |
| /// |
| /// - Via `forward_declare!` and `unsafe_define!` macros |
| /// - Via blanket `impl`s provided for references, pointers (e.g. see `mod |
| /// ref_transmutability` below). |
| /// |
| /// # Safety |
| /// |
| /// Two types implement `CcType` with the same name if and only if they can |
| /// be transmuted to one another. |
| pub unsafe trait CcType { |
| /// `Name` helps Rust type system to identify the given transmutability |
| /// equivalence class. |
| /// |
| /// `forward_declare!` and `unsafe_define!` macros form the `Name` using |
| /// the `symbol!` macro, based on the fully-qualified name of |
| /// the imported C++ type. |
| /// |
| /// In other scenarios, the `Name` is formed using an arbitrary |
| /// convention that is sufficient to guarantee non-overlapping |
| /// names (e.g. see the private `ref_transmutability::RefName` |
| /// type alias below). |
| type Name; |
| } |
| } |
| |
| use internal::*; |
| |
| extern "C" { |
| /// Adding an `Unsized` field to your type makes it completely unsized |
| /// (not just dynamically-sized). |
| type Unsized; |
| } |
| |
| /// A struct representing an "Incomplete Type" for `Name` |
| /// |
| /// For any given name, there may be multiple "incomplete types" for that name |
| /// -- for example, `Incomplete<symbol!("T"), crate_1::T1>` is a different type |
| /// from `Incomplete<symbol!("T"), T2>`. This is intentional: in public |
| /// interfaces, an incomplete type is allowed to be changed to the complete type |
| /// without breaking callers. |
| /// |
| /// TODO(jeanpierreda): make rust think this is ffi-safe. (PhantomData...) |
| #[repr(C)] |
| pub struct Incomplete<Name, Declarer>(std::marker::PhantomData<(Name, Declarer)>, Unsized); |
| |
| // Ensure this is not `Unpin`, `Send`, or `Sync`. This is discussed somewhat in: |
| // https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs |
| |
| /// Incomplete types can't be copied by value, because we don't know their true |
| /// size. |
| impl<Name, Declarer> !Unpin for Incomplete<Name, Declarer> {} |
| /// Similarly, we don't know if they are Send or Sync. |
| impl<Name, Declarer> !Send for Incomplete<Name, Declarer> {} |
| impl<Name, Declarer> !Sync for Incomplete<Name, Declarer> {} |
| |
| /// Marker trait for complete types. |
| /// |
| /// This is automatically implemented for anything `!Unpin`, but all other types |
| /// need to implement it by hand. Sorry, Rust just doesn't allow the kind of |
| /// negative reasoning we'd need to make this better. |
| /// |
| /// In particular, this is *not* implemented for `Incomplete<...>`. |
| pub trait Complete {} |
| |
| /// We know this to be true because `Incomplete<...>` is `!Unpin`. |
| /// |
| /// Ideally we'd get a better blanket impl, but this very nearly covers |
| /// everything. Most `Unpin` types are going to be wrappers around C++ types, |
| /// which can auto-generate a `Complete` impl, and the rest are unlikely to |
| /// matter. |
| impl<T: Unpin> Complete for T {} |
| |
| /// All forward declarations represented by `Incomplete<Name, ...>` form a |
| /// transmutability equivalence class (together with the complete definition(s) |
| /// - see the `unsafe_define!` macro below). |
| /// |
| /// # Safety notes |
| /// |
| /// We alias references to arbitrary `T` using references to `Incomplete`. This |
| /// is OK, because of the design of `Incomplete`: |
| /// |
| /// - layout: `Incomplete` has no fields of size > 0, so it does not alias `T` |
| /// incompatibly. |
| /// |
| /// - provenance: while in general it is not valid to access memory |
| /// **neighboring** that of a type (e.g. one can't use &vec[0] to access |
| /// vec[1]), in this case, we are using `feature(extern_types)`, which is a |
| /// DST which must grant access to the following memory or else it would be |
| /// useless. (This type of access is the reason the feature exists). |
| unsafe impl<Name, Declarer> CcType for Incomplete<Name, Declarer> { |
| type Name = Name; |
| } |
| |
| /// `unsafe_define!(symbol!("..."), T)` claims that `T` is the unique definition |
| /// of the type identified by that symbol, and implements the conversions for |
| /// pointers to and from the corresponding `Incomplete` pointers. |
| /// |
| /// Safety: if more than one type claim to be the definition, then they may be |
| /// transmuted into each other in safe code. The safety requirements for |
| /// `std::mem::transmute` apply. |
| // Each `rustfmt` invocation would indent the `type Name...` line further and further to the right. |
| #[rustfmt::skip] |
| #[macro_export] |
| macro_rules! unsafe_define { |
| // TODO(jeanpierreda): support generic complete type (e.g. `symbol!("xyz") <-> Foo<T> where T : Bar`) |
| ($Name:ty, $Complete:ty) => { |
| unsafe impl $crate::internal::CcType for $Complete { |
| type Name = $Name; |
| } |
| }; |
| } |
| |
| /// If `T` can be transmuted into `U`, then one can also transmute between |
| /// `*const T`, and `* const U`. (This are always "thin" pointers - with the |
| /// same size as `usize`.) |
| unsafe impl<T: ?Sized> CcType for *const T |
| where |
| T: CcType, |
| { |
| type Name = (*const (), T::Name); |
| } |
| |
| /// If `T` can be transmuted into `U`, then one can also transmute between `*mut |
| /// T`, and `* mut U`. (This are always "thin" pointers - with the same size as |
| /// `usize`.) |
| unsafe impl<T: ?Sized> CcType for *mut T |
| where |
| T: CcType, |
| { |
| type Name = (*mut (), T::Name); |
| } |
| |
| /// If `T` can be transmuted into `U`, then one can also transmute between |
| /// arrays `[T; N]` and `[U; N]`. |
| /// |
| /// # Safety notes |
| /// |
| /// If `T` and `U` are transmutable then they should have the same alignment, |
| /// size, and stride. Based on "Unsafe Code Guidelines Reference" [1], this |
| /// means that the arrays `[T; N]` and `[U, N]` have the same memory layout. |
| /// |
| /// Note that `T` below has an implicit `T: Sized` constraint. This means that |
| /// it is *not* possible to transmute into or out-of an array of |
| /// forward-declared types. OTOH, it *is* possible to transmute between arrays |
| /// of transmutable, completely-defined/sized types (e.g. between arrays of |
| /// template instantiations). |
| /// |
| /// [1] |
| /// https://rust-lang.github.io/unsafe-code-guidelines/layout/arrays-and-slices.html#layout-of-rust-array-types |
| unsafe impl<T, const N: usize> CcType for [T; N] |
| where |
| T: CcType, |
| { |
| type Name = ([(); N], T::Name); |
| } |
| |
| /// If `T` can be transmuted into `U`, then one can also transmute between |
| /// slices `[T]` and `[U]`. |
| /// |
| /// # Safety notes |
| /// |
| /// ## Slices |
| /// |
| /// "Unsafe Code Guidelines Reference" [1] points out that "the layout of a |
| /// slice [T] of length N is the same as that of a [T; N] array". Therefore the |
| /// safety of slice tranmutability can just depend on the safety of array |
| /// transmutability and we can just refer to the safety notes for the |
| /// `impl ... for [T; N]`. |
| /// |
| /// [1] |
| /// https://rust-lang.github.io/unsafe-code-guidelines/layout/arrays-and-slices.html#layout-of-rust-array-types |
| /// |
| /// ## References to slices |
| /// |
| /// The `impl` below only covers slices `[T]`, but it seems useful to talk here |
| /// about the safety of transmuting *references* to slices: `&[T]` (covered by |
| /// the `impl`s in the `ref_transmutability` module below). Transmuting of such |
| /// "fat" pointers is safe based on "Unsafe Code Guidelines Reference" [2] |
| /// pointing out that the layout of `&[T]` is independent of T - it is expected |
| /// to have the same layout as: |
| /// |
| /// ```rs |
| /// #[repr(C)] |
| /// struct Slice<T> { |
| /// ptr: *const T, |
| /// len: usize, |
| /// } |
| /// ``` |
| /// |
| /// [2] https://rust-lang.github.io/unsafe-code-guidelines/layout/pointers.html#notes |
| unsafe impl<T> CcType for [T] |
| where |
| T: CcType, |
| { |
| // Using somewhat icky `usize::MAX` to work around the fact that [()] is |
| // unsized. |
| type Name = ([(); usize::MAX], T::Name); |
| } |
| |
| /// If `T` can be transmuted into `U`, then one can also transmute between `&'a |
| /// T`, `&'a U`, `Pin<&'a T>`, and `Pin<&'a U>`. The corresponding CcType uses |
| /// `RefName` as the `Name` of the transmutability equivalence class. |
| /// |
| /// # Safety |
| /// |
| /// 1. `&T` => `&U`: Pointers to transmutable things are themselves |
| /// transmutable. This is safe for "thin" pointers (e.g. `&i32`) as well as |
| /// for "fat" pointers (e.g. `&[i32]` also stores the size of the slice). |
| /// |
| /// 2. `&T` => `Pin<&T>`: `Pin` is `repr(transparent)` and `Pin::new` is |
| /// safe. |
| /// |
| /// 3. `Pin<&T>` => `&T`: `Pin` is `repr(transparent)` and `Pin::get_ref` is |
| /// safe. |
| /// |
| /// 4. `Pin<&T>` => `Pin<&U>`: This combines transmutes 3, 1, and 2. |
| /// |
| /// 5. `&T` => `Pin<&U>`: This combines transmutes 1 and 2. |
| /// |
| /// 6. `Pin<&T>` => `&U`: This combines transmutes 4 and 3. |
| /// |
| /// # Transmuting to the unpinned variant |
| /// |
| /// We allow transmuting to the unpinned variant, when known to be safe. |
| /// |
| /// ## Motivation |
| /// |
| /// Consider what happens if a forward declaration in C++ is removed in favor of |
| /// the complete type, where the library containing that declaration was wrapped |
| /// in Rust. If the type is trivially relocatable, and we automatically generate |
| /// C++ bindings which do not use `Pin` except for non-trivially-relocatable |
| /// types, this would be a backwards incompatible change. So, we should |
| /// also support unpinning during the cast. |
| /// |
| /// ## Limited scope of impls |
| /// |
| /// Note: ideally we'd blanket impl the conversion to allow unpinning any smart |
| /// pointer type, and not only mut references, but this causes coherence issues, |
| /// because `Pin` itself is a smart pointer. I could not think of a workaround |
| /// that did not require specialization, other than to hardcode the set of |
| /// pointer types supported. |
| /// |
| /// Since we're hardcoding a set of pointer types above *anyway*, we just need |
| /// to keep the list of unpinnable types in sync with the list of "pointers to |
| /// transmutable things" above. |
| mod ref_transmutability { |
| use super::CcType; |
| use std::pin::Pin; |
| |
| type RefName<'a, Name> = (&'a (), Name); |
| |
| /// Safety: See the doc comment for the `ref_transmutability` module. |
| unsafe impl<'a, T: ?Sized> CcType for &'a T |
| where |
| T: CcType, |
| { |
| type Name = RefName<'a, T::Name>; |
| } |
| |
| /// Safety: See the doc comment for the `ref_transmutability` module. |
| unsafe impl<'a, T: ?Sized> CcType for Pin<&'a T> |
| where |
| T: CcType, |
| { |
| type Name = RefName<'a, T::Name>; |
| } |
| } |
| |
| /// If `T` can be transmuted into `U`, then one can also transmute |
| /// 1) between `&'a mut T` and `&'a mut U` |
| /// 2) between `Pin<&'a mut T>` and `Pin<&'a mut U>` |
| /// 3) from `&'a mut T` into `Pin<&'a mut T>` or into `Pin<&'a mut U>` |
| /// |
| /// Additionally, if `T` and `U` are `Unpin`, then one can also transmute |
| /// 4) from `Pin<&'a mut T>` into `&'a mut T` or into `&'a mut U` |
| /// |
| /// The corresponding CcType uses `MutRefName` as the `Name` of the |
| /// transmutability equivalence class. |
| /// |
| /// # Safety |
| /// |
| /// The list below is very similar to the safety notes for `RefName`, except |
| /// that the 3rd item below also needs to talk about the `T: Unpin` constraint. |
| /// |
| /// 1. `&mut T` => `&mut U`: Pointers to transmutable things are themselves |
| /// transmutable. This is safe for "thin" pointers (e.g. `&i32`) as well as |
| /// for "fat" pointers (e.g. `&[i32]` also stores the size of the slice). |
| /// |
| /// 2. `&mut T` => `Pin<&mut T>`: `Pin` is `repr(transparent)` and `Pin::new` |
| /// is safe. |
| /// |
| /// 3. `Pin<&mut T>` => `&mut T`: `Pin` is `repr(transparent)` and |
| /// `Pin::into_inner` is safe and allowed for `T: Unpin`. |
| /// |
| /// 4. `Pin<&mut T>` => `Pin<&mut U>`: This combines transmutes 3, 1, and 2. |
| /// |
| /// 5. `&mut T` => `Pin<&mut U>`: This combines transmutes 1 and 2. |
| /// |
| /// 6. `Pin<&mut T>` => `&mut U`: This combines transmutes 4 and 3. |
| /// |
| /// # Transmuting to the unpinned variant |
| /// |
| /// Transmuting to the unpinned variant is described in more details in the doc |
| /// comment of `RefName`. |
| mod mut_ref_transmutability { |
| use super::CcType; |
| use std::pin::Pin; |
| |
| type MutRefName<'a, Name> = (&'a mut (), Name); |
| |
| /// Safety: See the doc comment for the `mut_ref_transmutability` module. |
| unsafe impl<'a, T: ?Sized> CcType for &'a mut T |
| where |
| T: CcType, |
| T: Unpin, // See safety notes for MutRefName, item 3 |
| { |
| type Name = MutRefName<'a, T::Name>; |
| } |
| |
| /// Safety: See the doc comment for the `mut_ref_transmutability` module. |
| unsafe impl<'a, T: ?Sized> CcType for Pin<&'a mut T> |
| where |
| T: CcType, |
| { |
| type Name = MutRefName<'a, T::Name>; |
| } |
| } |
| |
| /// Like `Into<T>`, but for completeness conversions. |
| pub trait CcCast<T> { |
| fn cc_cast(self) -> T; |
| } |
| |
| impl<T, U> CcCast<U> for T |
| where |
| T: CcType, |
| U: CcType<Name = T::Name>, |
| { |
| fn cc_cast(self) -> U { |
| assert_eq!(std::mem::size_of::<T>(), std::mem::size_of::<U>()); |
| let x = std::mem::ManuallyDrop::new(self); |
| unsafe { std::mem::transmute_copy(&*x) } |
| } |
| } |
| |
| impl<'a, T: ?Sized, U: ?Sized> CcCast<Vec<&'a U>> for Vec<&'a T> |
| where |
| T: CcType, |
| U: CcType<Name = T::Name>, |
| { |
| fn cc_cast(self) -> Vec<&'a U> { |
| let (p, len, capacity) = self.into_raw_parts(); |
| unsafe { Vec::from_raw_parts(p as *mut &'a U, len, capacity) } |
| } |
| } |