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));
