autocxx_idalib/reference_wrapper.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 core::{marker::PhantomData, ops::Deref, pin::Pin};
10
11use std::ops::DerefMut;
12#[cfg(nightly)]
13use std::{marker::Unsize, ops::DispatchFromDyn, ops::Receiver};
14
15use cxx::{memory::UniquePtrTarget, UniquePtr};
16
17/// A C++ const reference. These are different from Rust's `&T` in that
18/// these may exist even while the object is mutated elsewhere. See also
19/// [`CppMutRef`] for the mutable equivalent.
20///
21/// The key rule is: we *never* dereference these in Rust. Therefore, any
22/// UB here cannot manifest within Rust, but only across in C++, and therefore
23/// they are equivalently safe to using C++ references in pure-C++ codebases.
24///
25/// *Important*: you might be wondering why you've never encountered this type.
26/// These exist in autocxx-generated bindings only if the `unsafe_references_wrapped`
27/// safety policy is given. This may become the default in future.
28///
29/// # Usage
30///
31/// These types of references are pretty useless in Rust. You can't do
32/// field access. But, you can pass them back into C++! And specifically,
33/// you can call methods on them (i.e. use this type as a `this`). So
34/// the common case here is when C++ gives you a reference to some type,
35/// then you want to call methods on that reference.
36///
37/// # Calling methods
38///
39/// As noted, one of the main reasons for this type is to call methods.
40/// Currently, that depends on unstable Rust features. If you can't
41/// call methods on one of these references, check you're using nightly
42/// and add `#![feature(arbitrary_self_types)]` to your crate.
43///
44/// # Lifetimes
45///
46/// A `CppRef` is not associated with any Rust lifetime. Normally, for
47/// ergonomics, you actually may want a lifetime associated.
48/// [`CppLtRef`] gives you this.
49///
50/// # Field access
51///
52/// Field access would be achieved by adding C++ `get` and/or `set` methods.
53/// It's possible that a future version of `autocxx` could generate such
54/// getters and setters automatically, but they would need to be `unsafe`
55/// because there is no guarantee that the referent of a `CppRef` is actually
56/// what it's supposed to be, or alive. `CppRef`s may flow from C++ to Rust
57/// via arbitrary means, and with sufficient uses of `get` and `set` it would
58/// even be possible to create a use-after-free in pure Rust code (for instance,
59/// store a [`CppPin`] in a struct field, get a `CppRef` to its referent, then
60/// use a setter to reset that field of the struct.)
61///
62/// # Nullness
63///
64/// Creation of a null C++ reference is undefined behavior (because such
65/// a reference can only be created by dereferencing a null pointer.)
66/// However, in practice, they exist, and we need to be compatible with
67/// pre-existing C++ APIs even if they do naughty things like this.
68/// Therefore this `CppRef` type does allow null values. This is a bit
69/// unfortunate because it means `Option<CppRef<T>>`
70/// occupies more space than `CppRef<T>`.
71///
72/// # Dynamic dispatch
73///
74/// You might wonder if you can do this:
75/// ```ignore
76/// let CppRef<dyn Trait> = ...; // obtain some CppRef<concrete type>
77/// ```
78/// Dynamic dispatch works so long as you're using nightly (we require another
79/// unstable feature, `dispatch_from_dyn`). But we need somewhere to store
80/// the trait object, and `CppRef` isn't it -- a `CppRef` can only store a
81/// simple pointer to something else. So, you need to store the trait object
82/// in a `Box` or similar:
83/// ```ignore
84/// trait SomeTrait {
85/// fn some_method(self: CppRef<Self>)
86/// }
87/// impl SomeTrait for ffi::Concrete {
88/// fn some_method(self: CppRef<Self>) {}
89/// }
90/// let obj: Pin<Box<dyn SomeTrait>> = ffi::Concrete::new().within_box();
91/// let obj = CppPin::from_pinned_box(obj);
92/// farm_area.as_cpp_ref().some_method();
93/// ```
94///
95/// # Implementation notes
96///
97/// Internally, this is represented as a raw pointer in Rust. See the note above
98/// about Nullness for why we don't use [`core::ptr::NonNull`].
99#[repr(transparent)]
100pub struct CppRef<T: ?Sized>(*const T);
101
102impl<T: ?Sized> CppRef<T> {
103 /// Retrieve the underlying C++ pointer.
104 pub fn as_ptr(&self) -> *const T {
105 self.0
106 }
107
108 /// Get a regular Rust reference out of this C++ reference.
109 ///
110 /// # Safety
111 ///
112 /// Callers must guarantee that the referent is not modified by any other
113 /// C++ or Rust code while the returned reference exists. Callers must
114 /// also guarantee that no mutable Rust reference is created to the
115 /// referent while the returned reference exists.
116 ///
117 /// Callers must also be sure that the C++ reference is properly
118 /// aligned, not null, pointing to valid data, etc.
119 pub unsafe fn as_ref(&self) -> &T {
120 &*self.as_ptr()
121 }
122
123 /// Create a C++ reference from a raw pointer.
124 pub fn from_ptr(ptr: *const T) -> Self {
125 Self(ptr)
126 }
127
128 /// Create a mutable version of this reference, roughly equivalent
129 /// to C++ `const_cast`.
130 ///
131 /// The opposite is to use [`AsCppRef::as_cpp_ref`] on a [`CppMutRef`]
132 /// to obtain a [`CppRef`].
133 ///
134 /// # Safety
135 ///
136 /// Because we never dereference a `CppRef` in Rust, this cannot create
137 /// undefined behavior _within Rust_ and is therefore not unsafe. It is
138 /// however generally unwise, just as it is in C++. Use sparingly.
139 pub fn const_cast(&self) -> CppMutRef<T> {
140 CppMutRef(self.0 as *mut T)
141 }
142}
143
144#[cfg(nightly)]
145impl<T: ?Sized> Receiver for CppRef<T> {
146 type Target = T;
147}
148
149impl<T: ?Sized> Clone for CppRef<T> {
150 fn clone(&self) -> Self {
151 *self
152 }
153}
154
155impl<T: ?Sized> Copy for CppRef<T> {}
156
157#[cfg(nightly)]
158impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<CppRef<U>> for CppRef<T> {}
159
160/// A [`CppRef`] with an associated lifetime. This can be used in place of
161/// any `CppRef` due to a `Deref` implementation.
162#[repr(transparent)]
163pub struct CppLtRef<'a, T: ?Sized> {
164 ptr: CppRef<T>,
165 phantom: PhantomData<&'a T>,
166}
167
168impl<T: ?Sized> Deref for CppLtRef<'_, T> {
169 type Target = CppRef<T>;
170 fn deref(&self) -> &Self::Target {
171 // Safety: this type is transparent and contains a CppRef<T> as
172 // its only non-zero field.
173 unsafe { std::mem::transmute(self) }
174 }
175}
176
177impl<T: ?Sized> Clone for CppLtRef<'_, T> {
178 fn clone(&self) -> Self {
179 *self
180 }
181}
182
183impl<T: ?Sized> Copy for CppLtRef<'_, T> {}
184
185impl<T: ?Sized> CppLtRef<'_, T> {
186 /// Extend the lifetime of the returned reference beyond normal Rust
187 /// borrow checker rules.
188 ///
189 /// Normally, a reference can't be used beyond the lifetime of the object
190 /// which gave it to you, but sometimes C++ APIs can return references
191 /// to global or other longer-lived objects. In such a case you should
192 /// use this method to get a longer-lived reference.
193 ///
194 /// # Usage
195 ///
196 /// When you're given a C++ reference and you know its referent is valid
197 /// for a long time, use this method. Store the resulting `CppRef`
198 /// somewhere in Rust with an equivalent lifetime.
199 ///
200 /// # Safety
201 ///
202 /// Because `CppRef`s are never dereferenced in Rust, misuse of this API
203 /// cannot lead to undefined behavior _in Rust_ and is therefore not
204 /// unsafe. Nevertheless this can lead to UB in C++, so use carefully.
205 pub fn lifetime_cast(&self) -> CppRef<T> {
206 CppRef(self.ptr.as_ptr())
207 }
208
209 /// Create a C++ reference from a raw pointer.
210 pub fn from_ptr(ptr: *const T) -> Self {
211 Self {
212 ptr: CppRef::from_ptr(ptr),
213 phantom: PhantomData,
214 }
215 }
216}
217
218/// A C++ non-const reference. These are different from Rust's `&mut T` in that
219/// several C++ references can exist to the same underlying data ("aliasing")
220/// and that's not permitted for regular Rust references.
221///
222/// See [`CppRef`] for details on safety, usage models and implementation.
223///
224/// You can convert this to a [`CppRef`] using the [`std::convert::Into`] trait.
225#[repr(transparent)]
226pub struct CppMutRef<T: ?Sized>(*mut T);
227
228impl<T: ?Sized> CppMutRef<T> {
229 /// Retrieve the underlying C++ pointer.
230 pub fn as_mut_ptr(&self) -> *mut T {
231 self.0
232 }
233
234 /// Get a regular Rust mutable reference out of this C++ reference.
235 ///
236 /// # Safety
237 ///
238 /// Callers must guarantee that the referent is not modified by any other
239 /// C++ or Rust code while the returned reference exists. Callers must
240 /// also guarantee that no other Rust reference is created to the referent
241 /// while the returned reference exists.
242 ///
243 /// Callers must also be sure that the C++ reference is properly
244 /// aligned, not null, pointing to valid data, etc.
245 pub unsafe fn as_mut(&mut self) -> &mut T {
246 &mut *self.as_mut_ptr()
247 }
248
249 /// Create a C++ reference from a raw pointer.
250 pub fn from_ptr(ptr: *mut T) -> Self {
251 Self(ptr)
252 }
253}
254
255/// We implement `Deref` for `CppMutRef` so that any non-mutable
256/// methods can be called on a `CppMutRef` instance.
257impl<T: ?Sized> Deref for CppMutRef<T> {
258 type Target = CppRef<T>;
259 #[inline]
260 fn deref(&self) -> &Self::Target {
261 // Safety: `CppMutRef<T>` and `CppRef<T>` have the same
262 // layout.
263 unsafe { std::mem::transmute(self) }
264 }
265}
266
267impl<T: ?Sized> Clone for CppMutRef<T> {
268 fn clone(&self) -> Self {
269 *self
270 }
271}
272
273impl<T: ?Sized> Copy for CppMutRef<T> {}
274
275impl<T> From<CppMutRef<T>> for CppRef<T> {
276 fn from(mutable: CppMutRef<T>) -> Self {
277 Self(mutable.0)
278 }
279}
280
281#[repr(transparent)]
282pub struct CppMutLtRef<'a, T: ?Sized> {
283 ptr: CppMutRef<T>,
284 phantom: PhantomData<&'a mut T>,
285}
286
287impl<T: ?Sized> CppMutLtRef<'_, T> {
288 /// Extend the lifetime of the returned reference beyond normal Rust
289 /// borrow checker rules. See [`CppLtRef::lifetime_cast`].
290 pub fn lifetime_cast(&mut self) -> CppMutRef<T> {
291 CppMutRef(self.ptr.as_mut_ptr())
292 }
293
294 /// Create a C++ reference from a raw pointer.
295 pub fn from_ptr(ptr: *mut T) -> Self {
296 Self {
297 ptr: CppMutRef::from_ptr(ptr),
298 phantom: PhantomData,
299 }
300 }
301}
302
303#[cfg(nightly)]
304impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<CppMutRef<U>> for CppMutRef<T> {}
305
306/// Any type which can return a C++ reference to its contents.
307pub trait AsCppRef<T: ?Sized> {
308 /// Returns a reference which obeys C++ reference semantics
309 fn as_cpp_ref(&self) -> CppRef<T>;
310}
311
312/// Any type which can return a C++ reference to its contents.
313pub trait AsCppMutRef<T: ?Sized>: AsCppRef<T> {
314 /// Returns a mutable reference which obeys C++ reference semantics
315 fn as_cpp_mut_ref(&mut self) -> CppMutRef<T>;
316}
317
318impl<T: ?Sized> AsCppRef<T> for CppMutRef<T> {
319 fn as_cpp_ref(&self) -> CppRef<T> {
320 CppRef::from_ptr(self.0 as *const T)
321 }
322}
323
324/// Workaround for the inability to use std::ptr::addr_of! on the contents
325/// of a box.
326#[repr(transparent)]
327struct CppPinContents<T: ?Sized>(T);
328
329impl<T: ?Sized> CppPinContents<T> {
330 fn addr_of(&self) -> *const T {
331 std::ptr::addr_of!(self.0)
332 }
333 fn addr_of_mut(&mut self) -> *mut T {
334 std::ptr::addr_of_mut!(self.0)
335 }
336}
337
338/// A newtype wrapper which causes the contained object to obey C++ reference
339/// semantics rather than Rust reference semantics. That is, multiple aliasing
340/// mutable C++ references may exist to the contents.
341///
342/// C++ references are permitted to alias one another, and commonly do.
343/// Rust references must alias according only to the narrow rules of the
344/// borrow checker.
345///
346/// If you need C++ to access your Rust object, first imprison it in one of these
347/// objects, then use [`Self::as_cpp_ref`] to obtain C++ references to it.
348/// If you need the object back for use in the Rust domain, use [`CppPin::extract`],
349/// but be aware of the safety invariants that you - as a human - will need
350/// to guarantee.
351///
352/// # Usage models
353///
354/// From fairly safe to fairly unsafe:
355///
356/// * *Configure a thing in Rust then give it to C++*. Take your Rust object,
357/// set it up freely using Rust references, methods and data, then imprison
358/// it in a `CppPin` and keep it around while you work with it in C++.
359/// There is no possibility of _aliasing_ UB in this usage model, but you
360/// still need to be careful of use-after-free bugs, just as if you were
361/// to create a reference to any data in C++. The Rust borrow checker will
362/// help you a little by ensuring that your `CppRef` objects don't outlive
363/// the `CppPin`, but once those references pass into C++, it can't help.
364/// * *Pass a thing to C++, have it operate on it synchronously, then take
365/// it back*. To do this, you'd imprison your Rust object in a `CppPin`,
366/// then pass mutable C++ references (using [`AsCppMutRef::as_cpp_mut_ref`])
367/// into a C++ function. C++ would duly operate on the object, and thereafter
368/// you could reclaim the object with `extract()`. At this point, you (as
369/// a human) will need to give a guarantee that no references remain in the
370/// C++ domain. If your object was just locally used by a single C++ function,
371/// which has now returned, this type of local analysis may well be practical.
372/// * *Share a thing between Rust and C++*. This object can vend both C++
373/// references and Rust references (via `as_ref` etc.) It may be possible
374/// for you to guarantee that C++ does not mutate the object while any Rust
375/// reference exists. If you choose this model, you'll need to carefully
376/// track exactly what happens to references and pointers on both sides,
377/// and document your evidence for why you are sure this is safe.
378/// Failure here is bad: Rust makes all sorts of optimization decisions based
379/// upon its borrow checker guarantees, so mistakes can lead to undebuggable
380/// action-at-a-distance crashes.
381///
382/// # See also
383///
384/// See also [`CppUniquePtrPin`], which is equivalent for data which is in
385/// a [`cxx::UniquePtr`].
386// We also keep a `CppMutRef` to the contents for the sake of our `Deref`
387// implementation.
388pub struct CppPin<T: ?Sized>(Box<CppPinContents<T>>, CppMutRef<T>);
389
390impl<T: ?Sized> CppPin<T> {
391 /// Imprison the Rust data within a `CppPin`. This eliminates any remaining
392 /// Rust references (since we take the item by value) and this object
393 /// subsequently only vends C++ style references, not Rust references,
394 /// until or unless `extract` is called.
395 pub fn new(item: T) -> Self
396 where
397 T: Sized,
398 {
399 let mut contents = Box::new(CppPinContents(item));
400 let ptr = contents.addr_of_mut();
401 Self(contents, CppMutRef(ptr))
402 }
403
404 /// Imprison the boxed Rust data within a `CppPin`. This eliminates any remaining
405 /// Rust references (since we take the item by value) and this object
406 /// subsequently only vends C++ style references, not Rust references,
407 /// until or unless `extract` is called.
408 ///
409 /// If the item is already in a `Box`, this is slightly more efficient than
410 /// `new` because it will avoid moving/reallocating it.
411 pub fn from_box(item: Box<T>) -> Self {
412 // Safety: CppPinContents<T> is #[repr(transparent)] so
413 // this transmute from
414 // Box<T>
415 // to
416 // Box<CppPinContents<T>>
417 // is safe.
418 let mut contents = unsafe { std::mem::transmute::<Box<T>, Box<CppPinContents<T>>>(item) };
419 let ptr = contents.addr_of_mut();
420 Self(contents, CppMutRef(ptr))
421 }
422
423 // Imprison the boxed Rust data within a `CppPin`. This eliminates any remaining
424 /// Rust references (since we take the item by value) and this object
425 /// subsequently only vends C++ style references, not Rust references,
426 /// until or unless `extract` is called.
427 ///
428 /// If the item is already in a `Box`, this is slightly more efficient than
429 /// `new` because it will avoid moving/reallocating it.
430 pub fn from_pinned_box(item: Pin<Box<T>>) -> Self {
431 // Safety: it's OK to un-pin the Box because we'll be putting it
432 // into a CppPin which upholds the same pinned-ness contract.
433 Self::from_box(unsafe { Pin::into_inner_unchecked(item) })
434 }
435
436 /// Get an immutable pointer to the underlying object.
437 pub fn as_ptr(&self) -> *const T {
438 self.0.addr_of()
439 }
440
441 /// Get a mutable pointer to the underlying object.
442 pub fn as_mut_ptr(&mut self) -> *mut T {
443 self.0.addr_of_mut()
444 }
445
446 /// Get a normal Rust reference to the underlying object. This is unsafe.
447 ///
448 /// # Safety
449 ///
450 /// You must guarantee that C++ will not mutate the object while the
451 /// reference exists.
452 pub unsafe fn as_ref(&self) -> &T {
453 &*self.as_ptr()
454 }
455
456 /// Get a normal Rust mutable reference to the underlying object. This is unsafe.
457 ///
458 /// # Safety
459 ///
460 /// You must guarantee that C++ will not mutate the object while the
461 /// reference exists.
462 pub unsafe fn as_mut(&mut self) -> &mut T {
463 &mut *self.as_mut_ptr()
464 }
465
466 /// Extract the object from within its prison, for re-use again within
467 /// the domain of normal Rust references.
468 ///
469 /// This returns a `Box<T>`: if you want the underlying `T` you can extract
470 /// it using `*`.
471 ///
472 /// # Safety
473 ///
474 /// Callers promise that no remaining C++ references exist either
475 /// in the form of Rust [`CppRef`]/[`CppMutRef`] or any remaining pointers/
476 /// references within C++.
477 pub unsafe fn extract(self) -> Box<T> {
478 // Safety: CppPinContents<T> is #[repr(transparent)] so
479 // this transmute from
480 // Box<CppPinContents<T>>
481 // to
482 // Box<T>
483 // is safe.
484 std::mem::transmute(self.0)
485 }
486}
487
488impl<T: ?Sized> AsCppRef<T> for CppPin<T> {
489 fn as_cpp_ref(&self) -> CppRef<T> {
490 CppRef::from_ptr(self.as_ptr())
491 }
492}
493
494impl<T: ?Sized> AsCppMutRef<T> for CppPin<T> {
495 fn as_cpp_mut_ref(&mut self) -> CppMutRef<T> {
496 CppMutRef::from_ptr(self.as_mut_ptr())
497 }
498}
499
500impl<T: ?Sized> Deref for CppPin<T> {
501 type Target = CppMutRef<T>;
502
503 fn deref(&self) -> &Self::Target {
504 &self.1
505 }
506}
507
508impl<T: ?Sized> DerefMut for CppPin<T> {
509 fn deref_mut(&mut self) -> &mut Self::Target {
510 &mut self.1
511 }
512}
513
514/// Any newtype wrapper which causes the contained [`UniquePtr`] target to obey C++ reference
515/// semantics rather than Rust reference semantics. That is, multiple aliasing
516/// mutable C++ references may exist to the contents.
517///
518/// C++ references are permitted to alias one another, and commonly do.
519/// Rust references must alias according only to the narrow rules of the
520/// borrow checker.
521pub struct CppUniquePtrPin<T: UniquePtrTarget>(UniquePtr<T>, CppMutRef<T>);
522
523impl<T: UniquePtrTarget> CppUniquePtrPin<T> {
524 /// Imprison the type within a `CppPin`. This eliminates any remaining
525 /// Rust references (since we take the item by value) and this object
526 /// subsequently only vends C++ style references, not Rust references.
527 pub fn new(item: UniquePtr<T>) -> Self {
528 let ptr = item.as_mut_ptr();
529 Self(item, CppMutRef::from_ptr(ptr))
530 }
531
532 /// Get an immutable pointer to the underlying object.
533 pub fn as_ptr(&self) -> *const T {
534 // TODO - avoid brief reference here
535 self.0
536 .as_ref()
537 .expect("UniquePtr was null; we can't make a C++ reference")
538 }
539}
540
541impl<T: UniquePtrTarget> AsCppRef<T> for CppUniquePtrPin<T> {
542 fn as_cpp_ref(&self) -> CppRef<T> {
543 CppRef::from_ptr(self.as_ptr())
544 }
545}
546
547impl<T: UniquePtrTarget> AsCppMutRef<T> for CppUniquePtrPin<T> {
548 fn as_cpp_mut_ref(&mut self) -> CppMutRef<T> {
549 self.1
550 }
551}
552
553impl<T: UniquePtrTarget> Deref for CppUniquePtrPin<T> {
554 type Target = CppMutRef<T>;
555
556 fn deref(&self) -> &Self::Target {
557 &self.1
558 }
559}
560
561// It would be very nice to be able to impl Deref for UniquePtr
562impl<T: UniquePtrTarget> AsCppRef<T> for cxx::UniquePtr<T> {
563 fn as_cpp_ref(&self) -> CppRef<T> {
564 CppRef::from_ptr(self.as_ptr())
565 }
566}
567
568#[cfg(all(feature = "arbitrary_self_types", test))]
569mod tests {
570 use super::*;
571
572 struct CppOuter {
573 _a: u32,
574 inner: CppInner,
575 global: *const CppInner,
576 }
577
578 impl CppOuter {
579 fn get_inner_ref<'a>(self: &CppRef<'a, CppOuter>) -> CppRef<'a, CppInner> {
580 // Safety: emulating C++ code for test purposes. This is safe
581 // because we know the data isn't modified during the lifetime of
582 // the returned reference.
583 let self_rust_ref = unsafe { self.as_ref() };
584 CppRef::from_ptr(std::ptr::addr_of!(self_rust_ref.inner))
585 }
586 fn get_global_ref<'a>(self: &CppRef<'a, CppOuter>) -> CppRef<'a, CppInner> {
587 // Safety: emulating C++ code for test purposes. This is safe
588 // because we know the data isn't modified during the lifetime of
589 // the returned reference.
590 let self_rust_ref = unsafe { self.as_ref() };
591 CppRef::from_ptr(self_rust_ref.global)
592 }
593 }
594
595 struct CppInner {
596 b: u32,
597 }
598
599 impl CppInner {
600 fn value_is(self: &CppRef<Self>) -> u32 {
601 // Safety: emulating C++ code for test purposes. This is safe
602 // because we know the data isn't modified during the lifetime of
603 // the returned reference.
604 let self_rust_ref = unsafe { self.as_ref() };
605 self_rust_ref.b
606 }
607 }
608
609 #[test]
610 fn cpp_objects() {
611 let mut global = CppInner { b: 7 };
612 let global_ref_lifetime_phantom;
613 {
614 let outer = CppOuter {
615 _a: 12,
616 inner: CppInner { b: 3 },
617 global: &mut global,
618 };
619 let outer = CppPin::new(outer);
620 let inner_ref = outer.as_cpp_ref().get_inner_ref();
621 assert_eq!(inner_ref.value_is(), 3);
622 global_ref_lifetime_phantom = Some(outer.as_cpp_ref().get_global_ref().lifetime_cast());
623 }
624 let global_ref = global_ref_lifetime_phantom.unwrap();
625 let global_ref = global_ref.as_cpp_ref();
626 assert_eq!(global_ref.value_is(), 7);
627 }
628
629 #[test]
630 fn cpp_pin() {
631 let a = RustThing { _a: 4 };
632 let a = CppPin::new(a);
633 let _ = a.as_cpp_ref();
634 let _ = a.as_cpp_ref();
635 }
636}