autocxx/
value_param.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use cxx::{memory::UniquePtrTarget, UniquePtr};
use moveit::{AsMove, CopyNew, MoveNew, New};
use std::{marker::PhantomPinned, mem::MaybeUninit, ops::Deref, pin::Pin};

/// A trait representing a parameter to a C++ function which is received
/// by value.
///
/// Rust has the concept of receiving parameters by _move_ or by _reference_.
/// C++ has the concept of receiving a parameter by 'value', which means
/// the parameter gets copied.
///
/// To make it easy to pass such parameters from Rust, this trait exists.
/// It is implemented both for references `&T` and for `UniquePtr<T>`,
/// subject to the presence or absence of suitable copy and move constructors.
/// This allows you to pass in parameters by copy (as is ergonomic and normal
/// in C++) retaining the original parameter; or by move semantics thus
/// destroying the object you're passing in. Simply use a reference if you want
/// copy semantics, or the item itself if you want move semantics.
///
/// It is not recommended that you implement this trait, nor that you directly
/// use its methods, which are for use by `autocxx` generated code only.
///
/// # Use of `moveit` traits
///
/// Most of the implementations of this trait require the type to implement
/// [`CopyNew`], which is simply the `autocxx`/`moveit` way of saying that
/// the type has a copy constructor in C++.
///
/// # Being explicit
///
/// If you wish to explicitly force either a move or a copy of some type,
/// use [`as_mov`] or [`as_copy`].
///
/// # Performance
///
/// At present, some additional copying occurs for all implementations of
/// this trait other than that for [`cxx::UniquePtr`]. In the future it's
/// hoped that the implementation for `&T where T: CopyNew` can also avoid
/// this extra copying.
///
/// # Panics
///
/// The implementations of this trait which take a [`cxx::UniquePtr`] will
/// panic if the pointer is NULL.
///
/// # Safety
///
/// Implementers must guarantee that the pointer returned by `get_ptr`
/// is of the correct size and alignment of `T`.
pub unsafe trait ValueParam<T> {
    /// Any stack storage required. If, as part of passing to C++,
    /// we need to store a temporary copy of the value, this will be `T`,
    /// otherwise `()`.
    #[doc(hidden)]
    type StackStorage;
    /// Populate the stack storage given as a parameter. Only called if you
    /// return `true` from `needs_stack_space`.
    ///
    /// # Safety
    ///
    /// Callers must guarantee that this object will not move in memory
    /// between this call and any subsequent `get_ptr` call or drop.
    #[doc(hidden)]
    unsafe fn populate_stack_space(self, this: Pin<&mut Option<Self::StackStorage>>);
    /// Retrieve the pointer to the underlying item, to be passed to C++.
    /// Note that on the C++ side this is currently passed to `std::move`
    /// and therefore may be mutated.
    #[doc(hidden)]
    fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut T;
    #[doc(hidden)]
    /// Any special drop steps required for the stack storage. This is not
    /// necessary if the `StackStorage` type is something self-dropping
    /// such as `UniquePtr`; it's only necessary if it's something where
    /// manual management is required such as `MaybeUninit`.
    fn do_drop(_stack: Pin<&mut Self::StackStorage>) {}
}

unsafe impl<T> ValueParam<T> for &T
where
    T: CopyNew,
{
    type StackStorage = MaybeUninit<T>;

    unsafe fn populate_stack_space(self, mut stack: Pin<&mut Option<Self::StackStorage>>) {
        // Safety: we won't move/swap things within the pin.
        let slot = Pin::into_inner_unchecked(stack.as_mut());
        *slot = Some(MaybeUninit::uninit());
        crate::moveit::new::copy(self).new(Pin::new_unchecked(slot.as_mut().unwrap()))
    }
    fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut T {
        // Safety: it's OK to (briefly) create a reference to the T because we
        // populated it within `populate_stack_space`. It's OK to unpack the pin
        // because we're not going to move the contents.
        unsafe { Pin::into_inner_unchecked(stack).assume_init_mut() as *mut T }
    }

    fn do_drop(stack: Pin<&mut Self::StackStorage>) {
        // Switch to MaybeUninit::assume_init_drop when stabilized
        // Safety: per caller guarantees of populate_stack_space, we know this hasn't moved.
        unsafe { std::ptr::drop_in_place(Pin::into_inner_unchecked(stack).assume_init_mut()) };
    }
}

unsafe impl<T> ValueParam<T> for UniquePtr<T>
where
    T: UniquePtrTarget,
{
    type StackStorage = UniquePtr<T>;

    unsafe fn populate_stack_space(self, mut stack: Pin<&mut Option<Self::StackStorage>>) {
        // Safety: we will not move the contents of the pin.
        *Pin::into_inner_unchecked(stack.as_mut()) = Some(self)
    }

    fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut T {
        // Safety: we won't move/swap the contents of the outer pin, nor of the
        // type stored within the UniquePtr.
        unsafe {
            (Pin::into_inner_unchecked(
                (*Pin::into_inner_unchecked(stack))
                    .as_mut()
                    .expect("Passed a NULL UniquePtr as a C++ value parameter"),
            )) as *mut T
        }
    }
}

unsafe impl<T> ValueParam<T> for Pin<Box<T>> {
    type StackStorage = Pin<Box<T>>;

    unsafe fn populate_stack_space(self, mut stack: Pin<&mut Option<Self::StackStorage>>) {
        // Safety: we will not move the contents of the pin.
        *Pin::into_inner_unchecked(stack.as_mut()) = Some(self)
    }

    fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut T {
        // Safety: we won't move/swap the contents of the outer pin, nor of the
        // type stored within the UniquePtr.
        unsafe {
            (Pin::into_inner_unchecked((*Pin::into_inner_unchecked(stack)).as_mut())) as *mut T
        }
    }
}

