@@ -53,12 +53,10 @@ impl<T: GodotClass> RawGd<T> {
53
53
} else {
54
54
let raw_id = unsafe { interface_fn ! ( object_get_instance_id) ( obj) } ;
55
55
56
- // During Variant -> RawGd conversion, it can happen that the variant contains a dead object.
57
- // It's not quite clear how Godot detects instance_id == 0 here though, since the Variant holds the object as bytes in an array,
58
- // and there is no lookup performed.
59
- let Some ( instance_id) = InstanceId :: try_from_u64 ( raw_id) else {
60
- return Self :: null ( ) ;
61
- } ;
56
+ // This happened originally during Variant -> RawGd conversion, but at this point it's too late to detect, and UB has already
57
+ // occurred (the Variant holds the object pointer as bytes in an array, which becomes dangling the moment the actual object dies).
58
+ let instance_id = InstanceId :: try_from_u64 ( raw_id)
59
+ . expect ( "null instance ID when constructing object; this very likely causes UB" ) ;
62
60
63
61
// TODO(bromeon): this should query dynamic type of object, which can be different from T (upcast, FromGodot, etc).
64
62
// See comment in ObjectRtti.
@@ -593,6 +591,12 @@ impl<T: GodotClass> GodotFfiVariant for RawGd<T> {
593
591
. into_error ( variant. clone ( ) ) ) ;
594
592
}
595
593
594
+ // Check for dead objects *before* converting. Godot doesn't care if the objects are still alive, and hitting
595
+ // RawGd::from_obj_sys_weak() is too late and causes UB.
596
+ if variant. is_object_dead ( ) {
597
+ return Err ( FromVariantError :: DeadObject . into_error ( variant. clone ( ) ) ) ;
598
+ }
599
+
596
600
let raw = unsafe {
597
601
// Uses RawGd<Object> and not Self, because Godot still allows illegal conversions. We thus check with manual casting later on.
598
602
// See https://github.com/godot-rust/gdext/issues/158.
@@ -603,12 +607,6 @@ impl<T: GodotClass> GodotFfiVariant for RawGd<T> {
603
607
} )
604
608
} ;
605
609
606
- // Explicitly handle case where object is dead. See RawGd::from_obj_sys_weak() for some edge cases and considerations.
607
- if raw. is_null ( ) {
608
- // Passing `raw` is not useful, it would just print "null" for the value.
609
- return Err ( FromVariantError :: DeadObject . into_error ( variant. clone ( ) ) ) ;
610
- }
611
-
612
610
raw. with_inc_refcount ( ) . owned_cast ( ) . map_err ( |raw| {
613
611
FromVariantError :: WrongClass {
614
612
expected : T :: class_name ( ) ,
0 commit comments