Skip to content

Commit 3d7575f

Browse files
authored
Merge pull request #737 from godot-rust/qol/remove-not-unique-error
Remove `NotUniqueError`
2 parents 7ebd5ec + dd9a6ab commit 3d7575f

File tree

4 files changed

+145
-146
lines changed

4 files changed

+145
-146
lines changed

godot-core/src/obj/bounds.rs

+52-23
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
//! Different ways how bounds of a `GodotClass` can be checked.
99
//!
10-
//! This module contains three traits that can be used to check the characteristics of a `GodotClass` type:
10+
//! This module contains multiple traits that can be used to check the characteristics of a `GodotClass` type:
1111
//!
1212
//! 1. [`Declarer`] tells you whether the class is provided by the engine or user-defined.
1313
//! - [`DeclEngine`] is used for all classes provided by the engine (e.g. `Node3D`).
@@ -19,15 +19,16 @@
1919
//! - [`MemRefCounted`] is used for `RefCounted` classes and derived.
2020
//! - [`MemManual`] is used for `Object` and all inherited classes, which are not `RefCounted` (e.g. `Node`).<br><br>
2121
//!
22-
//! 3. [`DynMemory`] is used to check the memory strategy of the **dynamic** type.
23-
//!
24-
//! When you operate on methods of `T` or `Gd<T>` and are interested in instances, you can use this.
25-
//! 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.
26-
//! - [`MemRefCounted`] is used for `RefCounted` classes and derived. These are **always** reference-counted.
27-
//! - [`MemManual`] is used instances inheriting `Object`, which are not `RefCounted` (e.g. `Node`). Excludes `Object` itself. These are
28-
//! **always** manually managed.
29-
//! - [`MemDynamic`] is used for `Object` instances. `Gd<Object>` can point to objects of any possible class, so whether we are dealing with
30-
//! a ref-counted or manually-managed object is determined only at runtime.
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.
3132
//!
3233
//!
3334
//! # Example
@@ -44,7 +45,7 @@
4445
//! }
4546
//! ```
4647
//!
47-
//! Note that depending on if you want to exclude `Object`, you should use `DynMemory` instead of `Memory`.
48+
// Note that depending on if you want to exclude `Object`, you should use `DynMemory` instead of `Memory`.
4849

4950
use crate::obj::cap::GodotDefault;
5051
use crate::obj::{Bounds, Gd, GodotClass, RawGd};
@@ -86,6 +87,10 @@ pub(super) mod private {
8687
/// Defines the memory strategy of the static type.
8788
type Memory: Memory;
8889

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)]
8994
/// Defines the memory strategy of the instance (at runtime).
9095
type DynMemory: DynMemory;
9196

