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}