@@ -19,47 +19,61 @@ pub(crate) struct PtrLen {
1919 pub len : usize ,
2020}
2121
22+ /// Representation of C++ `std::exception_ptr` for all targets except MSVC.
23+ ///
24+ /// This is a single pointer.
25+ #[ repr( C ) ]
26+ #[ derive( Copy , Clone ) ]
27+ #[ cfg( not( target_env = "msvc" ) ) ]
28+ struct CxxExceptionRepr {
29+ ptr : NonNull < u8 > ,
30+ }
31+
32+ /// Representation of C++ `std::exception_ptr` for MSVC.
33+ ///
34+ /// Unfortunately, MSVC uses two pointers for `std::exception_ptr`, so we have
35+ /// to account for that.
36+ #[ repr( C ) ]
37+ #[ derive( Copy , Clone ) ]
38+ #[ cfg( target_env = "msvc" ) ]
39+ struct CxxExceptionRepr {
40+ ptr : NonNull < u8 > ,
41+ _ptr2 : * mut u8 ,
42+ }
43+
2244extern "C" {
2345 /// Helper to construct the default exception from the error message.
2446 #[ link_name = "cxxbridge1$default_exception" ]
25- fn default_exception ( ptr : * const u8 , len : usize ) -> * mut u8 ;
47+ fn default_exception ( ptr : * const u8 , len : usize ) -> CxxExceptionRepr ;
2648 /// Helper to clone the instance of `std::exception_ptr` on the C++ side.
2749 #[ link_name = "cxxbridge1$clone_exception" ]
28- fn clone_exception ( ptr : * const u8 ) -> * mut u8 ;
50+ fn clone_exception ( ptr : & CxxExceptionRepr ) -> CxxExceptionRepr ;
2951 /// Helper to drop the instance of `std::exception_ptr` on the C++ side.
3052 #[ link_name = "cxxbridge1$drop_exception" ]
31- fn drop_exception ( ptr : * mut u8 ) ;
53+ fn drop_exception ( ptr : CxxExceptionRepr ) ;
3254}
3355
34- /// C++ exception containing `std::exception_ptr`.
56+ /// C++ exception containing an `std::exception_ptr`.
3557///
3658/// This object is the Rust wrapper over `std::exception_ptr`, so it owns the exception pointer.
3759/// I.e., the exception is either referenced by a `std::exception_ptr` on the C++ side or the
3860/// reference is moved to this object on the Rust side.
3961#[ repr( C ) ]
4062#[ must_use]
41- pub struct CxxException ( NonNull < u8 > ) ;
63+ pub struct CxxException ( CxxExceptionRepr ) ;
4264
4365impl CxxException {
4466 /// Construct the default `rust::Error` exception from the specified `exc_text`.
4567 fn new_default ( exc_text : & str ) -> Self {
46- let exception_ptr = unsafe {
47- default_exception ( exc_text. as_ptr ( ) , exc_text. len ( ) )
48- } ;
49- CxxException (
50- NonNull :: new ( exception_ptr)
51- . expect ( "Exception conversion returned a null pointer" )
52- )
68+ let exception_repr = unsafe { default_exception ( exc_text. as_ptr ( ) , exc_text. len ( ) ) } ;
69+ CxxException ( exception_repr)
5370 }
5471}
5572
5673impl Clone for CxxException {
5774 fn clone ( & self ) -> Self {
58- let clone_ptr = unsafe { clone_exception ( self . 0 . as_ptr ( ) ) } ;
59- Self (
60- NonNull :: new ( clone_ptr)
61- . expect ( "Exception cloning returned a null pointer" )
62- )
75+ let exception_repr = unsafe { clone_exception ( & self . 0 ) } ;
76+ Self ( exception_repr)
6377 }
6478}
6579
@@ -71,7 +85,7 @@ impl From<Exception> for CxxException {
7185
7286impl Drop for CxxException {
7387 fn drop ( & mut self ) {
74- unsafe { drop_exception ( self . 0 . as_ptr ( ) ) } ;
88+ unsafe { drop_exception ( self . 0 ) } ;
7589 }
7690}
7791
@@ -84,49 +98,34 @@ unsafe impl Sync for CxxException {}
8498
8599/// C++ "result" containing `std::exception_ptr` or a `null`.
86100#[ repr( C ) ]
87- pub struct CxxResult ( * mut u8 ) ;
101+ pub struct CxxResult ( Option < CxxException > ) ;
88102
89103impl From < CxxException > for CxxResult {
90104 fn from ( value : CxxException ) -> Self {
91- let res = Self ( value. 0 . as_ptr ( ) ) ;
92- // NOTE: we are copying the pointer, so we need to forget it here,
93- // otherwise we'd double-free the `std::exception_ptr`.
94- core:: mem:: forget ( value) ;
95- res
96- }
97- }
98-
99- impl Drop for CxxResult {
100- fn drop ( & mut self ) {
101- if !self . 0 . is_null ( ) {
102- unsafe { drop_exception ( self . 0 ) } ;
103- }
105+ Self ( Some ( value) )
104106 }
105107}
106108
107109impl CxxResult {
108110 /// Construct an empty `Ok` result.
109111 pub fn new ( ) -> Self {
110- Self ( core :: ptr :: null_mut ( ) )
112+ Self ( None )
111113 }
112114}
113115
114116impl CxxResult {
115117 unsafe fn exception ( self ) -> Result < ( ) , CxxException > {
116118 // SAFETY: We know that the `Result` can only contain a valid `std::exception_ptr` or null.
117- match unsafe { self . 0 . as_mut ( ) } {
119+ match self . 0 {
118120 None => Ok ( ( ) ) ,
119- Some ( ptr) => {
120- let res = CxxException ( NonNull :: from ( ptr) ) ;
121- // NOTE: we are copying the pointer, so we need to forget this
122- // object, otherwise we'd double-free the `std::exception_ptr`.
123- core:: mem:: forget ( self ) ;
124- Err ( res)
125- }
121+ Some ( ptr) => Err ( ptr) ,
126122 }
127123 }
128124}
129125
126+ // Assert that the result is not larger than the exception (`Option` will use the niche).
127+ const _: ( ) = assert ! ( core:: mem:: size_of:: <CxxResult >( ) == core:: mem:: size_of:: <CxxException >( ) ) ;
128+
130129#[ repr( C ) ]
131130pub struct CxxResultWithMessage {
132131 pub ( crate ) res : CxxResult ,
@@ -142,19 +141,20 @@ impl CxxResultWithMessage {
142141 // SAFETY: The message is always given for the exception and we constructed it in
143142 // a `Box` in `cxxbridge1$exception()`. We just reconstruct it here.
144143 let what = unsafe {
145- str:: from_utf8_unchecked_mut (
146- slice:: from_raw_parts_mut ( self . msg . ptr . as_ptr ( ) , self . msg . len ) )
144+ str:: from_utf8_unchecked_mut ( slice:: from_raw_parts_mut (
145+ self . msg . ptr . as_ptr ( ) ,
146+ self . msg . len ,
147+ ) )
147148 } ;
148149 Err ( Exception {
149150 src,
150- what : unsafe { Box :: from_raw ( what) }
151+ what : unsafe { Box :: from_raw ( what) } ,
151152 } )
152153 }
153154 }
154155 }
155156}
156157
157-
158158/// Trait to convert an arbitrary Rust error into a C++ exception.
159159///
160160/// If an implementation of [`ToCxxException`] is explicitly provided for an `E`, then this
@@ -211,9 +211,8 @@ impl<T: Display> ToCxxExceptionDefault for &T {
211211 } ;
212212 // we have sufficient buffer size, just construct from the inplace
213213 // buffer
214- let exc_text = unsafe {
215- std:: str:: from_utf8_unchecked ( & buffer. assume_init_ref ( ) [ 0 ..size] )
216- } ;
214+ let exc_text =
215+ unsafe { std:: str:: from_utf8_unchecked ( & buffer. assume_init_ref ( ) [ 0 ..size] ) } ;
217216 CxxException :: new_default ( exc_text)
218217 }
219218 #[ cfg( not( feature = "std" ) ) ]
@@ -234,8 +233,8 @@ macro_rules! map_rust_error_to_cxx_exception {
234233 // the need for `specialization` feature. Namely, `ToCxxException` for `T` has higher
235234 // weight and is selected before `ToCxxExceptionDefault`, which is defined on `&T` (and
236235 // requires auto-deref). If it's not defined, then the default is used.
237- use $crate:: ToCxxExceptionDefault ;
238236 use $crate:: ToCxxException ;
237+ use $crate:: ToCxxExceptionDefault ;
239238 ( & $err) . to_cxx_exception( )
240239 } ;
241240 exc
@@ -250,7 +249,7 @@ macro_rules! map_rust_result_to_cxx_result {
250249 unsafe { :: core:: ptr:: write( $ret_ptr, ok) } ;
251250 $crate:: private:: CxxResult :: new( )
252251 }
253- Err ( err) => $crate:: private:: CxxResult :: from( err)
252+ Err ( err) => $crate:: private:: CxxResult :: from( err) ,
254253 }
255254 } ;
256255}
0 commit comments