Skip to content

Commit e510e0b

Browse files
committed
Add bounds for future-proof Gd deref
The current `RawGd::as_upcast_ref/mut` rely on `mem::transmute` to convert between `&Gd<T>` and `&Base`. This works as long as Base is an engine-defined class, which is currently always given. However, if Rust-Rust inheritance is introduced at some point in the future, this assumption no longer holds. The new bound ensures soundness in such cases.
1 parent 72d6d17 commit e510e0b

File tree

3 files changed

+33
-6
lines changed

3 files changed

+33
-6
lines changed

godot-core/src/obj/gd.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -780,7 +780,14 @@ where
780780
// ----------------------------------------------------------------------------------------------------------------------------------------------
781781
// Trait impls
782782

783-
impl<T: GodotClass> Deref for Gd<T> {
783+
/// Dereferences to the nearest engine class, enabling direct calls to its `&self` methods.
784+
///
785+
/// For engine classes, returns `T` itself. For user classes, returns `T::Base` (the direct engine base class).
786+
/// The bound ensures that the target is always an engine-provided class.
787+
impl<T: GodotClass> Deref for Gd<T>
788+
where
789+
GdDerefTarget<T>: Bounds<Declarer = bounds::DeclEngine>,
790+
{
784791
// Target is always an engine class:
785792
// * if T is an engine class => T
786793
// * if T is a user class => T::Base
@@ -791,7 +798,14 @@ impl<T: GodotClass> Deref for Gd<T> {
791798
}
792799
}
793800

794-
impl<T: GodotClass> DerefMut for Gd<T> {
801+
/// Mutably dereferences to the nearest engine class, enabling direct calls to its `&mut self` methods.
802+
///
803+
/// For engine classes, returns `T` itself. For user classes, returns `T::Base` (the direct engine base class).
804+
/// The bound ensures that the target is always an engine-provided class.
805+
impl<T: GodotClass> DerefMut for Gd<T>
806+
where
807+
GdDerefTarget<T>: Bounds<Declarer = bounds::DeclEngine>,
808+
{
795809
fn deref_mut(&mut self) -> &mut Self::Target {
796810
self.raw.as_target_mut()
797811
}

godot-core/src/obj/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,9 @@ pub use bounds::private::Bounds;
4242

4343
// Do not re-export rtti here.
4444

45+
/// Resolves the type to which a `Gd<T>` dereferences.
46+
///
47+
/// This type alias abstracts over the two `Declarer` options for Godot objects:
48+
/// - [`bounds::DeclEngine`]: for all engine-provided classes, `DerefTarget<T>` is `T`.
49+
/// - [`bounds::DeclUser`]: for Rust-defined user classes, `DerefTarget<T>` is `T::Base`.
4550
type GdDerefTarget<T> = <<T as Bounds>::Declarer as bounds::Declarer>::DerefTarget<T>;

godot-core/src/obj/raw_gd.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,8 @@ impl<T: GodotClass> RawGd<T> {
261261
/// Bounds should be added on user-facing safe APIs.
262262
pub(super) unsafe fn as_upcast_ref<Base>(&self) -> &Base
263263
where
264-
Base: GodotClass,
264+
// DeclEngine needed for sound transmute; in case we add Rust-defined base classes.
265+
Base: GodotClass + Bounds<Declarer = bounds::DeclEngine>,
265266
{
266267
self.ensure_valid_upcast::<Base>();
267268

@@ -299,7 +300,8 @@ impl<T: GodotClass> RawGd<T> {
299300
/// Bounds should be added on user-facing safe APIs.
300301
pub(super) unsafe fn as_upcast_mut<Base>(&mut self) -> &mut Base
301302
where
302-
Base: GodotClass,
303+
// DeclEngine needed for sound transmute; in case we add Rust-defined base classes.
304+
Base: GodotClass + Bounds<Declarer = bounds::DeclEngine>,
303305
{
304306
self.ensure_valid_upcast::<Base>();
305307

@@ -315,7 +317,10 @@ impl<T: GodotClass> RawGd<T> {
315317

316318
/// # Panics
317319
/// If this `RawGd` is null.
318-
pub(super) fn as_target(&self) -> &GdDerefTarget<T> {
320+
pub(super) fn as_target(&self) -> &GdDerefTarget<T>
321+
where
322+
GdDerefTarget<T>: Bounds<Declarer = bounds::DeclEngine>,
323+
{
319324
// SAFETY: There are two possible Declarer::DerefTarget types:
320325
// - T, if T is an engine class
321326
// - T::Base, if T is a user class
@@ -325,7 +330,10 @@ impl<T: GodotClass> RawGd<T> {
325330

326331
/// # Panics
327332
/// If this `RawGd` is null.
328-
pub(super) fn as_target_mut(&mut self) -> &mut GdDerefTarget<T> {
333+
pub(super) fn as_target_mut(&mut self) -> &mut GdDerefTarget<T>
334+
where
335+
GdDerefTarget<T>: Bounds<Declarer = bounds::DeclEngine>,
336+
{
329337
// SAFETY: See as_target().
330338
unsafe { self.as_upcast_mut::<GdDerefTarget<T>>() }
331339
}

0 commit comments

Comments
 (0)