@@ -206,10 +206,12 @@ impl<T: GodotClass> Gd<T> {
206206 } )
207207 }
208208
209- /// ⚠️ Returns the last known, possibly invalid instance ID of this object.
209+ /// Returns the last known, possibly invalid instance ID of this object.
210210 ///
211211 /// This function does not check that the returned instance ID points to a valid instance!
212212 /// Unless performance is a problem, use [`instance_id()`][Self::instance_id] instead.
213+ ///
214+ /// This method is safe and never panics.
213215 pub fn instance_id_unchecked ( & self ) -> InstanceId {
214216 // SAFETY:
215217 // A `Gd` can only be created from a non-null `RawGd`. Meaning `raw.instance_id_unchecked()` will
@@ -390,16 +392,22 @@ where
390392 /// example to the node tree in case of nodes.
391393 ///
392394 /// # Panics
393- /// * When the referred-to object has already been destroyed.
394- /// * When this is invoked on an upcast `Gd<Object>` that dynamically points to a reference-counted type (i.e. operation not supported).
395+ /// - When the referred-to object has already been destroyed.
396+ /// - When this is invoked on an upcast `Gd<Object>` that dynamically points to a reference-counted type (i.e. operation not supported).
397+ /// - When the object is bound by an ongoing `bind()` or `bind_mut()` call (through a separate `Gd` pointer).
395398 pub fn free ( self ) {
399+ // Note: this method is NOT invoked when the free() call happens dynamically (e.g. through GDScript or reflection).
400+ // As such, do not use it for operations and validations to perform upon destruction.
401+
396402 // TODO disallow for singletons, either only at runtime or both at compile time (new memory policy) and runtime
403+ use dom:: Domain ;
397404
398405 // Runtime check in case of T=Object, no-op otherwise
399406 let ref_counted = T :: Mem :: is_ref_counted ( & self . raw ) ;
400407 assert_ne ! (
401408 ref_counted, Some ( true ) ,
402- "called free() on Gd<Object> which points to a RefCounted dynamic type; free() only supported for manually managed types."
409+ "called free() on Gd<Object> which points to a RefCounted dynamic type; free() only supported for manually managed types\n \
410+ object: {self:?}"
403411 ) ;
404412
405413 // If ref_counted returned None, that means the instance was destroyed
@@ -408,7 +416,17 @@ where
408416 "called free() on already destroyed object"
409417 ) ;
410418
411- // This destroys the Storage instance, no need to run destructor again
419+ // SAFETY: object must be alive, which was just checked above. No multithreading here.
420+ // Also checked in the C free_instance_func callback, however error message can be more precise here and we don't need to instruct
421+ // the engine about object destruction. Both paths are tested.
422+ let bound = unsafe { T :: Declarer :: is_currently_bound ( & self . raw ) } ;
423+ assert ! (
424+ !bound,
425+ "called free() while a bind() or bind_mut() call is active"
426+ ) ;
427+
428+ // SAFETY: object alive as checked.
429+ // This destroys the Storage instance, no need to run destructor again.
412430 unsafe {
413431 sys:: interface_fn!( object_destroy) ( self . raw . obj_sys ( ) ) ;
414432 }
0 commit comments