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