autocxx/
value_param.rs

1// Copyright 2022 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use cxx::{memory::UniquePtrTarget, UniquePtr};
10use moveit::{AsMove, CopyNew, MoveNew, New};
11use std::{marker::PhantomPinned, mem::MaybeUninit, ops::Deref, pin::Pin};
12
13/// A trait representing a parameter to a C++ function which is received
14/// by value.
15///
16/// Rust has the concept of receiving parameters by _move_ or by _reference_.
17/// C++ has the concept of receiving a parameter by 'value', which means
18/// the parameter gets copied.
19///
20/// To make it easy to pass such parameters from Rust, this trait exists.
21/// It is implemented both for references `&T` and for `UniquePtr<T>`,
22/// subject to the presence or absence of suitable copy and move constructors.
23/// This allows you to pass in parameters by copy (as is ergonomic and normal
24/// in C++) retaining the original parameter; or by move semantics thus
25/// destroying the object you're passing in. Simply use a reference if you want
26/// copy semantics, or the item itself if you want move semantics.
27///
28/// It is not recommended that you implement this trait, nor that you directly
29/// use its methods, which are for use by `autocxx` generated code only.
30///
31/// # Use of `moveit` traits
32///
33/// Most of the implementations of this trait require the type to implement
34/// [`CopyNew`], which is simply the `autocxx`/`moveit` way of saying that
35/// the type has a copy constructor in C++.
36///
37/// # Being explicit
38///
39/// If you wish to explicitly force either a move or a copy of some type,
40/// use [`as_mov`] or [`as_copy`].
41///
42/// # Performance
43///
44/// At present, some additional copying occurs for all implementations of
45/// this trait other than that for [`cxx::UniquePtr`]. In the future it's
46/// hoped that the implementation for `&T where T: CopyNew` can also avoid
47/// this extra copying.
48///
49/// # Panics
50///
51/// The implementations of this trait which take a [`cxx::UniquePtr`] will
52/// panic if the pointer is NULL.
53///
54/// # Safety
55///
56/// Implementers must guarantee that the pointer returned by `get_ptr`
57/// is of the correct size and alignment of `T`.
58pub unsafe trait ValueParam<T> {
59    /// Any stack storage required. If, as part of passing to C++,
60    /// we need to store a temporary copy of the value, this will be `T`,
61    /// otherwise `()`.
62    #[doc(hidden)]
63    type StackStorage;
64    /// Populate the stack storage given as a parameter. Only called if you
65    /// return `true` from `needs_stack_space`.
66    ///
67    /// # Safety
68    ///
69    /// Callers must guarantee that this object will not move in memory
70    /// between this call and any subsequent `get_ptr` call or drop.
71    #[doc(hidden)]
72    unsafe fn populate_stack_space(self, this: Pin<&mut Option<Self::StackStorage>>);
73    /// Retrieve the pointer to the underlying item, to be passed to C++.
74    /// Note that on the C++ side this is currently passed to `std::move`
75    /// and therefore may be mutated.
76    #[doc(hidden)]
77    fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut T;
78    #[doc(hidden)]
79    /// Any special drop steps required for the stack storage. This is not
80    /// necessary if the `StackStorage` type is something self-dropping
81    /// such as `UniquePtr`; it's only necessary if it's something where
82    /// manual management is required such as `MaybeUninit`.
83    fn do_drop(_stack: Pin<&mut Self::StackStorage>) {}
84}
85
86unsafe impl<T> ValueParam<T> for &T
87where
88    T: CopyNew,
89{
90    type StackStorage = MaybeUninit<T>;
91
92    unsafe fn populate_stack_space(self, mut stack: Pin<&mut Option<Self::StackStorage>>) {
93        // Safety: we won't move/swap things within the pin.
94        let slot = Pin::into_inner_unchecked(stack.as_mut());
95        *slot = Some(MaybeUninit::uninit());
96        crate::moveit::new::copy(self).new(Pin::new_unchecked(slot.as_mut().unwrap()))
97    }
98    fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut T {
99        // Safety: it's OK to (briefly) create a reference to the T because we
100        // populated it within `populate_stack_space`. It's OK to unpack the pin
101        // because we're not going to move the contents.
102        unsafe { Pin::into_inner_unchecked(stack).assume_init_mut() as *mut T }
103    }
104
105    fn do_drop(stack: Pin<&mut Self::StackStorage>) {
106        // Switch to MaybeUninit::assume_init_drop when stabilized
107        // Safety: per caller guarantees of populate_stack_space, we know this hasn't moved.
108        unsafe { std::ptr::drop_in_place(Pin::into_inner_unchecked(stack).assume_init_mut()) };
109    }
110}
111
112unsafe impl<T> ValueParam<T> for UniquePtr<T>
113where
114    T: UniquePtrTarget,
115{
116    type StackStorage = UniquePtr<T>;
117
118    unsafe fn populate_stack_space(self, mut stack: Pin<&mut Option<Self::StackStorage>>) {
119        // Safety: we will not move the contents of the pin.
120        *Pin::into_inner_unchecked(stack.as_mut()) = Some(self)
121    }
122
123    fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut T {
124        // Safety: we won't move/swap the contents of the outer pin, nor of the
125        // type stored within the UniquePtr.
126        unsafe {
127            (Pin::into_inner_unchecked(
128                (*Pin::into_inner_unchecked(stack))
129                    .as_mut()
130                    .expect("Passed a NULL UniquePtr as a C++ value parameter"),
131            )) as *mut T
132        }
133    }
134}
135
136unsafe impl<T> ValueParam<T> for Pin<Box<T>> {
137    type StackStorage = Pin<Box<T>>;
138
139    unsafe fn populate_stack_space(self, mut stack: Pin<&mut Option<Self::StackStorage>>) {
140        // Safety: we will not move the contents of the pin.
141        *Pin::into_inner_unchecked(stack.as_mut()) = Some(self)
142    }
143
144    fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut T {
145        // Safety: we won't move/swap the contents of the outer pin, nor of the
146        // type stored within the UniquePtr.
147        unsafe {
148            (Pin::into_inner_unchecked((*Pin::into_inner_unchecked(stack)).as_mut())) as *mut T
149        }
150    }
151}
152
153unsafe impl<'a, T: 'a> ValueParam<T> for &'a UniquePtr<T>
154where
155    T: UniquePtrTarget + CopyNew,
156{
157    type StackStorage = <&'a T as ValueParam<T>>::StackStorage;
158
159    unsafe fn populate_stack_space(self, stack: Pin<&mut Option<Self::StackStorage>>) {
160        self.as_ref()
161            .expect("Passed a NULL &UniquePtr as a C++ value parameter")
162            .populate_stack_space(stack)
163    }
164
165    fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut T {
166        <&'a T as ValueParam<T>>::get_ptr(stack)
167    }
168
169    fn do_drop(stack: Pin<&mut Self::StackStorage>) {
170        <&'a T as ValueParam<T>>::do_drop(stack)
171    }
172}
173
174unsafe impl<'a, T: 'a> ValueParam<T> for &'a Pin<Box<T>>
175where
176    T: CopyNew,
177{
178    type StackStorage = <&'a T as ValueParam<T>>::StackStorage;
179
180    unsafe fn populate_stack_space(self, stack: Pin<&mut Option<Self::StackStorage>>) {
181        self.as_ref().get_ref().populate_stack_space(stack)
182    }
183
184    fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut T {
185        <&'a T as ValueParam<T>>::get_ptr(stack)
186    }
187
188    fn do_drop(stack: Pin<&mut Self::StackStorage>) {
189        <&'a T as ValueParam<T>>::do_drop(stack)
190    }
191}
192
193/// Explicitly force a value parameter to be taken using any type of [`crate::moveit::new::New`],
194/// i.e. a constructor.
195pub fn as_new<N: New>(constructor: N) -> impl ValueParam<N::Output> {
196    ByNew(constructor)
197}
198
199/// Explicitly force a value parameter to be taken by copy.
200pub fn as_copy<P: Deref>(ptr: P) -> impl ValueParam<P::Target>
201where
202    P::Target: CopyNew,
203{
204    ByNew(crate::moveit::new::copy(ptr))
205}
206
207/// Explicitly force a value parameter to be taken using C++ move semantics.
208pub fn as_mov<P: AsMove>(ptr: P) -> impl ValueParam<P::Target>
209where
210    P::Target: MoveNew,
211{
212    ByNew(crate::moveit::new::mov(ptr))
213}
214
215#[doc(hidden)]
216pub struct ByNew<N: New>(N);
217
218unsafe impl<N: New> ValueParam<N::Output> for ByNew<N> {
219    type StackStorage = MaybeUninit<N::Output>;
220
221    unsafe fn populate_stack_space(self, mut stack: Pin<&mut Option<Self::StackStorage>>) {
222        // Safety: we won't move/swap things within the pin.
223        let slot = Pin::into_inner_unchecked(stack.as_mut());
224        *slot = Some(MaybeUninit::uninit());
225        self.0.new(Pin::new_unchecked(slot.as_mut().unwrap()))
226    }
227    fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut N::Output {
228        // Safety: it's OK to (briefly) create a reference to the N::Output because we
229        // populated it within `populate_stack_space`. It's OK to unpack the pin
230        // because we're not going to move the contents.
231        unsafe { Pin::into_inner_unchecked(stack).assume_init_mut() as *mut N::Output }
232    }
233
234    fn do_drop(stack: Pin<&mut Self::StackStorage>) {
235        // Switch to MaybeUninit::assume_init_drop when stabilized
236        // Safety: per caller guarantees of populate_stack_space, we know this hasn't moved.
237        unsafe { std::ptr::drop_in_place(Pin::into_inner_unchecked(stack).assume_init_mut()) };
238    }
239}
240
241/// Implementation detail for how we pass value parameters into C++.
242/// This type is instantiated by auto-generated autocxx code each time we
243/// need to pass a value parameter into C++, and will take responsibility
244/// for extracting that value parameter from the [`ValueParam`] and doing
245/// any later cleanup.
246#[doc(hidden)]
247pub struct ValueParamHandler<T, VP: ValueParam<T>> {
248    // We can't populate this on 'new' because the object may move.
249    // Hence this is an Option - it's None until populate is called.
250    space: Option<VP::StackStorage>,
251    _pinned: PhantomPinned,
252}
253
254impl<T, VP: ValueParam<T>> ValueParamHandler<T, VP> {
255    /// Populate this stack space if needs be. Note safety guarantees
256    /// on [`get_ptr`].
257    ///
258    /// # Safety
259    ///
260    /// Callers must call [`populate`] exactly once prior to calling [`get_ptr`].
261    pub unsafe fn populate(self: Pin<&mut Self>, param: VP) {
262        // Structural pinning, as documented in [`std::pin`].
263        param.populate_stack_space(self.map_unchecked_mut(|s| &mut s.space))
264    }
265
266    /// Return a pointer to the underlying value which can be passed to C++.
267    ///
268    /// Per the unsafety contract of [`populate`], [`populate`] has been called exactly once
269    /// prior to this call.
270    pub fn get_ptr(self: Pin<&mut Self>) -> *mut T {
271        // Structural pinning, as documented in [`std::pin`]. `map_unchecked_mut` doesn't play
272        // nicely with `unwrap`, so we have to do it manually.
273        unsafe {
274            VP::get_ptr(Pin::new_unchecked(
275                self.get_unchecked_mut().space.as_mut().unwrap(),
276            ))
277        }
278    }
279}
280
281impl<T, VP: ValueParam<T>> Default for ValueParamHandler<T, VP> {
282    fn default() -> Self {
283        Self {
284            space: None,
285            _pinned: PhantomPinned,
286        }
287    }
288}
289
290impl<T, VP: ValueParam<T>> Drop for ValueParamHandler<T, VP> {
291    fn drop(&mut self) {
292        if let Some(space) = self.space.as_mut() {
293            unsafe { VP::do_drop(Pin::new_unchecked(space)) }
294        }
295    }
296}