blob: 4acc52e091b2cb1fa4a678b991685201c116f351 [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
9//! It would be highly desirable to share a lot of this code with `value_param.rs`
10//! but this proves to be surprisingly fiddly.
11
12use cxx::{memory::UniquePtrTarget, UniquePtr};
13use moveit::MoveRef;
14use std::{
15 marker::{PhantomData, PhantomPinned},
16 pin::Pin,
17};
18
19/// A trait representing a parameter to a C++ function which is received
20/// by rvalue (i.e. by move).
21///
22/// # Panics
23///
24/// The implementations of this trait which take a [`cxx::UniquePtr`] will
25/// panic if the pointer is NULL.
26///
27/// # Safety
28///
29/// Implementers must guarantee that the pointer returned by `get_ptr`
30/// is of the correct size and alignment of `T`.
31pub unsafe trait RValueParam<T>: Sized {
32 /// Retrieve the pointer to the underlying item, to be passed to C++.
33 /// Note that on the C++ side this is currently passed to `std::move`
34 /// and therefore may be mutated.
35 #[doc(hidden)]
36 fn get_ptr(stack: Pin<&mut Self>) -> *mut T;
37}
38
39unsafe impl<T> RValueParam<T> for UniquePtr<T>
40where
41 T: UniquePtrTarget,
42{
43 fn get_ptr(stack: Pin<&mut Self>) -> *mut T {
44 // Safety: we won't move/swap the contents of the outer pin, nor of the
45 // type stored within the UniquePtr.
46 unsafe {
47 (Pin::into_inner_unchecked(
48 (*Pin::into_inner_unchecked(stack))
49 .as_mut()
50 .expect("Passed a NULL UniquePtr as a C++ rvalue parameter"),
51 )) as *mut T
52 }
53 }
54}
55
56unsafe impl<T> RValueParam<T> for Pin<Box<T>> {
57 fn get_ptr(stack: Pin<&mut Self>) -> *mut T {
58 // Safety: we won't move/swap the contents of the outer pin, nor of the
59 // type stored within the UniquePtr.
60 unsafe {
61 (Pin::into_inner_unchecked((*Pin::into_inner_unchecked(stack)).as_mut())) as *mut T
62 }
63 }
64}
65
66unsafe impl<'a, T> RValueParam<T> for Pin<MoveRef<'a, T>> {
67 fn get_ptr(stack: Pin<&mut Self>) -> *mut T {
68 // Safety: we won't move/swap the contents of the outer pin, nor of the
69 // type stored within the UniquePtr.
70 unsafe {
71 (Pin::into_inner_unchecked((*Pin::into_inner_unchecked(stack)).as_mut())) as *mut T
72 }
73 }
74}
75
76/// Implementation detail for how we pass rvalue parameters into C++.
77/// This type is instantiated by auto-generated autocxx code each time we
78/// need to pass a value parameter into C++, and will take responsibility
79/// for extracting that value parameter from the [`RValueParam`] and doing
80/// any later cleanup.
81#[doc(hidden)]
82pub struct RValueParamHandler<T, RVP>
83where
84 RVP: RValueParam<T>,
85{
86 // We can't populate this on 'new' because the object may move.
87 // Hence this is an Option - it's None until populate is called.
88 space: Option<RVP>,
89 _pinned: PhantomPinned,
90 _data: PhantomData<T>,
91}
92
93impl<T, RVP: RValueParam<T>> RValueParamHandler<T, RVP> {
94 /// Populate this stack space if needs be. Note safety guarantees
95 /// on [`get_ptr`].
96 ///
97 /// # Safety
98 ///
99 /// Callers must guarantee that this type will not move
100 /// in memory between calls to [`populate`] and [`get_ptr`].
101 /// Callers must call [`populate`] exactly once prior to calling [`get_ptr`].
102 pub unsafe fn populate(self: Pin<&mut Self>, param: RVP) {
103 // Structural pinning, as documented in [`std::pin`].
104 // Safety: we will not move the contents of the pin.
105 *Pin::into_inner_unchecked(self.map_unchecked_mut(|s| &mut s.space)) = Some(param)
106 }
107
108 /// Return a pointer to the underlying value which can be passed to C++.
109 /// Per the unsafety contract of [`populate`], the object must not have moved
110 /// since it was created, and [`populate`] has been called exactly once
111 /// prior to this call.
112 pub fn get_ptr(self: Pin<&mut Self>) -> *mut T {
113 // Structural pinning, as documented in [`std::pin`]. `map_unchecked_mut` doesn't play
114 // nicely with `unwrap`, so we have to do it manually.
115 unsafe {
116 RVP::get_ptr(Pin::new_unchecked(
117 self.get_unchecked_mut().space.as_mut().unwrap(),
118 ))
119 }
120 }
121}
122
123impl<T, VP: RValueParam<T>> Default for RValueParamHandler<T, VP> {
124 fn default() -> Self {
125 Self {
126 space: None,
127 _pinned: PhantomPinned,
128 _data: PhantomData,
129 }
130 }
131}