@@ -146,6 +151,7 @@ pub trait Memory: Sealed {}
146151
/// Specifies the memory strategy of the dynamic type.
147152
///
148153
/// For `Gd<Object>`, it is determined at runtime whether the instance is manually managed or ref-counted.
154+
#[doc(hidden)]
149155
pub trait DynMemory: Sealed {
150156
/// Initialize reference counter
151157
#[doc(hidden)]
@@ -175,6 +181,10 @@ pub trait DynMemory: Sealed {
175181
#[doc(hidden)]
176182
fn is_ref_counted<T: GodotClass>(obj: &RawGd<T>) -> Option<bool>;
177183

184+
/// Return the reference count, or `None` if the object is dead or manually managed.
185+
#[doc(hidden)]
186+
fn get_ref_count<T: GodotClass>(obj: &RawGd<T>) -> Option<usize>;
187+
178188
/// Returns `true` if argument and return pointers are passed as `Ref<T>` pointers given this
179189
/// [`PtrcallType`].
180190
///
@@ -236,38 +246,46 @@ impl DynMemory for MemRefCounted {
236246
Some(true)
237247
}
238248

249+
fn get_ref_count<T: GodotClass>(obj: &RawGd<T>) -> Option<usize> {
250+
let ref_count = obj.with_ref_counted(|refc| refc.get_reference_count());
251+
252+
// TODO find a safer cast alternative, e.g. num-traits crate with ToPrimitive (Debug) + AsPrimitive (Release).
253+
Some(ref_count as usize)
254+
}
255+
239256
fn pass_as_ref(call_type: sys::PtrcallType) -> bool {
240257
matches!(call_type, sys::PtrcallType::Virtual)
241258
}
242259
}
243260

244261
/// Memory managed through Godot reference counter, if present; otherwise manual.
245262
/// This is used only for `Object` classes.
263+
#[doc(hidden)]
246264
pub struct MemDynamic {}
265+
impl MemDynamic {
266+
/// Check whether dynamic type is ref-counted.
267+
fn inherits_refcounted<T: GodotClass>(obj: &RawGd<T>) -> bool {
268+
obj.instance_id_unchecked()
269+
.map(|id| id.is_ref_counted())
270+
.unwrap_or(false)
271+
}
272+
}
247273
impl Sealed for MemDynamic {}
248274
impl DynMemory for MemDynamic {
249275
fn maybe_init_ref<T: GodotClass>(obj: &mut RawGd<T>) {
250276
out!(" MemDyn::init <{}>", std::any::type_name::<T>());
251-
if obj
252-
.instance_id_unchecked()
253-
.map(|id| id.is_ref_counted())
254-
.unwrap_or(false)
255-
{
277+
if Self::inherits_refcounted(obj) {
256278
// Will call `RefCounted::init_ref()` which checks for liveness.
257-
out!("MemDyn -> MemRefc");
279+
out!(" MemDyn -> MemRefc");
258280
MemRefCounted::maybe_init_ref(obj)
259281
} else {
260-
out!("MemDyn -> MemManu");
282+
out!(" MemDyn -> MemManu");
261283
}
262284
}
263285

264286
fn maybe_inc_ref<T: GodotClass>(obj: &mut RawGd<T>) {
265287
out!(" MemDyn::inc <{}>", std::any::type_name::<T>());
266-
if obj
267-
.instance_id_unchecked()
268-
.map(|id| id.is_ref_counted())
269-
.unwrap_or(false)
270-
{
288+
if Self::inherits_refcounted(obj) {
271289
// Will call `RefCounted::reference()` which checks for liveness.
272290
MemRefCounted::maybe_inc_ref(obj)
273291
}
@@ -291,6 +309,14 @@ impl DynMemory for MemDynamic {
291309
// Return `None` if obj is dead
292310
obj.instance_id_unchecked().map(|id| id.is_ref_counted())
293311
}
312+
313+
fn get_ref_count<T: GodotClass>(obj: &RawGd<T>) -> Option<usize> {
314+
if Self::inherits_refcounted(obj) {
315+
MemRefCounted::get_ref_count(obj)
316+
} else {
317+
None
318+
}
319+
}
294320
}
295321

296322
/// No memory management, user responsible for not leaking.
@@ -307,6 +333,9 @@ impl DynMemory for MemManual {
307333
fn is_ref_counted<T: GodotClass>(_obj: &RawGd<T>) -> Option<bool> {
308334
Some(false)
309335
}
336+
fn get_ref_count<T: GodotClass>(_obj: &RawGd<T>) -> Option<usize> {
337+
None
338+
}
310339
}
311340

312341
// ----------------------------------------------------------------------------------------------------------------------------------------------

godot-core/src/obj/gd.rs

+41-81
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ impl<T: GodotClass> Gd<T> {
411411
#[doc(hidden)]
412412
pub fn script_sys(&self) -> sys::GDExtensionScriptLanguagePtr
413413
where
414-
T: Inherits<crate::classes::ScriptLanguage>,
414+
T: Inherits<classes::ScriptLanguage>,
415415
{
416416
self.raw.script_sys()
417417
}
@@ -553,6 +553,44 @@ where
553553
}
554554
}
555555

556+
/// _The methods in this impl block are only available for objects `T` that are reference-counted,
557+
/// i.e. anything that inherits `RefCounted`._ <br><br>
558+
impl<T> Gd<T>
559+
where
560+
T: GodotClass + Bounds<Memory = bounds::MemRefCounted>,
561+
{
562+
/// Makes sure that `self` does not share references with other `Gd` instances.
563+
///
564+
/// Succeeds if the reference count is 1.
565+
/// Otherwise, returns the shared object and its reference count.
566+
///
567+
/// ## Example
568+
///
569+
/// ```no_run
570+
/// use godot::prelude::*;
571+
///
572+
/// let obj = RefCounted::new_gd();
573+
/// match obj.try_to_unique() {
574+
/// Ok(unique_obj) => {
575+
/// // No other Gd<T> shares a reference with `unique_obj`.
576+
/// },
577+
/// Err((shared_obj, ref_count)) => {
578+
/// // `shared_obj` is the original object `obj`.
579+
/// // `ref_count` is the total number of references (including one held by `shared_obj`).
580+
/// }
581+
/// }
582+
/// ```
583+
pub fn try_to_unique(self) -> Result<Self, (Self, usize)> {
584+
use crate::obj::bounds::DynMemory as _;
585+
586+
match <T as Bounds>::DynMemory::get_ref_count(&self.raw) {
587+
Some(1) => Ok(self),
588+
Some(ref_count) => Err((self, ref_count)),
589+
None => unreachable!(),
590+
}
591+
}
592+
}
593+
556594
// ----------------------------------------------------------------------------------------------------------------------------------------------
557595
// Trait impls
558596

@@ -737,83 +775,5 @@ impl<T: GodotClass> std::hash::Hash for Gd<T> {
737775
impl<T: GodotClass> std::panic::UnwindSafe for Gd<T> {}
738776
impl<T: GodotClass> std::panic::RefUnwindSafe for Gd<T> {}
739777

740-
/// Error stemming from the non-uniqueness of the [`Gd`] instance.
741-
///
742-
/// Keeping track of the uniqueness of references can be crucial in many applications, especially if we want to ensure
743-
/// that the passed [`Gd`] reference will be possessed by only one different object instance or function in its lifetime.
744-
///
745-
/// Only applicable to [`GodotClass`] objects that inherit from [`RefCounted`](crate::gen::classes::RefCounted). To check the
746-
/// uniqueness, call the `check()` associated method.
747-
///
748-
/// ## Example
749-
///
750-
/// ```no_run
751-
/// use godot::prelude::*;
752-
/// use godot::obj::NotUniqueError;
753-
///
754-
/// let shared = RefCounted::new_gd();
755-
/// let cloned = shared.clone();
756-
/// let result = NotUniqueError::check(shared);
757-
///
758-
/// assert!(result.is_err());
759-
///
760-
/// if let Err(error) = result {
761-
/// assert_eq!(error.get_reference_count(), 2)
762-
/// }
763-
/// ```
764-
#[derive(Debug)]
765-
pub struct NotUniqueError {
766-
reference_count: i32,
767-
}
768-
769-
impl NotUniqueError {
770-
/// check [`Gd`] reference uniqueness.
771-
///
772-
/// Checks the [`Gd`] of the [`GodotClass`](crate::obj::GodotClass) that inherits from [`RefCounted`](crate::gen::classes::RefCounted)
773-
/// if it is an unique reference to the object.
774-
///
775-
/// ## Example
776-
///
777-
/// ```no_run
778-
/// use godot::prelude::*;
779-
/// use godot::obj::NotUniqueError;
780-
///
781-
/// let unique = RefCounted::new_gd();
782-
/// assert!(NotUniqueError::check(unique).is_ok());
783-
///
784-
/// let shared = RefCounted::new_gd();
785-
/// let cloned = shared.clone();
786-
/// assert!(NotUniqueError::check(shared).is_err());
787-
/// assert!(NotUniqueError::check(cloned).is_err());
788-
/// ```
789-
pub fn check<T>(rc: Gd<T>) -> Result<Gd<T>, Self>
790-
where
791-
T: Inherits<crate::gen::classes::RefCounted>,
792-
{
793-
let rc = rc.upcast::<crate::gen::classes::RefCounted>();
794-
let reference_count = rc.get_reference_count();
795-
796-
if reference_count != 1 {
797-
Err(Self { reference_count })
798-
} else {
799-
Ok(rc.cast::<T>())
800-
}
801-
}
802-
803-
/// Get the detected reference count
804-
pub fn get_reference_count(&self) -> i32 {
805-
self.reference_count
806-
}
807-
}
808-
809-
impl std::error::Error for NotUniqueError {}
810-
811-
impl std::fmt::Display for NotUniqueError {
812-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
813-
write!(
814-
f,
815-
"pointer is not unique, current reference count: {}",
816-
self.reference_count
817-
)
818-
}
819-
}
778+
#[deprecated = "Removed; see `Gd::try_to_unique()`"]
779+
pub type NotUniqueError = ();

0 commit comments

Comments
 (0)