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::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///
166/// This trait only answers *whether* an object is ref-counted; the ref-counting operations themselves are the same in all cases and
167/// live on [`RawGd`]. For `MemRefCounted`/`MemManual`, the answer is known at compile-time and can be optimized.
168#[doc(hidden)]
169pub trait DynMemory: Sealed {
170 /// Check if ref-counted, return `None` if information is not available (dynamic and obj dead).
171 #[doc(hidden)]
172 fn is_ref_counted<T: GodotClass>(obj: &RawGd<T>) -> Option<bool>;
173
174 /// Returns `true` if argument and return pointers are passed as `Ref<T>` pointers given this
175 /// [`PtrcallType`].
176 ///
177 /// See [`PtrcallType::Virtual`] for information about `Ref<T>` objects.
178 #[doc(hidden)]
179 fn pass_as_ref(_call_type: sys::PtrcallType) -> bool {
180 false
181 }
182}
183
184/// Memory managed through Godot reference counter (always present).
185/// This is used for `RefCounted` classes and derived.
186pub struct MemRefCounted {}
187impl Sealed for MemRefCounted {}
188impl Memory for MemRefCounted {
189 const IS_REF_COUNTED: bool = true;
190}
191impl DynMemory for MemRefCounted {
192 fn is_ref_counted<T: GodotClass>(_obj: &RawGd<T>) -> Option<bool> {
193 Some(true)
194 }
195
196 fn pass_as_ref(call_type: sys::PtrcallType) -> bool {
197 matches!(call_type, sys::PtrcallType::Virtual)
198 }
199}
200
201/// Memory managed through Godot reference counter, if present; otherwise manual.
202/// This is used only for `Object` classes.
203#[doc(hidden)]
204pub struct MemDynamic {}
205impl Sealed for MemDynamic {}
206impl DynMemory for MemDynamic {
207 fn is_ref_counted<T: GodotClass>(obj: &RawGd<T>) -> Option<bool> {
208 // Return `None` if obj is dead. The instance ID carries a ref-countedness bit, so this needs no FFI call.
209 obj.instance_id_unchecked().map(|id| id.is_ref_counted())
210 }
211}
212
213/// No memory management, user responsible for not leaking.
214/// This is used for all `Object` derivates, which are not `RefCounted`. `Object` itself is also excluded.
215pub struct MemManual {}
216impl Sealed for MemManual {}
217impl Memory for MemManual {
218 const IS_REF_COUNTED: bool = false;
219}
220impl DynMemory for MemManual {
221 fn is_ref_counted<T: GodotClass>(_obj: &RawGd<T>) -> Option<bool> {
222 Some(false)
223 }
224}
225
226// ----------------------------------------------------------------------------------------------------------------------------------------------
227// Declarer bounds
228
229/// Trait that specifies who declares a given `GodotClass`.
230pub trait Declarer: Sealed {
231 /// The target type of a `Deref` operation on a `Gd<T>`.
232 #[doc(hidden)]
233 type DerefTarget<T: GodotClass>: GodotClass;
234
235 /// Used as a field in `RawGd`; only set for user-defined classes.
236 #[doc(hidden)]
237 #[allow(private_bounds)]
238 type InstanceCache: InstanceCache;
239
240 /// Check if the object is a user object *and* currently locked by a `bind()` or `bind_mut()` guard.
241 ///
242 /// # Safety
243 /// Object must be alive.
244 #[doc(hidden)]
245 unsafe fn is_currently_bound<T>(obj: &RawGd<T>) -> bool
246 where
247 T: GodotClass + Bounds<Declarer = Self>;
248
249 #[doc(hidden)]
250 fn create_gd<T>() -> Gd<T>
251 where
252 T: GodotDefault + Bounds<Declarer = Self>;
253}
254
255/// Expresses that a class is declared by the Godot engine.
256pub enum DeclEngine {}
257impl Sealed for DeclEngine {}
258impl Declarer for DeclEngine {
259 type DerefTarget<T: GodotClass> = T;
260 type InstanceCache = ();
261
262 unsafe fn is_currently_bound<T>(_obj: &RawGd<T>) -> bool
263 where
264 T: GodotClass + Bounds<Declarer = Self>,
265 {
266 false
267 }
268
269 fn create_gd<T>() -> Gd<T>
270 where
271 T: GodotDefault + Bounds<Declarer = Self>,
272 {
273 crate::classes::construct_engine_object()
274 }
275}
276
277/// Expresses that a class is declared by the user.
278pub enum DeclUser {}
279impl Sealed for DeclUser {}
280impl Declarer for DeclUser {
281 type DerefTarget<T: GodotClass> = T::Base;
282 type InstanceCache = std::cell::Cell<sys::GDExtensionClassInstancePtr>;
283
284 unsafe fn is_currently_bound<T>(obj: &RawGd<T>) -> bool
285 where
286 T: GodotClass + Bounds<Declarer = Self>,
287 {
288 // `storage()` returns `None` for placeholder instances (runtime classes accessed in editor); they hold no Rust binding to be bound.
289 // Treat as not currently bound -- callers like `Gd::free()` use this only to detect active `bind()` / `bind_mut()` guards.
290 match obj.storage() {
291 Some(storage) => storage.is_bound(),
292 None => false,
293 }
294 }
295
296 fn create_gd<T>() -> Gd<T>
297 where
298 T: GodotDefault + Bounds<Declarer = Self>,
299 {
300 Gd::default_instance()
301 }
302}
303
304// ----------------------------------------------------------------------------------------------------------------------------------------------
305// Exportable bounds (still hidden)
306
307#[doc(hidden)]
308pub trait Exportable: Sealed {}
309
310#[doc(hidden)]
311pub enum Yes {}
312impl Sealed for Yes {}
313impl Exportable for Yes {}
314
315#[doc(hidden)]
316pub enum No {}
317impl Sealed for No {}
318impl Exportable for No {}