@@ -19,47 +19,61 @@ pub(crate) struct PtrLen {
19
19
pub len : usize ,
20
20
}
21
21
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
+
22
44
extern "C" {
23
45
/// Helper to construct the default exception from the error message.
24
46
#[ 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 ;
26
48
/// Helper to clone the instance of `std::exception_ptr` on the C++ side.
27
49
#[ link_name = "cxxbridge1$clone_exception" ]
28
- fn clone_exception ( ptr : * const u8 ) -> * mut u8 ;
50
+ fn clone_exception ( ptr : & CxxExceptionRepr ) -> CxxExceptionRepr ;
29
51
/// Helper to drop the instance of `std::exception_ptr` on the C++ side.
30
52
#[ link_name = "cxxbridge1$drop_exception" ]
31
- fn drop_exception ( ptr : * mut u8 ) ;
53
+ fn drop_exception ( ptr : CxxExceptionRepr ) ;
32
54
}
33
55
34
- /// C++ exception containing `std::exception_ptr`.
56
+ /// C++ exception containing an `std::exception_ptr`.
35
57
///
36
58
/// This object is the Rust wrapper over `std::exception_ptr`, so it owns the exception pointer.
37
59
/// I.e., the exception is either referenced by a `std::exception_ptr` on the C++ side or the
38
60
/// reference is moved to this object on the Rust side.
39
61
#[ repr( C ) ]
40
62
#[ must_use]
41
- pub struct CxxException ( NonNull < u8 > ) ;
63
+ pub struct CxxException ( CxxExceptionRepr ) ;
42
64
43
65
impl CxxException {
44
66
/// Construct the default `rust::Error` exception from the specified `exc_text`.
45
67
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)
53
70
}
54
71
}
55
72
56
73
impl Clone for CxxException {
57
74
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)
63
77
}
64
78
}
65
79
@@ -71,7 +85,7 @@ impl From<Exception> for CxxException {
71
85
72
86
impl Drop for CxxException {
73
87
fn drop ( & mut self ) {
74
- unsafe { drop_exception ( self . 0 . as_ptr ( ) ) } ;
88
+ unsafe { drop_exception ( self . 0 ) } ;
75
89
}
76
90
}
77
91
@@ -84,49 +98,34 @@ unsafe impl Sync for CxxException {}
84
98
85
99
/// C++ "result" containing `std::exception_ptr` or a `null`.
86
100
#[ repr( C ) ]
87
- pub struct CxxResult ( * mut u8 ) ;
101
+ pub struct CxxResult ( Option < CxxException > ) ;
88
102
89
103
impl From < CxxException > for CxxResult {
90
104
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) )
104
106
}
105
107
}
106
108
107
109
impl CxxResult {
108
110
/// Construct an empty `Ok` result.
109
111
pub fn new ( ) -> Self {
110
- Self ( core :: ptr :: null_mut ( ) )
112
+ Self ( None )
111
113
}
112
114
}
113
115
114
116
impl CxxResult {
115
117
unsafe fn exception ( self ) -> Result < ( ) , CxxException > {
116
118
// 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 {
118
120
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) ,
126
122
}
127
123
}
128
124
}
129
125
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
+
130
129
#[ repr( C ) ]
131
130
pub struct CxxResultWithMessage {
132
131
pub ( crate ) res : CxxResult ,
@@ -142,19 +141,20 @@ impl CxxResultWithMessage {
142
141
// SAFETY: The message is always given for the exception and we constructed it in
143
142
// a `Box` in `cxxbridge1$exception()`. We just reconstruct it here.
144
143
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
+ ) )
147
148
} ;
148
149
Err ( Exception {
149
150
src,
150
- what : unsafe { Box :: from_raw ( what) }
151
+ what : unsafe { Box :: from_raw ( what) } ,
151
152
} )
152
153
}
153
154
}
154
155
}
155
156
}
156
157
157
-
158
158
/// Trait to convert an arbitrary Rust error into a C++ exception.
159
159
///
160
160
/// If an implementation of [`ToCxxException`] is explicitly provided for an `E`, then this
@@ -211,9 +211,8 @@ impl<T: Display> ToCxxExceptionDefault for &T {
211
211
} ;
212
212
// we have sufficient buffer size, just construct from the inplace
213
213
// 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] ) } ;
217
216
CxxException :: new_default ( exc_text)
218
217
}
219
218
#[ cfg( not( feature = "std" ) ) ]
@@ -234,8 +233,8 @@ macro_rules! map_rust_error_to_cxx_exception {
234
233
// the need for `specialization` feature. Namely, `ToCxxException` for `T` has higher
235
234
// weight and is selected before `ToCxxExceptionDefault`, which is defined on `&T` (and
236
235
// requires auto-deref). If it's not defined, then the default is used.
237
- use $crate:: ToCxxExceptionDefault ;
238
236
use $crate:: ToCxxException ;
237
+ use $crate:: ToCxxExceptionDefault ;
239
238
( & $err) . to_cxx_exception( )
240
239
} ;
241
240
exc
@@ -250,7 +249,7 @@ macro_rules! map_rust_result_to_cxx_result {
250
249
unsafe { :: core:: ptr:: write( $ret_ptr, ok) } ;
251
250
$crate:: private:: CxxResult :: new( )
252
251
}
253
- Err ( err) => $crate:: private:: CxxResult :: from( err)
252
+ Err ( err) => $crate:: private:: CxxResult :: from( err) ,
254
253
}
255
254
} ;
256
255
}
0 commit comments