blob: 8e564a12a926cb562e0fd0633fe59d740f0cf877 [file] [log] [blame]
Brian Silverman4e662aa2022-05-11 23:10:19 -07001// 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::{CopyNew, DerefMove, 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<Output = T>, T>(constructor: N) -> impl ValueParam<T> {
196 ByNew(constructor)
197}
198
199/// Explicitly force a value parameter to be taken by copy.
200pub fn as_copy<P: Deref<Target = T>, T>(ptr: P) -> impl ValueParam<T>
201where
202 T: CopyNew,
203{
204 ByNew(crate::moveit::new::copy(ptr))
205}
206
207/// Explicitly force a value parameter to be taken usign C++ move semantics.
208pub fn as_mov<P: DerefMove + Deref<Target = T>, T>(ptr: P) -> impl ValueParam<T>
209where
210 P: DerefMove,
211 P::Target: MoveNew,
212{
213 ByNew(crate::moveit::new::mov(ptr))
214}
215
216#[doc(hidden)]
217pub struct ByNew<N: New>(N);
218
219unsafe impl<N, T> ValueParam<T> for ByNew<N>
220where
221 N: New<Output = T>,
222{
223 type StackStorage = MaybeUninit<T>;
224
225 unsafe fn populate_stack_space(self, mut stack: Pin<&mut Option<Self::StackStorage>>) {
226 // Safety: we won't move/swap things within the pin.
227 let slot = Pin::into_inner_unchecked(stack.as_mut());
228 *slot = Some(MaybeUninit::uninit());
229 self.0.new(Pin::new_unchecked(slot.as_mut().unwrap()))
230 }
231 fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut T {
232 // Safety: it's OK to (briefly) create a reference to the T because we
233 // populated it within `populate_stack_space`. It's OK to unpack the pin
234 // because we're not going to move the contents.
235 unsafe { Pin::into_inner_unchecked(stack).assume_init_mut() as *mut T }
236 }
237
238 fn do_drop(stack: Pin<&mut Self::StackStorage>) {
239 // Switch to MaybeUninit::assume_init_drop when stabilized
240 // Safety: per caller guarantees of populate_stack_space, we know this hasn't moved.
241 unsafe { std::ptr::drop_in_place(Pin::into_inner_unchecked(stack).assume_init_mut()) };
242 }
243}
244
245/// Implementation detail for how we pass value parameters into C++.
246/// This type is instantiated by auto-generated autocxx code each time we
247/// need to pass a value parameter into C++, and will take responsibility
248/// for extracting that value parameter from the [`ValueParam`] and doing
249/// any later cleanup.
250#[doc(hidden)]
251pub struct ValueParamHandler<T, VP: ValueParam<T>> {
252 // We can't populate this on 'new' because the object may move.
253 // Hence this is an Option - it's None until populate is called.
254 space: Option<VP::StackStorage>,
255 _pinned: PhantomPinned,
256}
257
258impl<T, VP: ValueParam<T>> ValueParamHandler<T, VP> {
259 /// Populate this stack space if needs be. Note safety guarantees
260 /// on [`get_ptr`].
261 ///
262 /// # Safety
263 ///
264 /// Callers must call [`populate`] exactly once prior to calling [`get_ptr`].
265 pub unsafe fn populate(self: Pin<&mut Self>, param: VP) {
266 // Structural pinning, as documented in [`std::pin`].
267 param.populate_stack_space(self.map_unchecked_mut(|s| &mut s.space))
268 }
269
270 /// Return a pointer to the underlying value which can be passed to C++.
271 ///
272 /// Per the unsafety contract of [`populate`], [`populate`] has been called exactly once
273 /// prior to this call.
274 pub fn get_ptr(self: Pin<&mut Self>) -> *mut T {
275 // Structural pinning, as documented in [`std::pin`]. `map_unchecked_mut` doesn't play
276 // nicely with `unwrap`, so we have to do it manually.
277 unsafe {
278 VP::get_ptr(Pin::new_unchecked(
279 self.get_unchecked_mut().space.as_mut().unwrap(),
280 ))
281 }
282 }
283}
284
285impl<T, VP: ValueParam<T>> Default for ValueParamHandler<T, VP> {
286 fn default() -> Self {
287 Self {
288 space: None,
289 _pinned: PhantomPinned,
290 }
291 }
292}
293
294impl<T, VP: ValueParam<T>> Drop for ValueParamHandler<T, VP> {
295 fn drop(&mut self) {
296 if let Some(space) = self.space.as_mut() {
297 unsafe { VP::do_drop(Pin::new_unchecked(space)) }
298 }
299 }
300}