blob: 9907737c1ccd64cc6ea94e40a996c9498d692845 [file] [edit]
// 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
#![no_std]
#![deny(missing_docs, warnings)]
//! This crate defines Rust types which correspond to C++ references:
//!
//! * `CRef<T>` <-> `const T&`
//! * `CMut<T>` <-> `T&`
use core::marker::PhantomData;
use core::pin::Pin;
use core::ptr::NonNull;
/// A reference similar to `const T&` in C++.
///
/// During its lifetime, this reference is guaranteed to be non-null,
/// non-dangling, and point to a value of type `T`.
///
/// Unlike Rust's shared references, mutations to the pointed-to value may occur
/// while this reference is still in use.
///
/// Additionally, this reference behaves similarly to `Pin<&T>`: the target
/// value must be pinned in memory unless `T: Unpin`.
///
/// # Layout
///
/// `CRef<'a, T>` is `#[repr(transparent)]` over `NonNull<T>`, and this is part
/// of its API: it has the same size, alignment, and function-call ABI as
/// `NonNull<T>`. When `T: Sized`, it can cross an `extern "C"` boundary as a
/// `const T*` or `const T&`, and by the `std::option` representation guarantee,
/// `Option<CRef<'a, T>>` has the same size, alignment, and ABI as `*const T`,
/// with `None` as null.
#[repr(transparent)]
pub struct CRef<'a, T: ?Sized> {
// Invariant: `ptr` is non-null, non-dangling, and points to a valid value of type `T`.
// If `T` is not `Unpin`, the pointed-to value must be pinned in memory.
ptr: NonNull<T>,
marker: PhantomData<&'a T>,
}
impl<'a, T: ?Sized> CRef<'a, T> {
/// Creates a `CRef` from a Rust reference.
///
/// This method is only usable if `T: Unpin`. For other types, use `Box::pin` or `std::pin::pin`
/// to create a pinned reference, then pass it to `CRef::from_pin`.
pub const fn from_ref(r: &'a T) -> Self
where
T: Unpin,
{
// Safety: `r` is a valid Rust reference, so it is non-null, non-dangling, and points to a
// valid value of type `T`.
unsafe { Self::from_ptr(r) }
}
/// Creates a `CRef` from a pinned Rust reference.
pub const fn from_pin(r: Pin<&'a T>) -> Self {
// Safety: `r` is a valid pinned Rust reference, so it is non-null, non-dangling, and points
// to a valid value of type `T`.
unsafe { Self::from_ptr(Pin::get_ref(r)) }
}
/// Creates a `CRef` from a raw pointer.
///
/// # Safety
///
///`ptr` must be non-null, non-dangling, and point to a value of type `T` for the lifetime of
/// the resulting `CRef`.
pub const unsafe fn from_ptr(ptr: *const T) -> Self {
// Safety: `ptr` is a valid pointer to a `T`, so it is non-null, non-dangling, and points to
// a valid value of type `T`.
CRef { ptr: unsafe { NonNull::new_unchecked(ptr as *mut _) }, marker: PhantomData }
}
/// Returns a pinned reference to the target value. The target value *must* be unchanged
/// for the lifetime of the returned reference.
///
/// # Safety
///
/// The target value of this reference must not be mutated during the lifetime of the returned
/// reference.
pub const unsafe fn unchanging(this: Self) -> Pin<&'a T> {
// Safety: `CRef` guarantees that if `T` is not `Unpin`, then the target is pinned.
unsafe { Pin::new_unchecked(&*this.ptr.as_ptr()) }
}
/// Converts this `CRef` to a `CMut` as if by C++'s `const_cast` conversion.
///
/// # Safety
///
/// The target value must be valid to mutate on the current thread. This is *not* valid to use
/// with a `CRef` that was created via a Rust `&T` or `Pin<&T>`, e.g. via `CRef::from_ref` or
/// `CRef::from_pin`.
pub const unsafe fn const_cast(this: Self) -> CMut<'a, T> {
CMut { ptr: this.ptr, marker: PhantomData }
}
/// Returns a raw pointer to the target value.
pub const fn as_ptr(this: Self) -> *const T {
this.ptr.as_ptr()
}
}
/// A reference similar to `T&` in C++.
///
/// During its lifetime, this reference is guaranteed to be non-null,
/// non-dangling, and point to a value of type `T` which is valid to mutate on
/// the current thread.
///
/// Unlike Rust's mutable references, `CMut` is not guaranteed to be unique,
/// and other reads and writes to the pointed-to value may occur while this
/// reference is still in use. APIs which use `CMut` to mutate a value should
/// avoid mutations which may invalidate other existing references, such
/// as via iterator invalidation, setting an optional to "none" while a
/// reference to the innards persists, or any other invalidation of a
/// "child group" (see https://verdagon.dev/blog/group-borrowing#child-groups).
///
/// Additionally, this reference behaves similarly to `Pin<&mut T>`: the target
/// value must be pinned in memory unless `T: Unpin`.
///
/// # Layout
///
/// `CMut<'a, T>` is `#[repr(transparent)]` over `NonNull<T>`, and this is part
/// of its API: it has the same size, alignment, and function-call ABI as
/// `NonNull<T>`. When `T: Sized`, it can cross an `extern "C"` boundary as a
/// `T*` or `T&`, and by the `std::option` representation guarantee,
/// `Option<CMut<'a, T>>` has the same size, alignment, and ABI as `*mut T`,
/// with `None` as null.
#[repr(transparent)]
pub struct CMut<'a, T: ?Sized> {
// Invariant: `ptr` is non-null, non-dangling, and points to a valid value of type `T`.
// If `T` is not `Unpin`, the pointed-to value must be pinned in memory.
ptr: NonNull<T>,
marker: PhantomData<&'a mut T>,
}
impl<'a, T: ?Sized> CMut<'a, T> {
/// Creates a `CMut` from a Rust reference.
///
/// This method is only usable if `T: Unpin`. For other types, use `Box::pin` or `std::pin::pin`
/// to create a pinned reference, then pass it to `CMut::from_pin`.
pub const fn from_ref(r: &'a mut T) -> Self
where
T: Unpin,
{
// Safety: `r` is a valid Rust reference, so it is non-null, non-dangling, and points to a
// valid value of type `T`.
unsafe { Self::from_ptr(r as *mut T) }
}
/// Creates a `CMut` from a pinned Rust reference.
pub const fn from_pin(r: Pin<&'a mut T>) -> Self {
// Safety: `r` is a valid pinned Rust reference, so it is non-null, non-dangling, and points
// to a valid value of type `T`.
unsafe { Self::from_ptr(Pin::into_inner_unchecked(r) as *mut _) }
}
/// Creates a `CMut` from a raw pointer.
///
/// # Safety
///
/// For the remainder of the resuting `CMut`'s lifetime, `ptr` must be non-null, non-dangling,
/// and point to a value of type `T` which is valid to mutate on the current thread.
pub const unsafe fn from_ptr(ptr: *mut T) -> Self {
// Safety: `ptr` is a valid pointer to a `T`, so it is non-null, non-dangling, and points to
// a valid value of type `T`.
CMut { ptr: unsafe { NonNull::new_unchecked(ptr) }, marker: PhantomData }
}
/// Converts this `CMut` to a `CRef`.
///
/// This conversion is "free" and does not impose any additional safety requirements.
pub const fn into_const(this: Self) -> CRef<'a, T> {
// Safety: `this` is a valid pointer to a `T`, so it is non-null, non-dangling, and points
// to a valid value of type `T`.
unsafe { CRef::from_ptr(this.ptr.as_ptr()) }
}
/// Returns a pinned reference to the target value. The target value *must* be unchanged
/// for the lifetime of the returned reference.
///
/// # Safety
///
/// The target value of this reference must not be mutated during the lifetime of the returned
/// reference.
pub const unsafe fn unchanging(this: Self) -> Pin<&'a T> {
// Safety: `CMut` guarantees that if `T` is not `Unpin`, then the target is pinned.
unsafe { Pin::new_unchecked(&*this.ptr.as_ptr()) }
}
/// Returns a pinned mutable reference to the target value.
///
/// # Safety
///
/// For the lifetime of the returned reference, the target value of the reference must not be
/// mutated or read via any other pointers or references.
pub const unsafe fn unique(this: Self) -> Pin<&'a mut T> {
// Safety: `CMut` guarantees that if `T` is not `Unpin`, then the target is pinned.
unsafe { Pin::new_unchecked(&mut *this.ptr.as_ptr()) }
}
/// Returns a regular (non-pinned) mutable reference to the target value.
/// This method is only available if `T: Unpin`.
///
/// This method is equivalent to `Pin::into_inner(CMut::unique(c_mut))`.
///
/// # Safety
///
/// For the lifetime of the returned reference, the target value of the reference must not be
/// mutated or read via any other pointers or references.
pub const unsafe fn unpin_unique(this: Self) -> &'a mut T
where
T: Unpin,
{
// Safety: this function has the same safety requirements as `CMut::unique`.
Pin::into_inner(unsafe { CMut::unique(this) })
}
/// Returns a raw pointer to the target value.
pub const fn as_ptr(this: Self) -> *const T {
this.ptr.as_ptr()
}
/// Returns a raw mutable pointer to the target value.
pub const fn as_mut_ptr(this: Self) -> *mut T {
this.ptr.as_ptr()
}
}
// Both `CRef` and `CMut` are unconditionally `Clone` and `Copy`, as both
// assume that shared mutable access may occur.
impl<'a, T: ?Sized> Copy for CRef<'a, T> {}
impl<'a, T: ?Sized> Clone for CRef<'a, T> {
fn clone(&self) -> Self {
*self
}
}
impl<'a, T: ?Sized> Copy for CMut<'a, T> {}
impl<'a, T: ?Sized> Clone for CMut<'a, T> {
fn clone(&self) -> Self {
*self
}
}
// `CRef` and `CMut` are unconditionally `Unpin`, as they are just pointers.
//
// They do imply, however, that the pointed-to object is pinned, and `&mut T` may only be acquired
// from them if `T: Unpin`.
impl<'a, T: ?Sized> Unpin for CRef<'a, T> {}
impl<'a, T: ?Sized> Unpin for CMut<'a, T> {}
/// A trait corresponding to the common C++ notion of "thread-safe" types.
///
/// A type is `ThreadSafe` if its `const` and non-`const` methods and APIS are safe to access
/// concurrently via multiple threads.
///
/// `CRef<T>` and `CMut<T>` are only `Send` or `Sync` when `T: ThreadSafe`.
/// When accessing only `const` methods across multiple threads concurrently,
/// prefer to share a `&T` or `Pin<&T>` between threads, and then downgrade to
/// a `CRef<T>` using `CRef::from_ref` or `CRef::from_pin`.
///
/// Other C++ threading concepts align with existing Rust traits:
/// * Thread-compatible types: `const` methods of these types may be accessed concurrently.
/// Thread-compatible types are `Sync` and `Send`, but not `ThreadSafe`.
/// * Thread-unsafe types: neither `const` nor non-`const` methods may be accessed concurrently.
/// Thread-unsafe types are not `Sync`. In some cases, they may additionally have affinity to a
/// particular thread, additionally making them not `Send`.
///
/// # Safety
///
/// This trait is only safe to implement if all functions which consume a `CRef<Self>` or
/// `CMut<Self>` can be invoked concurrently from any thread.
unsafe trait ThreadSafe {}
unsafe impl<'a, T: ThreadSafe + ?Sized> Send for CRef<'a, T> {}
unsafe impl<'a, T: ThreadSafe + ?Sized> Sync for CRef<'a, T> {}
unsafe impl<'a, T: ThreadSafe + ?Sized> Send for CMut<'a, T> {}
unsafe impl<'a, T: ThreadSafe + ?Sized> Sync for CMut<'a, T> {}
mod private {
// https://rust-lang.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed
pub trait Sealed {}
}
/// A trait for types that can act like a `CRef<T>`.
///
/// # Safety
///
/// Types which implement this trait must be freely transmutable to a `CRef<Target>`.
pub unsafe trait CRefLike<'a>: private::Sealed {
/// The type of the value being referenced.
type Target: ?Sized;
/// Converts `self` to a `CRef<'a, Self::Target>` as if by `transmute`.
fn into_cref(self) -> CRef<'a, Self::Target>
where
Self: Sized,
{
// Safety: implementing this trait guarantees that transmuting `Self` to
// `CRef<'a, Self::Target>` is valid.
unsafe { *(&self as *const Self as *const CRef<'a, Self::Target>) }
}
}
/// A trait for types that can act like a `CMut<T>`.
///
/// # Safety
///
/// Types which implement this trait must be freely transmutable to a `CMut<Target>`.
pub unsafe trait CMutLike<'a>: CRefLike<'a> {
/// Converts `self` to a `CRef<'a, Self::Target>` as if by `transmute`.
fn into_cmut(mut self) -> CMut<'a, Self::Target>
where
Self: Sized,
{
// Safety: implementing this trait guarantees that transmuting `Self` to
// `CMut<'a, Self::Target>` is valid.
unsafe { *(&mut self as *mut Self as *mut CMut<'a, Self::Target>) }
}
}
/// A macro designed for use in autogenerated Rust bindings to C++ APIs.
///
/// When used in argument position, this will accept any type which acts like
/// a const reference to the target type.
///
/// Note that use of this macro in argument position will result in a function which is generic
/// over the argument type. As with other conversion-style generics, prefer to perform the
/// converison and then immediately delegate to a non-generic helper function in order to avoid
/// monomorphization-induced code bloat.
#[macro_export]
macro_rules! CRefTo {
($lt:lifetime, $target_type:ty) => {
impl $crate::CRefLike<$lt, Target=$target_type>
}
}
/// A macro designed for use in autogenerated Rust bindings to C++ APIs.
///
/// When used in argument position, this will accept any type which acts like
/// a const reference to the target type.
///
/// Note that use of this macro in argument position will result in a function which is generic
/// over the argument type. As with other conversion-style generics, prefer to perform the
/// converison and then immediately delegate to a non-generic helper function in order to avoid
/// monomorphization-induced code bloat.
#[macro_export]
macro_rules! CMutTo {
($lt:lifetime, $target_type:ty) => {
impl $crate::CMutLike<$lt, Target=$target_type>
}
}
impl<'a, T: ?Sized> private::Sealed for CRef<'a, T> {}
impl<'a, T: ?Sized> private::Sealed for CMut<'a, T> {}
impl<T: ?Sized> private::Sealed for &T {}
impl<T: ?Sized> private::Sealed for &mut T {}
impl<T: ?Sized> private::Sealed for Pin<&T> {}
impl<T: ?Sized> private::Sealed for Pin<&mut T> {}
unsafe impl<'a, T: ?Sized> CRefLike<'a> for CRef<'a, T> {
type Target = T;
}
unsafe impl<'a, T: ?Sized> CRefLike<'a> for CMut<'a, T> {
type Target = T;
}
unsafe impl<'a, T: ?Sized> CMutLike<'a> for CMut<'a, T> {}
unsafe impl<'a, T: ?Sized> CRefLike<'a> for &'a T
where
T: Unpin,
{
type Target = T;
}
unsafe impl<'a, T: ?Sized> CRefLike<'a> for &'a mut T
where
T: Unpin,
{
type Target = T;
}
unsafe impl<'a, T: ?Sized> CMutLike<'a> for &'a mut T where T: Unpin {}
unsafe impl<'a, T: ?Sized> CRefLike<'a> for Pin<&'a T> {
type Target = T;
}
unsafe impl<'a, T: ?Sized> CRefLike<'a> for Pin<&'a mut T> {
type Target = T;
}
unsafe impl<'a, T: ?Sized> CMutLike<'a> for Pin<&'a mut T> {}