blob: 041bd72770be3dc839b297de940542d90a24a991 [file] [log] [blame]
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use core::{marker::PhantomData, ops::Deref, pin::Pin};
#[cfg(nightly)]
use std::{marker::Unsize, ops::DispatchFromDyn};
use cxx::{memory::UniquePtrTarget, UniquePtr};
/// A C++ const reference. These are different from Rust's `&T` in that
/// these may exist even while the object is mutated elsewhere. See also
/// [`CppMutRef`] for the mutable equivalent.
///
/// The key rule is: we *never* dereference these in Rust. Therefore, any
/// UB here cannot manifest within Rust, but only across in C++, and therefore
/// they are equivalently safe to using C++ references in pure-C++ codebases.
///
/// *Important*: you might be wondering why you've never encountered this type.
/// These exist in autocxx-generated bindings only if the `unsafe_references_wrapped`
/// safety policy is given. This may become the default in future.
///
/// # Usage
///
/// These types of references are pretty useless in Rust. You can't do
/// field access. But, you can pass them back into C++! And specifically,
/// you can call methods on them (i.e. use this type as a `this`). So
/// the common case here is when C++ gives you a reference to some type,
/// then you want to call methods on that reference.
///
/// # Calling methods
///
/// As noted, one of the main reasons for this type is to call methods.
/// Unfortunately, that depends on unstable Rust features. If you can't
/// call methods on one of these references, check you're using nightly
/// and add `#![feature(arbitrary_self_types)]` to your crate.
///
/// # Lifetimes and cloneability
///
/// Although these references implement C++ aliasing semantics, they
/// do attempt to give you Rust lifetime tracking. This means if a C++ object
/// gives you a reference, you won't be able to use that reference after the
/// C++ object is no longer around.
///
/// This is usually what you need, since a C++ object will typically give
/// you a reference to part of _itself_ or something that it owns. But,
/// if you know that the returned reference lasts longer than its vendor,
/// you can use `lifetime_cast` to get a long-lived version.
///
/// On the other hand, these references do not give you Rust's exclusivity
/// guarantees. These references can be freely cloned, and using [`CppRef::const_cast`]
/// you can even make a mutable reference from an immutable reference.
///
/// # Field access
///
/// Field access would be achieved by adding C++ `get` and/or `set` methods.
/// It's possible that a future version of `autocxx` could generate such
/// getters and setters automatically, but they would need to be `unsafe`
/// because there is no guarantee that the referent of a `CppRef` is actually
/// what it's supposed to be, or alive. `CppRef`s may flow from C++ to Rust
/// via arbitrary means, and with sufficient uses of `get` and `set` it would
/// even be possible to create a use-after-free in pure Rust code (for instance,
/// store a [`CppPin`] in a struct field, get a `CppRef` to its referent, then
/// use a setter to reset that field of the struct.)
///
/// # Deref
///
/// This type implements [`Deref`] because that's the mechanism that the
/// unstable Rust `arbitrary_self_types` features uses to determine callable
/// methods. However, actually calling `Deref::deref` is not permitted and will
/// result in a compilation failure. If you wish to create a Rust reference
/// from the C++ reference, see [`CppRef::as_ref`].
///
/// # Nullness
///
/// Creation of a null C++ reference is undefined behavior (because such
/// a reference can only be created by dereferencing a null pointer.)
/// However, in practice, they exist, and we need to be compatible with
/// pre-existing C++ APIs even if they do naughty things like this.
/// Therefore this `CppRef` type does allow null values. This is a bit
/// unfortunate because it means `Option<CppRef<T>>`
/// occupies more space than `CppRef<T>`.
///
/// # Dynamic dispatch
///
/// You might wonder if you can do this:
/// ```ignore
/// let CppRef<dyn Trait> = ...; // obtain some CppRef<concrete type>
/// ```
/// Dynamic dispatch works so long as you're using nightly (we require another
/// unstable feature, `dispatch_from_dyn`). But we need somewhere to store
/// the trait object, and `CppRef` isn't it -- a `CppRef` can only store a
/// simple pointer to something else. So, you need to store the trait object
/// in a `Box` or similar:
/// ```ignore
/// trait SomeTrait {
/// fn some_method(self: CppRef<Self>)
/// }
/// impl SomeTrait for ffi::Concrete {
/// fn some_method(self: CppRef<Self>) {}
/// }
/// let obj: Pin<Box<dyn SomeTrait>> = ffi::Concrete::new().within_box();
/// let obj = CppPin::from_pinned_box(obj);
/// farm_area.as_cpp_ref().some_method();
/// ```
///
/// # Implementation notes
///
/// Internally, this is represented as a raw pointer in Rust. See the note above
/// about Nullness for why we don't use [`core::ptr::NonNull`].
#[repr(transparent)]
pub struct CppRef<'a, T: ?Sized> {
ptr: *const T,
phantom: PhantomData<&'a T>,
}
impl<'a, T: ?Sized> CppRef<'a, T> {
/// Retrieve the underlying C++ pointer.
pub fn as_ptr(&self) -> *const T {
self.ptr
}
/// Get a regular Rust reference out of this C++ reference.
///
/// # Safety
///
/// Callers must guarantee that the referent is not modified by any other
/// C++ or Rust code while the returned reference exists. Callers must
/// also guarantee that no mutable Rust reference is created to the
/// referent while the returned reference exists.
///
/// Callers must also be sure that the C++ reference is properly
/// aligned, not null, pointing to valid data, etc.
pub unsafe fn as_ref(&self) -> &T {
&*self.as_ptr()
}
/// Create a C++ reference from a raw pointer.
pub fn from_ptr(ptr: *const T) -> Self {
Self {
ptr,
phantom: PhantomData,
}
}
/// Create a mutable version of this reference, roughly equivalent
/// to C++ `const_cast`.
///
/// The opposite is to use [`AsCppRef::as_cpp_ref`] on a [`CppMutRef`]
/// to obtain a [`CppRef`].
///
/// # Safety
///
/// Because we never dereference a `CppRef` in Rust, this cannot create
/// undefined behavior _within Rust_ and is therefore not unsafe. It is
/// however generally unwise, just as it is in C++. Use sparingly.
pub fn const_cast(&self) -> CppMutRef<'a, T> {
CppMutRef {
ptr: self.ptr as *mut T,
phantom: self.phantom,
}
}
/// Extend the lifetime of the returned reference beyond normal Rust
/// borrow checker rules.
///
/// Normally, a reference can't be used beyond the lifetime of the object
/// which gave it to you, but sometimes C++ APIs can return references
/// to global or other longer-lived objects. In such a case you should
/// use this method to get a longer-lived reference.
///
/// # Usage
///
/// When you're given a C++ reference and you know its referent is valid
/// for a long time, use this method. Store the resulting `PhantomReferent`
/// somewhere in Rust with an equivalent lifetime.
/// That object can then vend longer-lived `CppRef`s using
/// [`AsCppRef::as_cpp_ref`].
///
/// # Safety
///
/// Because `CppRef`s are never dereferenced in Rust, misuse of this API
/// cannot lead to undefined behavior _in Rust_ and is therefore not
/// unsafe. Nevertheless this can lead to UB in C++, so use carefully.
pub fn lifetime_cast(&self) -> PhantomReferent<T> {
PhantomReferent(self.ptr)
}
}
impl<'a, T: ?Sized> Deref for CppRef<'a, T> {
type Target = *const T;
#[inline]
fn deref(&self) -> &Self::Target {
// With `inline_const` we can simplify this to:
// const { panic!("you shouldn't deref CppRef!") }
struct C<T: ?Sized>(T);
impl<T: ?Sized> C<T> {
const V: core::convert::Infallible = panic!(
"You cannot directly obtain a Rust reference from a CppRef. Use CppRef::as_ref."
);
}
match C::<T>::V {}
}
}
impl<'a, T: ?Sized> Clone for CppRef<'a, T> {
fn clone(&self) -> Self {
Self {
ptr: self.ptr,
phantom: self.phantom,
}
}
}
#[cfg(nightly)]
impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<CppRef<'_, U>> for CppRef<'_, T> {}
/// A C++ non-const reference. These are different from Rust's `&mut T` in that
/// several C++ references can exist to the same underlying data ("aliasing")
/// and that's not permitted for regular Rust references.
///
/// See [`CppRef`] for details on safety, usage models and implementation.
///
/// You can convert this to a [`CppRef`] using the [`std::convert::Into`] trait.
#[repr(transparent)]
pub struct CppMutRef<'a, T: ?Sized> {
ptr: *mut T,
phantom: PhantomData<&'a T>,
}
impl<'a, T: ?Sized> CppMutRef<'a, T> {
/// Retrieve the underlying C++ pointer.
pub fn as_mut_ptr(&self) -> *mut T {
self.ptr
}
/// Get a regular Rust mutable reference out of this C++ reference.
///
/// # Safety
///
/// Callers must guarantee that the referent is not modified by any other
/// C++ or Rust code while the returned reference exists. Callers must
/// also guarantee that no other Rust reference is created to the referent
/// while the returned reference exists.
///
/// Callers must also be sure that the C++ reference is properly
/// aligned, not null, pointing to valid data, etc.
pub unsafe fn as_mut(&mut self) -> &mut T {
&mut *self.as_mut_ptr()
}
/// Create a C++ reference from a raw pointer.
pub fn from_ptr(ptr: *mut T) -> Self {
Self {
ptr,
phantom: PhantomData,
}
}
/// Extend the lifetime of the returned reference beyond normal Rust
/// borrow checker rules. See [`CppRef::lifetime_cast`].
pub fn lifetime_cast(&mut self) -> PhantomReferentMut<T> {
PhantomReferentMut(self.ptr)
}
}
impl<'a, T: ?Sized> Deref for CppMutRef<'a, T> {
type Target = *const T;
#[inline]
fn deref(&self) -> &Self::Target {
// With `inline_const` we can simplify this to:
// const { panic!("you shouldn't deref CppRef!") }
struct C<T: ?Sized>(T);
impl<T: ?Sized> C<T> {
const V: core::convert::Infallible = panic!("You cannot directly obtain a Rust reference from a CppMutRef. Use CppMutRef::as_mut.");
}
match C::<T>::V {}
}
}
impl<'a, T: ?Sized> Clone for CppMutRef<'a, T> {
fn clone(&self) -> Self {
Self {
ptr: self.ptr,
phantom: self.phantom,
}
}
}
impl<'a, T> From<CppMutRef<'a, T>> for CppRef<'a, T> {
fn from(mutable: CppMutRef<'a, T>) -> Self {
Self {
ptr: mutable.ptr,
phantom: mutable.phantom,
}
}
}
#[cfg(nightly)]
impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<CppMutRef<'_, U>> for CppMutRef<'_, T> {}
/// Any type which can return a C++ reference to its contents.
pub trait AsCppRef<T: ?Sized> {
/// Returns a reference which obeys C++ reference semantics
fn as_cpp_ref(&self) -> CppRef<T>;
}
/// Any type which can return a C++ reference to its contents.
pub trait AsCppMutRef<T: ?Sized>: AsCppRef<T> {
/// Returns a mutable reference which obeys C++ reference semantics
fn as_cpp_mut_ref(&mut self) -> CppMutRef<T>;
}
impl<'a, T: ?Sized> AsCppRef<T> for CppMutRef<'a, T> {
fn as_cpp_ref(&self) -> CppRef<T> {
CppRef::from_ptr(self.ptr)
}
}
/// Workaround for the inability to use std::ptr::addr_of! on the contents
/// of a box.
#[repr(transparent)]
struct CppPinContents<T: ?Sized>(T);
impl<T: ?Sized> CppPinContents<T> {
fn addr_of(&self) -> *const T {
std::ptr::addr_of!(self.0)
}
fn addr_of_mut(&mut self) -> *mut T {
std::ptr::addr_of_mut!(self.0)
}
}
/// A newtype wrapper which causes the contained object to obey C++ reference
/// semantics rather than Rust reference semantics. That is, multiple aliasing
/// mutable C++ references may exist to the contents.
///
/// C++ references are permitted to alias one another, and commonly do.
/// Rust references must alias according only to the narrow rules of the
/// borrow checker.
///
/// If you need C++ to access your Rust object, first imprison it in one of these
/// objects, then use [`Self::as_cpp_ref`] to obtain C++ references to it.
/// If you need the object back for use in the Rust domain, use [`CppPin::extract`],
/// but be aware of the safety invariants that you - as a human - will need
/// to guarantee.
///
/// # Usage models
///
/// From fairly safe to fairly unsafe:
///
/// * *Configure a thing in Rust then give it to C++*. Take your Rust object,
/// set it up freely using Rust references, methods and data, then imprison
/// it in a `CppPin` and keep it around while you work with it in C++.
/// There is no possibility of _aliasing_ UB in this usage model, but you
/// still need to be careful of use-after-free bugs, just as if you were
/// to create a reference to any data in C++. The Rust borrow checker will
/// help you a little by ensuring that your `CppRef` objects don't outlive
/// the `CppPin`, but once those references pass into C++, it can't help.
/// * *Pass a thing to C++, have it operate on it synchronously, then take
/// it back*. To do this, you'd imprison your Rust object in a `CppPin`,
/// then pass mutable C++ references (using [`AsCppMutRef::as_cpp_mut_ref`])
/// into a C++ function. C++ would duly operate on the object, and thereafter
/// you could reclaim the object with `extract()`. At this point, you (as
/// a human) will need to give a guarantee that no references remain in the
/// C++ domain. If your object was just locally used by a single C++ function,
/// which has now returned, this type of local analysis may well be practical.
/// * *Share a thing between Rust and C++*. This object can vend both C++
/// references and Rust references (via `as_ref` etc.) It may be possible
/// for you to guarantee that C++ does not mutate the object while any Rust
/// reference exists. If you choose this model, you'll need to carefully
/// track exactly what happens to references and pointers on both sides,
/// and document your evidence for why you are sure this is safe.
/// Failure here is bad: Rust makes all sorts of optimization decisions based
/// upon its borrow checker guarantees, so mistakes can lead to undebuggable
/// action-at-a-distance crashes.
///
/// # See also
///
/// See also [`CppUniquePtrPin`], which is equivalent for data which is in
/// a [`cxx::UniquePtr`].
pub struct CppPin<T: ?Sized>(Box<CppPinContents<T>>);
impl<T: ?Sized> CppPin<T> {
/// Imprison the Rust data within a `CppPin`. This eliminates any remaining
/// Rust references (since we take the item by value) and this object
/// subsequently only vends C++ style references, not Rust references,
/// until or unless `extract` is called.
pub fn new(item: T) -> Self
where
T: Sized,
{
Self(Box::new(CppPinContents(item)))
}
/// Imprison the boxed Rust data within a `CppPin`. This eliminates any remaining
/// Rust references (since we take the item by value) and this object
/// subsequently only vends C++ style references, not Rust references,
/// until or unless `extract` is called.
///
/// If the item is already in a `Box`, this is slightly more efficient than
/// `new` because it will avoid moving/reallocating it.
pub fn from_box(item: Box<T>) -> Self {
// Safety: CppPinContents<T> is #[repr(transparent)] so
// this transmute from
// Box<T>
// to
// Box<CppPinContents<T>>
// is safe.
let contents = unsafe { std::mem::transmute(item) };
Self(contents)
}
// Imprison the boxed Rust data within a `CppPin`. This eliminates any remaining
/// Rust references (since we take the item by value) and this object
/// subsequently only vends C++ style references, not Rust references,
/// until or unless `extract` is called.
///
/// If the item is already in a `Box`, this is slightly more efficient than
/// `new` because it will avoid moving/reallocating it.
pub fn from_pinned_box(item: Pin<Box<T>>) -> Self {
// Safety: it's OK to un-pin the Box because we'll be putting it
// into a CppPin which upholds the same pinned-ness contract.
Self::from_box(unsafe { Pin::into_inner_unchecked(item) })
}
/// Get an immutable pointer to the underlying object.
pub fn as_ptr(&self) -> *const T {
self.0.addr_of()
}
/// Get a mutable pointer to the underlying object.
pub fn as_mut_ptr(&mut self) -> *mut T {
self.0.addr_of_mut()
}
/// Get a normal Rust reference to the underlying object. This is unsafe.
///
/// # Safety
///
/// You must guarantee that C++ will not mutate the object while the
/// reference exists.
pub unsafe fn as_ref(&self) -> &T {
&*self.as_ptr()
}
/// Get a normal Rust mutable reference to the underlying object. This is unsafe.
///
/// # Safety
///
/// You must guarantee that C++ will not mutate the object while the
/// reference exists.
pub unsafe fn as_mut(&mut self) -> &mut T {
&mut *self.as_mut_ptr()
}
/// Extract the object from within its prison, for re-use again within
/// the domain of normal Rust references.
///
/// This returns a `Box<T>`: if you want the underlying `T` you can extract
/// it using `*`.
///
/// # Safety
///
/// Callers promise that no remaining C++ references exist either
/// in the form of Rust [`CppRef`]/[`CppMutRef`] or any remaining pointers/
/// references within C++.
pub unsafe fn extract(self) -> Box<T> {
// Safety: CppPinContents<T> is #[repr(transparent)] so
// this transmute from
// Box<CppPinContents<T>>
// to
// Box<T>
// is safe.
std::mem::transmute(self.0)
}
}
impl<T: ?Sized> AsCppRef<T> for CppPin<T> {
fn as_cpp_ref(&self) -> CppRef<T> {
CppRef::from_ptr(self.as_ptr())
}
}
impl<T: ?Sized> AsCppMutRef<T> for CppPin<T> {
fn as_cpp_mut_ref(&mut self) -> CppMutRef<T> {
CppMutRef::from_ptr(self.as_mut_ptr())
}
}
/// Any newtype wrapper which causes the contained [`UniquePtr`] target to obey C++ reference
/// semantics rather than Rust reference semantics. That is, multiple aliasing
/// mutable C++ references may exist to the contents.
///
/// C++ references are permitted to alias one another, and commonly do.
/// Rust references must alias according only to the narrow rules of the
/// borrow checker.
pub struct CppUniquePtrPin<T: UniquePtrTarget>(UniquePtr<T>);
impl<T: UniquePtrTarget> CppUniquePtrPin<T> {
/// Imprison the type within a `CppPin`. This eliminates any remaining
/// Rust references (since we take the item by value) and this object
/// subsequently only vends C++ style references, not Rust references.
pub fn new(item: UniquePtr<T>) -> Self {
Self(item)
}
/// Get an immutable pointer to the underlying object.
pub fn as_ptr(&self) -> *const T {
// TODO - avoid brief reference here
self.0
.as_ref()
.expect("UniquePtr was null; we can't make a C++ reference")
}
}
impl<T: UniquePtrTarget> AsCppRef<T> for CppUniquePtrPin<T> {
fn as_cpp_ref(&self) -> CppRef<T> {
CppRef::from_ptr(self.as_ptr())
}
}
impl<T: UniquePtrTarget> AsCppMutRef<T> for CppUniquePtrPin<T> {
fn as_cpp_mut_ref(&mut self) -> CppMutRef<T> {
let pinnned_ref: Pin<&mut T> = self
.0
.as_mut()
.expect("UniquePtr was null; we can't make a C++ reference");
let ptr = unsafe { Pin::into_inner_unchecked(pinnned_ref) };
CppMutRef::from_ptr(ptr)
}
}
/// A structure used to extend the lifetime of a returned C++ reference,
/// to indicate to Rust that it's beyond the normal Rust lifetime rules.
/// See [`CppRef::lifetime_cast`].
#[repr(transparent)]
pub struct PhantomReferent<T: ?Sized>(*const T);
impl<T: ?Sized> AsCppRef<T> for PhantomReferent<T> {
fn as_cpp_ref(&self) -> CppRef<T> {
CppRef::from_ptr(self.0)
}
}
/// A structure used to extend the lifetime of a returned C++ mutable reference,
/// to indicate to Rust that it's beyond the normal Rust lifetime rules.
/// See [`CppRef::lifetime_cast`].
#[repr(transparent)]
pub struct PhantomReferentMut<T: ?Sized>(*mut T);
impl<T: ?Sized> AsCppRef<T> for PhantomReferentMut<T> {
fn as_cpp_ref(&self) -> CppRef<T> {
CppRef::from_ptr(self.0)
}
}
impl<T: ?Sized> AsCppMutRef<T> for PhantomReferentMut<T> {
fn as_cpp_mut_ref(&mut self) -> CppMutRef<T> {
CppMutRef::from_ptr(self.0)
}
}
#[cfg(all(feature = "arbitrary_self_types", test))]
mod tests {
use super::*;
struct CppOuter {
_a: u32,
inner: CppInner,
global: *const CppInner,
}
impl CppOuter {
fn get_inner_ref<'a>(self: &CppRef<'a, CppOuter>) -> CppRef<'a, CppInner> {
// Safety: emulating C++ code for test purposes. This is safe
// because we know the data isn't modified during the lifetime of
// the returned reference.
let self_rust_ref = unsafe { self.as_ref() };
CppRef::from_ptr(std::ptr::addr_of!(self_rust_ref.inner))
}
fn get_global_ref<'a>(self: &CppRef<'a, CppOuter>) -> CppRef<'a, CppInner> {
// Safety: emulating C++ code for test purposes. This is safe
// because we know the data isn't modified during the lifetime of
// the returned reference.
let self_rust_ref = unsafe { self.as_ref() };
CppRef::from_ptr(self_rust_ref.global)
}
}
struct CppInner {
b: u32,
}
impl CppInner {
fn value_is(self: &CppRef<Self>) -> u32 {
// Safety: emulating C++ code for test purposes. This is safe
// because we know the data isn't modified during the lifetime of
// the returned reference.
let self_rust_ref = unsafe { self.as_ref() };
self_rust_ref.b
}
}
#[test]
fn cpp_objects() {
let mut global = CppInner { b: 7 };
let global_ref_lifetime_phantom;
{
let outer = CppOuter {
_a: 12,
inner: CppInner { b: 3 },
global: &mut global,
};
let outer = CppPin::new(outer);
let inner_ref = outer.as_cpp_ref().get_inner_ref();
assert_eq!(inner_ref.value_is(), 3);
global_ref_lifetime_phantom = Some(outer.as_cpp_ref().get_global_ref().lifetime_cast());
}
let global_ref = global_ref_lifetime_phantom.unwrap();
let global_ref = global_ref.as_cpp_ref();
assert_eq!(global_ref.value_is(), 7);
}
#[test]
fn cpp_pin() {
let a = RustThing { _a: 4 };
let a = CppPin::new(a);
let _ = a.as_cpp_ref();
let _ = a.as_cpp_ref();
}
}