Skip to main content

autocxx_idalib/
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
112// TODO implement for CppPin<T> and for CppRef<T: CopyNew>
113
114unsafe impl<T> ValueParam<T> for UniquePtr<T>
115where
116    T: UniquePtrTarget,
117{
118    type StackStorage = UniquePtr<T>;
119
120    unsafe fn populate_stack_space(self, mut stack: Pin<&mut Option<Self::StackStorage>>) {
121        // Safety: we will not move the contents of the pin.
122        *Pin::into_inner_unchecked(stack.as_mut()) = Some(self)
123    }
124
125    fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut T {
126        // Safety: we won't move/swap the contents of the outer pin, nor of the
127        // type stored within the UniquePtr.
128        unsafe {
129            (Pin::into_inner_unchecked(
130                (*Pin::into_inner_unchecked(stack))
131                    .as_mut()
132                    .expect("Passed a NULL UniquePtr as a C++ value parameter"),
133            )) as *mut T
134        }
135    }
136}
137
138unsafe impl<T> ValueParam<T> for Pin<Box<T>> {
139    type StackStorage = Pin<Box<T>>;
140
141    unsafe fn populate_stack_space(self, mut stack: Pin<&mut Option<Self::StackStorage>>) {
142        // Safety: we will not move the contents of the pin.
143        *Pin::into_inner_unchecked(stack.as_mut()) = Some(self)
144    }
145
146    fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut T {
147        // Safety: we won't move/swap the contents of the outer pin, nor of the
148        // type stored within the UniquePtr.
149        unsafe {
150            (Pin::into_inner_unchecked((*Pin::into_inner_unchecked(stack)).as_mut())) as *mut T
151        }
152    }
153}
154
155unsafe impl<'a, T: 'a> ValueParam<T> for &'a UniquePtr<T>
156where
157    T: UniquePtrTarget + CopyNew,
158{
159    type StackStorage = <&'a T as ValueParam<T>>::StackStorage;
160
161    unsafe fn populate_stack_space(self, stack: Pin<&mut Option<Self::StackStorage>>) {
162        self.as_ref()
163            .expect("Passed a NULL &UniquePtr as a C++ value parameter")
164            .populate_stack_space(stack)
165    }
166
167    fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut T {
168        <&'a T as ValueParam<T>>::get_ptr(stack)
169    }
170
171    fn do_drop(stack: Pin<&mut Self::StackStorage>) {
172        <&'a T as ValueParam<T>>::do_drop(stack)
173    }
174}
175
176unsafe impl<'a, T: 'a> ValueParam<T> for &'a Pin<Box<T>>
177where
178    T: CopyNew,
179{
180    type StackStorage = <&'a T as ValueParam<T>>::StackStorage;
181
182    unsafe fn populate_stack_space(self, stack: Pin<&mut Option<Self::StackStorage>>) {
183        self.as_ref().get_ref().populate_stack_space(stack)
184    }
185
186    fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut T {
187        <&'a T as ValueParam<T>>::get_ptr(stack)
188    }
189
190    fn do_drop(stack: Pin<&mut Self::StackStorage>) {
191        <&'a T as ValueParam<T>>::do_drop(stack)
192    }
193}
194
195/// Explicitly force a value parameter to be taken using any type of [`crate::moveit::new::New`],
196/// i.e. a constructor.
197pub fn as_new<N: New>(constructor: N) -> impl ValueParam<N::Output> {
198    ByNew(constructor)
199}
200
201/// Explicitly force a value parameter to be taken by copy.
202pub fn as_copy<P: Deref>(ptr: P) -> impl ValueParam<P::Target>
203where
204    P::Target: CopyNew,
205{
206    ByNew(crate::moveit::new::copy(ptr))
207}
208
209/// Explicitly force a value parameter to be taken using C++ move semantics.
210pub fn as_mov<P: AsMove>(ptr: P) -> impl ValueParam<P::Target>
211where
212    P::Target: MoveNew,
213{
214    ByNew(crate::moveit::new::mov(ptr))
215}
216
217#[doc(hidden)]
218pub struct ByNew<N: New>(N);
219
220unsafe impl<N: New> ValueParam<N::Output> for ByNew<N> {
221    type StackStorage = MaybeUninit<N::Output>;
222
223    unsafe fn populate_stack_space(self, mut stack: Pin<&mut Option<Self::StackStorage>>) {
224        // Safety: we won't move/swap things within the pin.
225        let slot = Pin::into_inner_unchecked(stack.as_mut());
226        *slot = Some(MaybeUninit::uninit());
227        self.0.new(Pin::new_unchecked(slot.as_mut().unwrap()))
228    }
229    fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut N::Output {
230        // Safety: it's OK to (briefly) create a reference to the N::Output because we
231        // populated it within `populate_stack_space`. It's OK to unpack the pin
232        // because we're not going to move the contents.
233        unsafe { Pin::into_inner_unchecked(stack).assume_init_mut() as *mut N::Output }
234    }
235
236    fn do_drop(stack: Pin<&mut Self::StackStorage>) {
237        // Switch to MaybeUninit::assume_init_drop when stabilized
238        // Safety: per caller guarantees of populate_stack_space, we know this hasn't moved.
239        unsafe { std::ptr::drop_in_place(Pin::into_inner_unchecked(stack).assume_init_mut()) };
240    }
241}
242
243/// Implementation detail for how we pass value parameters into C++.
244/// This type is instantiated by auto-generated autocxx code each time we
245/// need to pass a value parameter into C++, and will take responsibility
246/// for extracting that value parameter from the [`ValueParam`] and doing
247/// any later cleanup.
248#[doc(hidden)]
249pub struct ValueParamHandler<T, VP: ValueParam<T>> {
250    // We can't populate this on 'new' because the object may move.
251    // Hence this is an Option - it's None until populate is called.
252    space: Option<VP::StackStorage>,
253    _pinned: PhantomPinned,
254}
255
256impl<T, VP: ValueParam<T>> ValueParamHandler<T, VP> {
257    /// Populate this stack space if needs be. Note safety guarantees
258    /// on [`get_ptr`].
259    ///
260    /// # Safety
261    ///
262    /// Callers must call [`populate`] exactly once prior to calling [`get_ptr`].
263    pub unsafe fn populate(self: Pin<&mut Self>, param: VP) {
264        // Structural pinning, as documented in [`std::pin`].
265        param.populate_stack_space(self.map_unchecked_mut(|s| &mut s.space))
266    }
267
268    /// Return a pointer to the underlying value which can be passed to C++.
269    ///
270    /// Per the unsafety contract of [`populate`], [`populate`] has been called exactly once
271    /// prior to this call.
272    pub fn get_ptr(self: Pin<&mut Self>) -> *mut T {
273        // Structural pinning, as documented in [`std::pin`]. `map_unchecked_mut` doesn't play
274        // nicely with `unwrap`, so we have to do it manually.
275        unsafe {
276            VP::get_ptr(Pin::new_unchecked(
277                self.get_unchecked_mut().space.as_mut().unwrap(),
278            ))
279        }
280    }
281}
282
283impl<T, VP: ValueParam<T>> Default for ValueParamHandler<T, VP> {
284    fn default() -> Self {
285        Self {
286            space: None,
287            _pinned: PhantomPinned,
288        }
289    }
290}
291
292impl<T, VP: ValueParam<T>> Drop for ValueParamHandler<T, VP> {
293    fn drop(&mut self) {
294        if let Some(space) = self.space.as_mut() {
295            unsafe { VP::do_drop(Pin::new_unchecked(space)) }
296        }
297    }
298}