unsafe impl<'a, T: 'a> ValueParam<T> for &'a UniquePtr<T>
where
    T: UniquePtrTarget + CopyNew,
{
    type StackStorage = <&'a T as ValueParam<T>>::StackStorage;

    unsafe fn populate_stack_space(self, stack: Pin<&mut Option<Self::StackStorage>>) {
        self.as_ref()
            .expect("Passed a NULL &UniquePtr as a C++ value parameter")
            .populate_stack_space(stack)
    }

    fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut T {
        <&'a T as ValueParam<T>>::get_ptr(stack)
    }

    fn do_drop(stack: Pin<&mut Self::StackStorage>) {
        <&'a T as ValueParam<T>>::do_drop(stack)
    }
}

unsafe impl<'a, T: 'a> ValueParam<T> for &'a Pin<Box<T>>
where
    T: CopyNew,
{
    type StackStorage = <&'a T as ValueParam<T>>::StackStorage;

    unsafe fn populate_stack_space(self, stack: Pin<&mut Option<Self::StackStorage>>) {
        self.as_ref().get_ref().populate_stack_space(stack)
    }

    fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut T {
        <&'a T as ValueParam<T>>::get_ptr(stack)
    }

    fn do_drop(stack: Pin<&mut Self::StackStorage>) {
        <&'a T as ValueParam<T>>::do_drop(stack)
    }
}

/// Explicitly force a value parameter to be taken using any type of [`crate::moveit::new::New`],
/// i.e. a constructor.
pub fn as_new<N: New>(constructor: N) -> impl ValueParam<N::Output> {
    ByNew(constructor)
}

/// Explicitly force a value parameter to be taken by copy.
pub fn as_copy<P: Deref>(ptr: P) -> impl ValueParam<P::Target>
where
    P::Target: CopyNew,
{
    ByNew(crate::moveit::new::copy(ptr))
}

/// Explicitly force a value parameter to be taken using C++ move semantics.
pub fn as_mov<P: AsMove>(ptr: P) -> impl ValueParam<P::Target>
where
    P::Target: MoveNew,
{
    ByNew(crate::moveit::new::mov(ptr))
}

#[doc(hidden)]
pub struct ByNew<N: New>(N);

unsafe impl<N: New> ValueParam<N::Output> for ByNew<N> {
    type StackStorage = MaybeUninit<N::Output>;

    unsafe fn populate_stack_space(self, mut stack: Pin<&mut Option<Self::StackStorage>>) {
        // Safety: we won't move/swap things within the pin.
        let slot = Pin::into_inner_unchecked(stack.as_mut());
        *slot = Some(MaybeUninit::uninit());
        self.0.new(Pin::new_unchecked(slot.as_mut().unwrap()))
    }
    fn get_ptr(stack: Pin<&mut Self::StackStorage>) -> *mut N::Output {
        // Safety: it's OK to (briefly) create a reference to the N::Output because we
        // populated it within `populate_stack_space`. It's OK to unpack the pin
        // because we're not going to move the contents.
        unsafe { Pin::into_inner_unchecked(stack).assume_init_mut() as *mut N::Output }
    }

    fn do_drop(stack: Pin<&mut Self::StackStorage>) {
        // Switch to MaybeUninit::assume_init_drop when stabilized
        // Safety: per caller guarantees of populate_stack_space, we know this hasn't moved.
        unsafe { std::ptr::drop_in_place(Pin::into_inner_unchecked(stack).assume_init_mut()) };
    }
}

/// Implementation detail for how we pass value parameters into C++.
/// This type is instantiated by auto-generated autocxx code each time we
/// need to pass a value parameter into C++, and will take responsibility
/// for extracting that value parameter from the [`ValueParam`] and doing
/// any later cleanup.
#[doc(hidden)]
pub struct ValueParamHandler<T, VP: ValueParam<T>> {
    // We can't populate this on 'new' because the object may move.
    // Hence this is an Option - it's None until populate is called.
    space: Option<VP::StackStorage>,
    _pinned: PhantomPinned,
}

impl<T, VP: ValueParam<T>> ValueParamHandler<T, VP> {
    /// Populate this stack space if needs be. Note safety guarantees
    /// on [`get_ptr`].
    ///
    /// # Safety
    ///
    /// Callers must call [`populate`] exactly once prior to calling [`get_ptr`].
    pub unsafe fn populate(self: Pin<&mut Self>, param: VP) {
        // Structural pinning, as documented in [`std::pin`].
        param.populate_stack_space(self.map_unchecked_mut(|s| &mut s.space))
    }

    /// Return a pointer to the underlying value which can be passed to C++.
    ///
    /// Per the unsafety contract of [`populate`], [`populate`] has been called exactly once
    /// prior to this call.
    pub fn get_ptr(self: Pin<&mut Self>) -> *mut T {
        // Structural pinning, as documented in [`std::pin`]. `map_unchecked_mut` doesn't play
        // nicely with `unwrap`, so we have to do it manually.
        unsafe {
            VP::get_ptr(Pin::new_unchecked(
                self.get_unchecked_mut().space.as_mut().unwrap(),
            ))
        }
    }
}

impl<T, VP: ValueParam<T>> Default for ValueParamHandler<T, VP> {
    fn default() -> Self {
        Self {
            space: None,
            _pinned: PhantomPinned,
        }
    }
}

impl<T, VP: ValueParam<T>> Drop for ValueParamHandler<T, VP> {
    fn drop(&mut self) {
        if let Some(space) = self.space.as_mut() {
            unsafe { VP::do_drop(Pin::new_unchecked(space)) }
        }
    }
}