@@ -16,6 +16,7 @@ use crate::meta::{
16
16
CallContext , ClassName , FromGodot , GodotConvert , GodotFfiVariant , GodotType , RefArg , ToGodot ,
17
17
} ;
18
18
use crate :: obj:: bounds:: { Declarer , DynMemory as _} ;
19
+ use crate :: obj:: casts:: CastSuccess ;
19
20
use crate :: obj:: rtti:: ObjectRtti ;
20
21
use crate :: obj:: { bounds, Bounds , GdDerefTarget , GdMut , GdRef , GodotClass , InstanceId } ;
21
22
use crate :: storage:: { InstanceCache , InstanceStorage , Storage } ;
@@ -115,21 +116,8 @@ impl<T: GodotClass> RawGd<T> {
115
116
where
116
117
U : GodotClass ,
117
118
{
118
- if self . is_null ( ) {
119
- // Null can be cast to anything.
120
- return true ;
121
- }
122
-
123
- // SAFETY: object is forgotten below.
124
- let as_obj =
125
- unsafe { self . ffi_cast :: < classes:: Object > ( ) } . expect ( "everything inherits Object" ) ;
126
-
127
- // SAFETY: Object is always a base class.
128
- let cast_is_valid = unsafe { as_obj. as_upcast_ref :: < classes:: Object > ( ) }
129
- . is_class ( & U :: class_name ( ) . to_gstring ( ) ) ;
130
-
131
- std:: mem:: forget ( as_obj) ;
132
- cast_is_valid
119
+ self . is_null ( ) // Null can be cast to anything.
120
+ || self . as_object_ref ( ) . is_class ( & U :: class_name ( ) . to_gstring ( ) )
133
121
}
134
122
135
123
/// Returns `Ok(cast_obj)` on success, `Err(self)` on error
@@ -150,21 +138,17 @@ impl<T: GodotClass> RawGd<T> {
150
138
// The Deref/DerefMut impls for T implement an "implicit upcast" on the object (not Gd) level and
151
139
// rely on this (e.g. &Node3D -> &Node).
152
140
153
- let result = unsafe { self . ffi_cast :: < U > ( ) } ;
154
- match result {
155
- Some ( cast_obj) => {
156
- // duplicated ref, one must be wiped
157
- std:: mem:: forget ( self ) ;
158
- Ok ( cast_obj)
159
- }
160
- None => Err ( self ) ,
141
+ match self . ffi_cast :: < U > ( ) {
142
+ Ok ( success) => Ok ( success. into_dest ( self ) ) ,
143
+ Err ( _) => Err ( self ) ,
161
144
}
162
145
}
163
146
164
- /// # Safety
165
- /// Does not transfer ownership and is thus unsafe. Also operates on shared ref. Either the parameter or
166
- /// the return value *must* be forgotten (since reference counts are not updated).
167
- pub ( super ) unsafe fn ffi_cast < U > ( & self ) -> Option < RawGd < U > >
147
+ /// Low-level cast that allows selective use of either input or output type.
148
+ ///
149
+ /// On success, you'll get a `CastSuccess<T, U>` instance, which holds a weak `RawGd<U>`. You can only extract that one by trading
150
+ /// a strong `RawGd<T>` for it, to maintain the balance.
151
+ pub ( super ) fn ffi_cast < U > ( & self ) -> Result < CastSuccess < T , U > , ( ) >
168
152
where
169
153
U : GodotClass ,
170
154
{
@@ -177,7 +161,7 @@ impl<T: GodotClass> RawGd<T> {
177
161
if self . is_null ( ) {
178
162
// Null can be cast to anything.
179
163
// Forgetting a null doesn't do anything, since dropping a null also does nothing.
180
- return Some ( RawGd :: null ( ) ) ;
164
+ return Ok ( CastSuccess :: null ( ) ) ;
181
165
}
182
166
183
167
// Before Godot API calls, make sure the object is alive (and in Debug mode, of the correct type).
@@ -186,23 +170,32 @@ impl<T: GodotClass> RawGd<T> {
186
170
// a bug that must be solved by the user.
187
171
self . check_rtti ( "ffi_cast" ) ;
188
172
189
- let class_tag = interface_fn ! ( classdb_get_class_tag) ( U :: class_name ( ) . string_sys ( ) ) ;
190
- let cast_object_ptr = interface_fn ! ( object_cast_to) ( self . obj_sys ( ) , class_tag) ;
173
+ let cast_object_ptr = unsafe {
174
+ let class_tag = interface_fn ! ( classdb_get_class_tag) ( U :: class_name ( ) . string_sys ( ) ) ;
175
+ interface_fn ! ( object_cast_to) ( self . obj_sys ( ) , class_tag)
176
+ } ;
177
+
178
+ if cast_object_ptr. is_null ( ) {
179
+ return Err ( ( ) ) ;
180
+ }
191
181
192
182
// Create weak object, as ownership will be moved and reference-counter stays the same.
193
- sys:: ptr_then ( cast_object_ptr, |ptr| RawGd :: from_obj_sys_weak ( ptr) )
183
+ let weak = unsafe { RawGd :: from_obj_sys_weak ( cast_object_ptr) } ;
184
+ Ok ( CastSuccess :: from_weak ( weak) )
194
185
}
195
186
196
187
pub ( crate ) fn with_ref_counted < R > ( & self , apply : impl Fn ( & mut classes:: RefCounted ) -> R ) -> R {
197
188
// Note: this previously called Declarer::scoped_mut() - however, no need to go through bind() for changes in base RefCounted.
198
189
// Any accesses to user objects (e.g. destruction if refc=0) would bind anyway.
199
190
200
- let tmp = unsafe { self . ffi_cast :: < classes :: RefCounted > ( ) } ;
201
- let mut tmp = tmp . expect ( "object expected to inherit RefCounted" ) ;
202
- let return_val = apply ( tmp . as_target_mut ( ) ) ;
191
+ let mut cast_obj = self
192
+ . ffi_cast :: < classes :: RefCounted > ( )
193
+ . expect ( "object expected to inherit RefCounted" ) ;
203
194
204
- std:: mem:: forget ( tmp) ; // no ownership transfer
205
- return_val
195
+ // Using as_dest_mut() ensures that there is no refcount increment happening, i.e. any apply() function happens on *current* object.
196
+ // Apart from performance considerations, this is relevant when examining RefCounted::get_reference_count() -- otherwise we have an
197
+ // Observer effect, where reading the RefCounted object changes its reference count -- e.g. in Debug impl.
198
+ apply ( cast_obj. as_dest_mut ( ) . as_target_mut ( ) )
206
199
}
207
200
208
201
// TODO replace the above with this -- last time caused UB; investigate.
@@ -314,21 +307,21 @@ impl<T: GodotClass> RawGd<T> {
314
307
#[ cfg( debug_assertions) ]
315
308
{
316
309
// SAFETY: we forget the object below and do not leave the function before.
317
- let ffi_ref: RawGd < Base > =
318
- unsafe { self . ffi_cast :: < Base > ( ) . expect ( "failed FFI upcast" ) } ;
310
+ let ffi_dest = self . ffi_cast :: < Base > ( ) . expect ( "failed FFI upcast" ) ;
319
311
320
312
// The ID check is not that expressive; we should do a complete comparison of the ObjectRtti, but currently the dynamic types can
321
313
// be different (see comment in ObjectRtti struct). This at least checks that the transmuted object is not complete garbage.
322
314
// We get direct_id from Self and not Base because the latter has no API with current bounds; but this equivalence is tested in Deref.
323
315
let direct_id = self . instance_id_unchecked ( ) . expect ( "direct_id null" ) ;
324
- let ffi_id = ffi_ref. instance_id_unchecked ( ) . expect ( "ffi_id null" ) ;
316
+ let ffi_id = ffi_dest
317
+ . as_dest_ref ( )
318
+ . instance_id_unchecked ( )
319
+ . expect ( "ffi_id null" ) ;
325
320
326
321
assert_eq ! (
327
322
direct_id, ffi_id,
328
- "upcast_ref: direct and FFI IDs differ. This is a bug, please report to gdext maintainers."
323
+ "upcast_ref: direct and FFI IDs differ. This is a bug, please report to godot-rust maintainers."
329
324
) ;
330
-
331
- std:: mem:: forget ( ffi_ref) ;
332
325
}
333
326
}
334
327
0 commit comments