This is an automated email from the ASF dual-hosted git repository.

tlopex pushed a commit to branch rust-borrowed-object-cast
in repository https://gitbox.apache.org/repos/asf/tvm-ffi.git

commit 0adb5a54fd4d9f9fc83e889a8d18f0380be72cda
Author: tlopex <[email protected]>
AuthorDate: Wed Sep 2 23:43:05 2026 -0400

    [RUST] Add borrowed object node cast
---
 rust/tvm-ffi/src/object.rs      | 20 ++++++++++++++++++++
 rust/tvm-ffi/tests/test_cast.rs | 21 +++++++++++++++++++++
 2 files changed, 41 insertions(+)

diff --git a/rust/tvm-ffi/src/object.rs b/rust/tvm-ffi/src/object.rs
index ddfbcf55..d690a64b 100644
--- a/rust/tvm-ffi/src/object.rs
+++ b/rust/tvm-ffi/src/object.rs
@@ -144,6 +144,21 @@ pub unsafe trait ObjectRefCore: Sized + Clone {
                 == ObjectArc::as_raw(Other::data(other)).cast::<()>()
         }
     }
+
+    /// Borrow the underlying object as node type `N` when its runtime type 
matches.
+    ///
+    /// Unlike [`ObjectRefCast::try_cast`], this method neither consumes the
+    /// reference nor changes the object's reference count. The returned node
+    /// cannot outlive `self`.
+    #[inline(always)]
+    fn as_node<N: ObjectCore>(&self) -> Option<&N> {
+        let object = unsafe { ObjectArc::as_raw(Self::data(self)) };
+        let type_index = unsafe { (*object.cast::<TVMFFIObject>()).type_index 
};
+        if !is_instance_of::<N>(type_index) {
+            return None;
+        }
+        Some(unsafe { &*object.cast::<N>() })
+    }
 }
 
 /// An owning, hashable identity key for an FFI object.
@@ -217,6 +232,11 @@ pub fn is_instance_of<Target: 
ObjectCore>(object_type_index: i32) -> bool {
     if object_type_index == target_type_index {
         return true;
     }
+    // A final type cannot have a separately registered subtype. Keep common
+    // borrowed checks, such as `IntImmObj`, to one integer comparison.
+    if Target::TYPE_FINAL {
+        return false;
+    }
     let object_begin = TypeIndex::kTVMFFIStaticObjectBegin as i32;
     // Only object types participate in the type hierarchy.
     if object_type_index < object_begin || target_type_index < object_begin {
diff --git a/rust/tvm-ffi/tests/test_cast.rs b/rust/tvm-ffi/tests/test_cast.rs
index 29689808..4cd1ce90 100644
--- a/rust/tvm-ffi/tests/test_cast.rs
+++ b/rust/tvm-ffi/tests/test_cast.rs
@@ -54,6 +54,7 @@ struct TestBase {
 #[repr(C)]
 #[derive(Object)]
 #[type_key = "testing.TestObjectDerived"]
+#[type_final]
 struct TestDerivedObj {
     base: TestBaseObj,
     extra: i64,
@@ -142,6 +143,26 @@ fn test_upcast_downcast_roundtrip() {
     assert_eq!(delete_counter.load(Ordering::Relaxed), 1);
 }
 
+#[test]
+fn test_borrowed_node_cast_preserves_reference_count() {
+    let delete_counter = Arc::new(AtomicU32::new(0));
+    let base: TestBase = new_derived(7, 8, delete_counter.clone())
+        .try_cast()
+        .unwrap();
+    let strong_count = ObjectArc::strong_count(TestBase::data(&base));
+
+    let derived = base.as_node::<TestDerivedObj>().unwrap();
+    assert_eq!(derived.base.value, 7);
+    assert_eq!(derived.extra, 8);
+    let base_node = base.as_node::<TestBaseObj>().unwrap();
+    assert_eq!(base_node.value, 7);
+    assert_eq!(ObjectArc::strong_count(TestBase::data(&base)), strong_count);
+
+    let base_only = new_base(1, delete_counter.clone());
+    assert!(base_only.as_node::<TestDerivedObj>().is_none());
+    assert_eq!(ObjectArc::strong_count(TestBase::data(&base_only)), 1);
+}
+
 #[test]
 fn test_generated_borrow_and_upcast_conversions() {
     let delete_counter = Arc::new(AtomicU32::new(0));

Reply via email to