Skip to main content

godot_core/obj/
bounds.rs

1/*
2 * Copyright (c) godot-rust; Bromeon and contributors.
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
6 */
7
8//! Different ways how bounds of a `GodotClass` can be checked.
9//!
10//! This module contains multiple traits that can be used to check the characteristics of a `GodotClass` type:
11//!
12//! 1. [`Declarer`] tells you whether the class is provided by the engine or user-defined.
13//!    - [`DeclEngine`] is used for all classes provided by the engine (e.g. `Node3D`).
14//!    - [`DeclUser`] is used for all classes defined by the user, typically through `#[derive(GodotClass)]`.<br><br>
15//!
16//! 2. [`Memory`] is used to check the memory strategy of the **static** type.
17//!
18//!    This is useful when you operate on associated functions of `Gd<T>` or `T`, e.g. for construction.
19//!    - [`MemRefCounted`] is used for `RefCounted` classes and derived.
20//!    - [`MemManual`] is used for `Object` and all inherited classes, which are not `RefCounted` (e.g. `Node`).<br><br>
21//!
22// FIXME excluded because broken; see below.
23// 3. [`DynMemory`] is used to check the memory strategy of the **dynamic** type.
24//
25//    When you operate on methods of `T` or `Gd<T>` and are interested in instances, you can use this.
26//    Most of the time, this is not what you want -- just use `Memory` if you want to know if a type is manually managed or ref-counted.
27//    - [`MemRefCounted`] is used for `RefCounted` classes and derived. These are **always** reference-counted.
28//    - [`MemManual`] is used instances inheriting `Object`, which are not `RefCounted` (e.g. `Node`). Excludes `Object` itself. These are
29//      **always** manually managed.
30//    - [`MemDynamic`] is used for `Object` instances. `Gd<Object>` can point to objects of any possible class, so whether we are dealing with
31//      a ref-counted or manually-managed object is determined only at runtime.
32//!
33//!
34//! # Example
35//!
36//! Declare a custom smart pointer which wraps `Gd<T>` pointers, but only accepts `T` objects that are manually managed.
37//! ```
38//! use godot::prelude::*;
39//! use godot::obj::{bounds, Bounds};
40//!
41//! struct MyGd<T>
42//! where T: GodotClass + Bounds<Memory = bounds::MemManual>
43//! {
44//!    inner: Gd<T>,
45//! }
46//! ```
47//!
48// Note that depending on if you want to exclude `Object`, you should use `DynMemory` instead of `Memory`.
49
50use private::Sealed;
51
52use crate::obj::cap::GodotDefault;
53use crate::obj::{Bounds, Gd, GodotClass, RawGd};
54use crate::storage::{InstanceCache, Storage};
55use crate::{out, sys};
56
57// ----------------------------------------------------------------------------------------------------------------------------------------------
58// Sealed trait
59
60pub(super) mod private {
61    use super::{Declarer, DynMemory, Exportable, Memory};
62
63    // Bounds trait declared here for code locality; re-exported in crate::obj.
64
65    /// Library-implemented trait to check bounds on `GodotClass` types.
66    ///
67    /// See [`bounds`](crate::obj::bounds) module for how to use this for bounds checking.
68    ///
69    /// # No manual `impl`
70    ///
71    /// <div class="warning">
72    /// <strong>Never</strong> implement this trait manually.
73    /// </div>
74    ///
75    /// Most of the time, this trait is covered by [`#[derive(GodotClass)]`](../register/derive.GodotClass.html).
76    /// If you implement `GodotClass` manually, use the [`implement_godot_bounds!`][crate::implement_godot_bounds] macro.
77    ///
78    /// There are two reasons to avoid a handwritten `impl Bounds`:
79    /// - The trait is `unsafe` and it is very easy to get internal bounds wrong. This will lead to immediate UB.
80    /// - Apart from the documented members, the trait may have undocumented items that may be broken at any time and stand under no SemVer
81    ///   guarantees.
82    ///
83    /// # Safety
84    ///
85    /// Internal. The library implements this trait and ensures safety.
86    pub unsafe trait Bounds {
87        /// Defines the memory strategy of the static type.
88        type Memory: Memory;
89
90        // FIXME: this is broken as a bound: one cannot use T: Bounds<DynMemory = MemRefCounted> to include Object AND RefCounted,
91        // since Object itself has DynMemory = MemDynamic. Needs to either use traits like in gdnative, or more types to account for
92        // different combinations (as only positive ones can be expressed, not T: Bounds<Memory != MemManual>).
93        #[doc(hidden)]
94        /// Defines the memory strategy of the instance (at runtime).
95        type DynMemory: DynMemory;
96
97        /// Whether this class is a core Godot class provided by the engine, or declared by the user as a Rust struct.
98        // TODO what about GDScript user classes?
99        type Declarer: Declarer;
100
101        /// True if *either* `T: Inherits<Node>` *or* `T: Inherits<Resource>` is fulfilled.
102        ///
103        /// Enables `#[export]` for those classes.
104        #[doc(hidden)]
105        type Exportable: Exportable;
106    }
107
108    /// Implements [`Bounds`] for a user-defined class.
109    ///
110    /// This is only necessary if you do not use the proc-macro API.
111    ///
112    /// Since `Bounds` is a supertrait of [`GodotClass`][crate::obj::GodotClass], you cannot accidentally forget to implement it.
113    ///
114    /// # Example
115    /// ```no_run
116    /// use godot::prelude::*;
117    /// use godot::obj::bounds::implement_godot_bounds;
118    /// use godot::meta::ClassId;
119    ///
120    /// struct MyClass {}
121    ///
122    /// impl GodotClass for MyClass {
123    ///     type Base = Node;
124    ///
125    ///     fn class_id() -> ClassId {
126    ///         ClassId::new_cached::<MyClass>(|| "MyClass".to_string())
127    ///     }
128    /// }
129    ///
130    /// implement_godot_bounds!(MyClass);
131    #[macro_export]
132    macro_rules! implement_godot_bounds {
133        ($UserClass:ty) => {
134            // SAFETY: bounds are library-defined, dependent on base. User has no influence in selecting them -> macro is safe.
135            unsafe impl $crate::obj::Bounds for $UserClass {
136                type Memory = <<$UserClass as $crate::obj::GodotClass>::Base as $crate::obj::Bounds>::Memory;
137                type DynMemory = <<$UserClass as $crate::obj::GodotClass>::Base as $crate::obj::Bounds>::DynMemory;
138                type Declarer = $crate::obj::bounds::DeclUser;
139                type Exportable = <<$UserClass as $crate::obj::GodotClass>::Base as $crate::obj::Bounds>::Exportable;
140            }
141        };
142    }
143
144    pub trait Sealed {}
145}
146
147// ----------------------------------------------------------------------------------------------------------------------------------------------
148// Macro re-exports
149
150pub use crate::implement_godot_bounds;
151
152// ----------------------------------------------------------------------------------------------------------------------------------------------
153// Memory bounds
154
155/// Specifies the memory strategy of the static type.
156pub trait Memory: Sealed {
157    /// True for everything inheriting `RefCounted`, false for `Object` and all other classes.
158    #[doc(hidden)]
159    const IS_REF_COUNTED: bool;
160}
161
162/// Specifies the memory strategy of the dynamic type.
163///
164/// For `Gd<Object>`, it is determined at runtime whether the instance is manually managed or ref-counted.
165#[doc(hidden)]
166pub trait DynMemory: Sealed {
167    /// Initialize reference counter
168    #[doc(hidden)]
169    fn maybe_init_ref<T: GodotClass>(obj: &mut RawGd<T>);
170
171    /// If ref-counted, then increment count
172    #[doc(hidden)]
173    fn maybe_inc_ref<T: GodotClass>(obj: &mut RawGd<T>);
174
175    /// If ref-counted, then decrement count. Returns `true` if the count hit 0 and the object can be
176    /// safely freed.
177    ///
178    /// This behavior can be overriden by a script, making it possible for the function to return `false`
179    /// even when the reference count hits 0. This is meant to be used to have a separate reference count
180    /// from Godot's internal reference count, or otherwise stop the object from being freed when the
181    /// reference count hits 0.
182    ///
183    /// # Safety
184    ///
185    /// If this method is used on a [`Gd`] that inherits from [`RefCounted`](crate::classes::RefCounted)
186    /// then the reference count must either be incremented before it hits 0, or some [`Gd`] referencing
187    /// this object must be forgotten.
188    #[doc(hidden)]
189    unsafe fn maybe_dec_ref<T: GodotClass>(obj: &mut RawGd<T>) -> bool;
190
191    /// Check if ref-counted, return `None` if information is not available (dynamic and obj dead)
192    #[doc(hidden)]
193    fn is_ref_counted<T: GodotClass>(obj: &RawGd<T>) -> Option<bool>;
194
195    /// Return the reference count, or `None` if the object is dead or manually managed.
196    #[doc(hidden)]
197    fn get_ref_count<T: GodotClass>(obj: &RawGd<T>) -> Option<usize>;
198
199    /// Returns `true` if argument and return pointers are passed as `Ref<T>` pointers given this
200    /// [`PtrcallType`].
201    ///
202    /// See [`PtrcallType::Virtual`] for information about `Ref<T>` objects.
203    #[doc(hidden)]
204    fn pass_as_ref(_call_type: sys::PtrcallType) -> bool {
205        false
206    }
207}
208
209/// Memory managed through Godot reference counter (always present).
210/// This is used for `RefCounted` classes and derived.
211pub struct MemRefCounted {}
212impl Sealed for MemRefCounted {}
213impl Memory for MemRefCounted {
214    const IS_REF_COUNTED: bool = true;
215}
216impl DynMemory for MemRefCounted {
217    fn maybe_init_ref<T: GodotClass>(obj: &mut RawGd<T>) {
218        out!("  MemRefc::init:  {obj:?}");
219        if obj.is_null() {
220            return;
221        }
222        obj.with_ref_counted(|refc| {
223            let success = refc.init_ref();
224            assert!(success, "init_ref() failed");
225        });
226
227        /*
228        // SAFETY: DynMemory=MemRefCounted statically guarantees that the object inherits from RefCounted.
229        let refc = unsafe { obj.as_ref_counted_unchecked() };
230
231        let success = refc.init_ref();
232        assert!(success, "init_ref() failed");*/
233    }
234
235    fn maybe_inc_ref<T: GodotClass>(obj: &mut RawGd<T>) {
236        out!("  MemRefc::inc:   {obj:?}");
237        if obj.is_null() {
238            return;
239        }
240        obj.with_ref_counted(|refc| {
241            let success = refc.reference();
242            assert!(success, "reference() failed");
243        });
244    }
245
246    unsafe fn maybe_dec_ref<T: GodotClass>(obj: &mut RawGd<T>) -> bool {
247        out!("  MemRefc::dec:   {obj:?}");
248        if obj.is_null() {
249            return false;
250        }
251        obj.with_ref_counted(|refc| {
252            let is_last = refc.unreference();
253            out!("  +-- was last={is_last}");
254            is_last
255        })
256    }
257
258    fn is_ref_counted<T: GodotClass>(_obj: &RawGd<T>) -> Option<bool> {
259        Some(true)
260    }
261
262    fn get_ref_count<T: GodotClass>(obj: &RawGd<T>) -> Option<usize> {
263        let ref_count = obj.with_ref_counted(|refc| refc.get_reference_count());
264
265        // TODO find a safer cast alternative, e.g. num-traits crate with ToPrimitive (Debug) + AsPrimitive (Release).
266        Some(ref_count as usize)
267    }
268
269    fn pass_as_ref(call_type: sys::PtrcallType) -> bool {
270        matches!(call_type, sys::PtrcallType::Virtual)
271    }
272}
273
274/// Memory managed through Godot reference counter, if present; otherwise manual.
275/// This is used only for `Object` classes.
276#[doc(hidden)]
277pub struct MemDynamic {}
278impl MemDynamic {
279    /// Check whether dynamic type is ref-counted.
280    fn inherits_refcounted<T: GodotClass>(obj: &RawGd<T>) -> bool {
281        obj.instance_id_unchecked()
282            .is_some_and(|id| id.is_ref_counted())
283    }
284}
285impl Sealed for MemDynamic {}
286impl DynMemory for MemDynamic {
287    fn maybe_init_ref<T: GodotClass>(obj: &mut RawGd<T>) {
288        out!("  MemDyn::init:  {obj:?}");
289        if Self::inherits_refcounted(obj) {
290            // Will call `RefCounted::init_ref()` which checks for liveness.
291            out!("    MemDyn -> MemRefc");
292            MemRefCounted::maybe_init_ref(obj)
293        } else {
294            out!("    MemDyn -> MemManu");
295        }
296    }
297
298    fn maybe_inc_ref<T: GodotClass>(obj: &mut RawGd<T>) {
299        out!("  MemDyn::inc:   {obj:?}");
300        if Self::inherits_refcounted(obj) {
301            // Will call `RefCounted::reference()` which checks for liveness.
302            MemRefCounted::maybe_inc_ref(obj)
303        }
304    }
305
306    unsafe fn maybe_dec_ref<T: GodotClass>(obj: &mut RawGd<T>) -> bool {
307        unsafe {
308            out!("  MemDyn::dec:   {obj:?}");
309            if obj
310                .instance_id_unchecked()
311                .is_some_and(|id| id.is_ref_counted())
312            {
313                // Will call `RefCounted::unreference()` which checks for liveness.
314                MemRefCounted::maybe_dec_ref(obj)
315            } else {
316                false
317            }
318        }
319    }
320
321    fn is_ref_counted<T: GodotClass>(obj: &RawGd<T>) -> Option<bool> {
322        // Return `None` if obj is dead
323        obj.instance_id_unchecked().map(|id| id.is_ref_counted())
324    }
325
326    fn get_ref_count<T: GodotClass>(obj: &RawGd<T>) -> Option<usize> {
327        if Self::inherits_refcounted(obj) {
328            MemRefCounted::get_ref_count(obj)
329        } else {
330            None
331        }
332    }
333}
334
335/// No memory management, user responsible for not leaking.
336/// This is used for all `Object` derivates, which are not `RefCounted`. `Object` itself is also excluded.
337pub struct MemManual {}
338impl Sealed for MemManual {}
339impl Memory for MemManual {
340    const IS_REF_COUNTED: bool = false;
341}
342impl DynMemory for MemManual {
343    fn maybe_init_ref<T: GodotClass>(_obj: &mut RawGd<T>) {}
344    fn maybe_inc_ref<T: GodotClass>(_obj: &mut RawGd<T>) {}
345    unsafe fn maybe_dec_ref<T: GodotClass>(_obj: &mut RawGd<T>) -> bool {
346        false
347    }
348    fn is_ref_counted<T: GodotClass>(_obj: &RawGd<T>) -> Option<bool> {
349        Some(false)
350    }
351    fn get_ref_count<T: GodotClass>(_obj: &RawGd<T>) -> Option<usize> {
352        None
353    }
354}
355
356// ----------------------------------------------------------------------------------------------------------------------------------------------
357// Declarer bounds
358
359/// Trait that specifies who declares a given `GodotClass`.
360pub trait Declarer: Sealed {
361    /// The target type of a `Deref` operation on a `Gd<T>`.
362    #[doc(hidden)]
363    type DerefTarget<T: GodotClass>: GodotClass;
364
365    /// Used as a field in `RawGd`; only set for user-defined classes.
366    #[doc(hidden)]
367    #[allow(private_bounds)]
368    type InstanceCache: InstanceCache;
369
370    /// Check if the object is a user object *and* currently locked by a `bind()` or `bind_mut()` guard.
371    ///
372    /// # Safety
373    /// Object must be alive.
374    #[doc(hidden)]
375    unsafe fn is_currently_bound<T>(obj: &RawGd<T>) -> bool
376    where
377        T: GodotClass + Bounds<Declarer = Self>;
378
379    #[doc(hidden)]
380    fn create_gd<T>() -> Gd<T>
381    where
382        T: GodotDefault + Bounds<Declarer = Self>;
383}
384
385/// Expresses that a class is declared by the Godot engine.
386pub enum DeclEngine {}
387impl Sealed for DeclEngine {}
388impl Declarer for DeclEngine {
389    type DerefTarget<T: GodotClass> = T;
390    type InstanceCache = ();
391
392    unsafe fn is_currently_bound<T>(_obj: &RawGd<T>) -> bool
393    where
394        T: GodotClass + Bounds<Declarer = Self>,
395    {
396        false
397    }
398
399    fn create_gd<T>() -> Gd<T>
400    where
401        T: GodotDefault + Bounds<Declarer = Self>,
402    {
403        crate::classes::construct_engine_object()
404    }
405}
406
407/// Expresses that a class is declared by the user.
408pub enum DeclUser {}
409impl Sealed for DeclUser {}
410impl Declarer for DeclUser {
411    type DerefTarget<T: GodotClass> = T::Base;
412    type InstanceCache = std::cell::Cell<sys::GDExtensionClassInstancePtr>;
413
414    unsafe fn is_currently_bound<T>(obj: &RawGd<T>) -> bool
415    where
416        T: GodotClass + Bounds<Declarer = Self>,
417    {
418        unsafe { obj.storage().unwrap_unchecked().is_bound() }
419    }
420
421    fn create_gd<T>() -> Gd<T>
422    where
423        T: GodotDefault + Bounds<Declarer = Self>,
424    {
425        Gd::default_instance()
426    }
427}
428
429// ----------------------------------------------------------------------------------------------------------------------------------------------
430// Exportable bounds (still hidden)
431
432#[doc(hidden)]
433pub trait Exportable: Sealed {}
434
435#[doc(hidden)]
436pub enum Yes {}
437impl Sealed for Yes {}
438impl Exportable for Yes {}
439
440#[doc(hidden)]
441pub enum No {}
442impl Sealed for No {}
443impl Exportable for No {}