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}