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

tqchen pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm-ffi.git


The following commit(s) were added to refs/heads/main by this push:
     new a21f5fa7 [Rust] Borrow references and values in place in downcast_ref, 
dispatch and match_any!; add Optional::as_ref (#931)
a21f5fa7 is described below

commit a21f5fa79329cc63b46c0cf087ebcf7ac4516686
Author: Tianqi Chen <[email protected]>
AuthorDate: Thu Nov 5 09:54:51 2026 -0500

    [Rust] Borrow references and values in place in downcast_ref, dispatch and 
match_any!; add Optional::as_ref (#931)
    
    Extends `downcast_ref` to borrow values in place, like
    `std::any::Any::downcast_ref`, without touching reference counts.
    
    - **`Any::downcast_ref` / `AnyView::downcast_ref`** borrow managed
    references (`Array<T>`, `Map`, `List`, every `#[derive(ObjectRef)]`
    type) and values stored in the cell as themselves (`String`, `Bytes`,
    `i64`, `f64`), as well as object types (`ArrayObj`). The check is
    strict, as in `try_as`, and never converts. An `AnyView` borrow is tied
    to the view, which holds its own copy of the cell.
    - **Object references' `downcast_ref`** borrows another reference type
    in place: `&Expr` borrows as `&Add` when the expression is an `Add`.
    This is the reverse of an upcast.
    - **`#[dispatch]` and `match_any!`.** Handlers taking `&Type` and arms
    `Type(ref name)` accept every type `downcast_ref` does. A managed
    reference (`&Array<i64>`, `&Add`) or a value (`&String`) is borrowed
    from the source's cell in place, as `&NodeObj` already was. A
    reference-type `ref` arm used to clone the handle; it no longer does,
    and the existing tests' expected reference counts are updated to match.
    - **`Optional::as_ref`** borrows the value, like `Option::as_ref`, for
    values the cell stores as themselves.
    - The hidden traits: `RefFromAny` is renamed `RefFromAnyUnchecked`
    (std's `_unchecked`: the caller guarantees the type).
    `DowncastRefFromAny` and `DowncastRefFromObject` are the checked forms
    that back `downcast_ref`. Object types are covered by a blanket impl, so
    existing `downcast_ref::<FooObj>()` calls and generic callers are
    unchanged.
---
 rust/tvm-ffi-macros/src/object_macros.rs        |  22 +++-
 rust/tvm-ffi/src/any.rs                         |  39 +++---
 rust/tvm-ffi/src/collections/array.rs           |  30 ++++-
 rust/tvm-ffi/src/collections/list.rs            |  24 +++-
 rust/tvm-ffi/src/collections/map.rs             |  26 +++-
 rust/tvm-ffi/src/extra/dispatch.rs              |   3 +-
 rust/tvm-ffi/src/extra/structural_mutate.rs     |   8 ++
 rust/tvm-ffi/src/lib.rs                         |   4 +-
 rust/tvm-ffi/src/match_any_internal.rs          |  25 +++-
 rust/tvm-ffi/src/object.rs                      |  27 ++--
 rust/tvm-ffi/src/optional.rs                    |  27 ++++
 rust/tvm-ffi/src/type_traits.rs                 | 158 +++++++++++++++++++++---
 rust/tvm-ffi/tests/test_match_any.rs            |   4 +-
 rust/tvm-ffi/tests/test_match_any_ast_lookup.rs |  20 +--
 14 files changed, 335 insertions(+), 82 deletions(-)

diff --git a/rust/tvm-ffi-macros/src/object_macros.rs 
b/rust/tvm-ffi-macros/src/object_macros.rs
index c62c6448..4808d130 100644
--- a/rust/tvm-ffi-macros/src/object_macros.rs
+++ b/rust/tvm-ffi-macros/src/object_macros.rs
@@ -567,9 +567,9 @@ pub(crate) fn expand_object_ref(
             );
         };
 
-        unsafe impl #tvm_ffi_crate::type_traits::RefFromAny for #struct_name {
+        unsafe impl #tvm_ffi_crate::type_traits::RefFromAnyUnchecked for 
#struct_name {
             #[inline(always)]
-            unsafe fn ref_from_any(cell: 
&#tvm_ffi_crate::tvm_ffi_sys::TVMFFIAny) -> &Self {
+            unsafe fn ref_from_any_unchecked(cell: 
&#tvm_ffi_crate::tvm_ffi_sys::TVMFFIAny) -> &Self {
                 &*(::core::ptr::addr_of!(cell.data_union) as *const Self)
             }
         }
@@ -583,6 +583,24 @@ pub(crate) fn expand_object_ref(
             }
         }
 
+        unsafe impl #tvm_ffi_crate::type_traits::DowncastRefFromAny for 
#struct_name {
+            #[inline(always)]
+            unsafe fn downcast_ref_from_any(
+                cell: &#tvm_ffi_crate::tvm_ffi_sys::TVMFFIAny,
+            ) -> ::core::option::Option<&Self> {
+                
#tvm_ffi_crate::type_traits::downcast_ref_from_any_in_place::<Self>(cell)
+            }
+        }
+
+        unsafe impl #tvm_ffi_crate::type_traits::DowncastRefFromObject for 
#struct_name {
+            #[inline(always)]
+            unsafe fn downcast_ref_from_object(
+                place: &*mut #tvm_ffi_crate::tvm_ffi_sys::TVMFFIObject,
+            ) -> ::core::option::Option<&Self> {
+                
#tvm_ffi_crate::type_traits::downcast_ref_from_object_in_place::<Self>(place)
+            }
+        }
+
         impl ::std::convert::From<&#struct_name> for #struct_name {
             #[inline]
             fn from(value: &#struct_name) -> Self {
diff --git a/rust/tvm-ffi/src/any.rs b/rust/tvm-ffi/src/any.rs
index a853822f..1f4d2081 100644
--- a/rust/tvm-ffi/src/any.rs
+++ b/rust/tvm-ffi/src/any.rs
@@ -156,28 +156,23 @@ impl<'a> AnyView<'a> {
         }
     }
 
-    /// Borrow the viewed object as `T` when its runtime type is `T` or a 
subtype.
+    /// Borrow the viewed value as `T` when it holds one, without copying it.
     ///
-    /// `T` is an object representation type such as `ArrayObj`. Non-object 
values
-    /// and unrelated object types return `None`. This does not change the 
object's
-    /// reference count; the borrow cannot outlive this view or the owner it
-    /// borrows.
+    /// `T` is an object type such as `ArrayObj`, a managed reference such as
+    /// `Array<i64>`, or a value stored in place such as `String` or `i64`. The
+    /// check is strict, as in [`try_as`](Self::try_as), and never converts.
+    /// The borrow does not change any reference count and is tied to this 
view,
+    /// which holds its own copy of the value's cell.
     ///
     /// # Returns
     ///
-    /// The borrowed object, or `None`.
+    /// The borrowed value, or `None`.
     ///
     /// See also [`Any::downcast_ref`].
     #[inline(always)]
-    pub fn downcast_ref<T: object::ObjectCore>(&self) -> Option<&T> {
-        if self.data.type_index < TypeIndex::kTVMFFIStaticObjectBegin as i32
-            || !object::is_instance_of::<T>(self.data.type_index)
-        {
-            return None;
-        }
-        // SAFETY: the type check establishes T's layout, and AnyView keeps
-        // the allocation borrowed for at least the lifetime of this reference.
-        Some(unsafe { &*self.data.data_union.v_obj.cast::<T>() })
+    pub fn downcast_ref<T: crate::type_traits::DowncastRefFromAny>(&self) -> 
Option<&T> {
+        // SAFETY: the view holds a valid cell for as long as it is borrowed.
+        unsafe { T::downcast_ref_from_any(&self.data) }
     }
 
     /// Return the viewed value as a `T` when it is stored as exactly `T`.
@@ -329,19 +324,15 @@ impl Any {
     /// use tvm_ffi::{collections::array::ArrayObj, Any, Array};
     /// let value = Any::from(Array::from_vec(vec![1_i64, 2]));
     /// assert_eq!(value.downcast_ref::<ArrayObj>().unwrap().size, 2);
+    /// assert_eq!(value.downcast_ref::<Array<i64>>().unwrap()[1], 2);
+    /// assert!(value.downcast_ref::<i64>().is_none());
     /// ```
     ///
     /// See also [`try_as`](Self::try_as).
     #[inline]
-    pub fn downcast_ref<T: object::ObjectCore>(&self) -> Option<&T> {
-        if self.data.type_index < TypeIndex::kTVMFFIStaticObjectBegin as i32
-            || !object::is_instance_of::<T>(self.data.type_index)
-        {
-            return None;
-        }
-        // SAFETY: the object tag and runtime type check guarantee a live T
-        // layout, and this borrow is bounded by the owning Any's lifetime.
-        Some(unsafe { &*self.data.data_union.v_obj.cast::<T>() })
+    pub fn downcast_ref<T: crate::type_traits::DowncastRefFromAny>(&self) -> 
Option<&T> {
+        // SAFETY: the owned cell stays valid for as long as `self` is 
borrowed.
+        unsafe { T::downcast_ref_from_any(&self.data) }
     }
 
     /// Return the stored value as a `T` when it is stored as exactly `T`.
diff --git a/rust/tvm-ffi/src/collections/array.rs 
b/rust/tvm-ffi/src/collections/array.rs
index 07249398..c76ceb72 100644
--- a/rust/tvm-ffi/src/collections/array.rs
+++ b/rust/tvm-ffi/src/collections/array.rs
@@ -24,7 +24,7 @@ use crate::any::TryFromTemp;
 use crate::collections::list::seq_cells;
 use crate::derive::Object;
 use crate::object::{Object, ObjectArc};
-use crate::type_traits::{ContainerElement, RefFromAny};
+use crate::type_traits::{ContainerElement, RefFromAnyUnchecked};
 use crate::{Any, AnyCompatible, AnyView, ObjectCoreWithExtraItems, 
ObjectRefCore};
 use tvm_ffi_sys::TVMFFITypeIndex as TypeIndex;
 use tvm_ffi_sys::{TVMFFIAny, TVMFFIObject};
@@ -262,7 +262,7 @@ impl<T: ContainerElement + Clone> Array<T> {
 
 /// Lends `&T` from the array's cell in place. Every cell of an `Array<T>`
 /// holds a `T` (checked when the array is built), so only a debug check runs.
-impl<T: ContainerElement + Clone + RefFromAny> std::ops::Index<usize> for 
Array<T> {
+impl<T: ContainerElement + Clone + RefFromAnyUnchecked> std::ops::Index<usize> 
for Array<T> {
     type Output = T;
 
     fn index(&self, index: usize) -> &T {
@@ -277,7 +277,7 @@ impl<T: ContainerElement + Clone + RefFromAny> 
std::ops::Index<usize> for Array<
         unsafe {
             let cell = &*(container.data as *const TVMFFIAny).add(index);
             debug_assert!(T::container_check_any_strict(cell));
-            T::ref_from_any(cell)
+            T::ref_from_any_unchecked(cell)
         }
     }
 }
@@ -425,16 +425,36 @@ where
 }
 
 // The cell's payload word is the single object pointer of the handle.
-unsafe impl<T> crate::type_traits::RefFromAny for Array<T>
+unsafe impl<T> crate::type_traits::RefFromAnyUnchecked for Array<T>
 where
     T: ContainerElement + Clone,
 {
     #[inline(always)]
-    unsafe fn ref_from_any(cell: &TVMFFIAny) -> &Self {
+    unsafe fn ref_from_any_unchecked(cell: &TVMFFIAny) -> &Self {
         &*(core::ptr::addr_of!(cell.data_union) as *const Self)
     }
 }
 
+unsafe impl<T> crate::type_traits::DowncastRefFromAny for Array<T>
+where
+    T: ContainerElement + Clone,
+{
+    #[inline(always)]
+    unsafe fn downcast_ref_from_any(cell: &TVMFFIAny) -> Option<&Self> {
+        crate::type_traits::downcast_ref_from_any_in_place::<Self>(cell)
+    }
+}
+
+unsafe impl<T> crate::type_traits::DowncastRefFromObject for Array<T>
+where
+    T: ContainerElement + Clone,
+{
+    #[inline(always)]
+    unsafe fn downcast_ref_from_object(place: &*mut TVMFFIObject) -> 
Option<&Self> {
+        crate::type_traits::downcast_ref_from_object_in_place::<Self>(place)
+    }
+}
+
 impl<T> TryFrom<Any> for Array<T>
 where
     T: ContainerElement + Clone,
diff --git a/rust/tvm-ffi/src/collections/list.rs 
b/rust/tvm-ffi/src/collections/list.rs
index dea75f18..8da80fc3 100644
--- a/rust/tvm-ffi/src/collections/list.rs
+++ b/rust/tvm-ffi/src/collections/list.rs
@@ -657,16 +657,36 @@ unsafe impl<T: ContainerElement> AnyCompatible for 
List<T> {
 }
 
 // The cell's payload word is the single object pointer of the handle.
-unsafe impl<T> crate::type_traits::RefFromAny for List<T>
+unsafe impl<T> crate::type_traits::RefFromAnyUnchecked for List<T>
 where
     T: ContainerElement,
 {
     #[inline(always)]
-    unsafe fn ref_from_any(cell: &TVMFFIAny) -> &Self {
+    unsafe fn ref_from_any_unchecked(cell: &TVMFFIAny) -> &Self {
         &*(core::ptr::addr_of!(cell.data_union) as *const Self)
     }
 }
 
+unsafe impl<T> crate::type_traits::DowncastRefFromAny for List<T>
+where
+    T: ContainerElement,
+{
+    #[inline(always)]
+    unsafe fn downcast_ref_from_any(cell: &TVMFFIAny) -> Option<&Self> {
+        crate::type_traits::downcast_ref_from_any_in_place::<Self>(cell)
+    }
+}
+
+unsafe impl<T> crate::type_traits::DowncastRefFromObject for List<T>
+where
+    T: ContainerElement,
+{
+    #[inline(always)]
+    unsafe fn downcast_ref_from_object(place: &*mut TVMFFIObject) -> 
Option<&Self> {
+        crate::type_traits::downcast_ref_from_object_in_place::<Self>(place)
+    }
+}
+
 impl<T: ContainerElement> TryFrom<Any> for List<T> {
     type Error = Error;
 
diff --git a/rust/tvm-ffi/src/collections/map.rs 
b/rust/tvm-ffi/src/collections/map.rs
index 2c9b11c1..b8f3c6d1 100644
--- a/rust/tvm-ffi/src/collections/map.rs
+++ b/rust/tvm-ffi/src/collections/map.rs
@@ -572,17 +572,39 @@ where
 }
 
 // The cell's payload word is the single object pointer of the handle.
-unsafe impl<K, V> crate::type_traits::RefFromAny for Map<K, V>
+unsafe impl<K, V> crate::type_traits::RefFromAnyUnchecked for Map<K, V>
 where
     K: ContainerElement,
     V: ContainerElement,
 {
     #[inline(always)]
-    unsafe fn ref_from_any(cell: &TVMFFIAny) -> &Self {
+    unsafe fn ref_from_any_unchecked(cell: &TVMFFIAny) -> &Self {
         &*(core::ptr::addr_of!(cell.data_union) as *const Self)
     }
 }
 
+unsafe impl<K, V> crate::type_traits::DowncastRefFromAny for Map<K, V>
+where
+    K: ContainerElement,
+    V: ContainerElement,
+{
+    #[inline(always)]
+    unsafe fn downcast_ref_from_any(cell: &TVMFFIAny) -> Option<&Self> {
+        crate::type_traits::downcast_ref_from_any_in_place::<Self>(cell)
+    }
+}
+
+unsafe impl<K, V> crate::type_traits::DowncastRefFromObject for Map<K, V>
+where
+    K: ContainerElement,
+    V: ContainerElement,
+{
+    #[inline(always)]
+    unsafe fn downcast_ref_from_object(place: &*mut TVMFFIObject) -> 
Option<&Self> {
+        crate::type_traits::downcast_ref_from_object_in_place::<Self>(place)
+    }
+}
+
 impl<K, V> TryFrom<Any> for Map<K, V>
 where
     K: ContainerElement,
diff --git a/rust/tvm-ffi/src/extra/dispatch.rs 
b/rust/tvm-ffi/src/extra/dispatch.rs
index cb702594..8c39c370 100644
--- a/rust/tvm-ffi/src/extra/dispatch.rs
+++ b/rust/tvm-ffi/src/extra/dispatch.rs
@@ -260,7 +260,8 @@ macro_rules! callback_prefix {
         }
         impl<'v, F, T, O, R, $($A,)*>
             DispatchLink<(AnyView<'v>, $($A,)*), Option<R>, 
marker::FromCallback<$n, marker::BorrowedObject<T>>> for F
-        where F: for<'node> FnMut(&'node T, $($B,)*) -> O, T: 
crate::ObjectCore,
+        where F: for<'node> FnMut(&'node T, $($B,)*) -> O,
+              T: crate::type_traits::DowncastRefFromAny + 'static,
               O: CallbackResult<R>,
         {
             #[inline(always)]
diff --git a/rust/tvm-ffi/src/extra/structural_mutate.rs 
b/rust/tvm-ffi/src/extra/structural_mutate.rs
index 5527531f..1716276c 100644
--- a/rust/tvm-ffi/src/extra/structural_mutate.rs
+++ b/rust/tvm-ffi/src/extra/structural_mutate.rs
@@ -441,6 +441,14 @@ pub struct MutationView<'a, T = Any> {
     _not_send_sync: PhantomData<Rc<()>>,
 }
 
+impl<T> MutationView<'_, T> {
+    /// The viewed cell, borrowed in place by `match_any!` reference patterns.
+    #[inline(always)]
+    pub(crate) fn cell(&self) -> &TVMFFIAny {
+        &self.value
+    }
+}
+
 impl<'a> MutationView<'a> {
     #[inline(always)]
     pub(super) fn new(value: AnyView<'a>, mode: InplaceMode) -> Self {
diff --git a/rust/tvm-ffi/src/lib.rs b/rust/tvm-ffi/src/lib.rs
index d11c972f..66d60f13 100644
--- a/rust/tvm-ffi/src/lib.rs
+++ b/rust/tvm-ffi/src/lib.rs
@@ -111,7 +111,9 @@ pub use crate::type_traits::AnyCompatible;
 ///
 /// A handler's first parameter selects what it matches, as for
 /// [`match_any!`]: an owned FFI value such as `i64` or `Array<Any>`, converted
-/// strictly; `&NodeObj`, which borrows the object node; `MutationView<'_, T>`,
+/// strictly; `&Type` (such as `&Array<i64>` or `&tvm_ffi::String`) or
+/// `&NodeObj`, which borrow the value or the object node in place without
+/// changing a reference count; `MutationView<'_, T>`,
 /// which keeps the permission to mutate in place; or `AnyView<'_>`, which
 /// matches every value and must be the last handler.
 ///
diff --git a/rust/tvm-ffi/src/match_any_internal.rs 
b/rust/tvm-ffi/src/match_any_internal.rs
index c5fd11c6..196731db 100644
--- a/rust/tvm-ffi/src/match_any_internal.rs
+++ b/rust/tvm-ffi/src/match_any_internal.rs
@@ -97,24 +97,38 @@ impl<'a, T> SourceProbe<crate::MutationView<'a, T>> {
 #[doc(hidden)]
 pub trait SourceView {
     fn view(&self) -> AnyView<'_>;
+    /// The source's own cell, which a reference pattern borrows in place.
+    fn cell(&self) -> &crate::tvm_ffi_sys::TVMFFIAny;
 }
 impl SourceView for OwnedSource {
     #[inline(always)]
     fn view(&self) -> AnyView<'_> {
         (&self.0).into()
     }
+    #[inline(always)]
+    fn cell(&self) -> &crate::tvm_ffi_sys::TVMFFIAny {
+        self.0.as_raw_ffi_any()
+    }
 }
 impl SourceView for ViewSource<'_> {
     #[inline(always)]
     fn view(&self) -> AnyView<'_> {
         self.0
     }
+    #[inline(always)]
+    fn cell(&self) -> &crate::tvm_ffi_sys::TVMFFIAny {
+        self.0.as_raw_ffi_any()
+    }
 }
 impl SourceView for CapabilitySource<'_> {
     #[inline(always)]
     fn view(&self) -> AnyView<'_> {
         self.0.as_ref().unwrap().as_any_view()
     }
+    #[inline(always)]
+    fn cell(&self) -> &crate::tvm_ffi_sys::TVMFFIAny {
+        self.0.as_ref().unwrap().cell()
+    }
 }
 
 /// A rejected guard drops an ordinary result or returns the original 
capability.
@@ -265,16 +279,17 @@ pub trait RefPatternConversion<'b, S, T> {
         source: &'b mut S,
     ) -> Result<Matched<Self::Holder, Self::Recovery>, ()>;
 }
-impl<'b, T: crate::object::ObjectCore, S: SourceView> RefPatternConversion<'b, 
S, T>
-    for &&PatternConversionProbe<T>
+impl<'b, T: crate::type_traits::DowncastRefFromAny + 'static, S: SourceView>
+    RefPatternConversion<'b, S, T> for &&PatternConversionProbe<T>
 {
     type Holder = &'b T;
     type Recovery = ();
     #[inline(always)]
     fn try_convert_ref(&self, source: &'b mut S) -> 
Result<Matched<Self::Holder, ()>, ()> {
-        let view = source.view();
-        let node = view.downcast_ref::<T>().ok_or(())? as *const T;
-        // SAFETY: the checked node is borrowed from source for 'b. No owning
+        // Borrow the source's own cell, which lives for 'b: a reference type
+        // such as `&Add` points into the cell, an object type into the heap.
+        let node = unsafe { T::downcast_ref_from_any(source.cell()) 
}.ok_or(())? as *const T;
+        // SAFETY: the checked value is borrowed from source for 'b. No owning
         // conversion or capability transfer can occur while this borrow lives.
         Ok(Matched {
             value: unsafe { &*node },
diff --git a/rust/tvm-ffi/src/object.rs b/rust/tvm-ffi/src/object.rs
index 189275ab..af14e45d 100644
--- a/rust/tvm-ffi/src/object.rs
+++ b/rust/tvm-ffi/src/object.rs
@@ -164,24 +164,23 @@ pub unsafe trait ObjectRefCore: Sized + Clone {
         }
     }
 
-    /// Borrow the underlying object as `T` when its runtime type is `T` or a 
subtype.
+    /// Borrow the underlying object as `T` when it is one.
     ///
-    /// `T` is an object representation type such as `FooObj`, rather than its
-    /// managed reference `Foo`. Unrelated object types return `None`.
-    /// Unlike [`ObjectRefCast::try_cast`], this method neither consumes the
-    /// reference nor changes the object's reference count. The returned borrow
-    /// cannot outlive `self`.
+    /// `T` is an object type such as `FooObj`, or a managed reference such as
+    /// `Foo`: `&Expr` borrows as `&Add` when the expression is an `Add`.
+    /// Unrelated types return `None`. Unlike [`ObjectRefCast::try_cast`], this
+    /// method neither consumes the reference nor changes the object's 
reference
+    /// count. The returned borrow cannot outlive `self`.
     /// See [`crate::Any::downcast_ref`] for an example.
     #[inline(always)]
-    fn downcast_ref<T: ObjectCore>(&self) -> Option<&T> {
-        let object = unsafe { ObjectArc::as_raw(Self::data(self)) };
-        let type_index = unsafe { (*object.cast::<TVMFFIObject>()).type_index 
};
-        if !is_instance_of::<T>(type_index) {
-            return None;
+    fn downcast_ref<T: crate::type_traits::DowncastRefFromObject>(&self) -> 
Option<&T> {
+        // SAFETY: `ObjectArc` is exactly one non-null object pointer, and
+        // ObjectRefCore guarantees a live object allocation.
+        unsafe {
+            let place = &*(Self::data(self) as *const 
ObjectArc<Self::ContainerType>
+                as *const *mut TVMFFIObject);
+            T::downcast_ref_from_object(place)
         }
-        // SAFETY: ObjectRefCore guarantees a live object allocation, the type
-        // check establishes T's layout, and the borrow cannot outlive self.
-        Some(unsafe { &*object.cast::<T>() })
     }
 }
 
diff --git a/rust/tvm-ffi/src/optional.rs b/rust/tvm-ffi/src/optional.rs
index 41ff757f..eea2c336 100644
--- a/rust/tvm-ffi/src/optional.rs
+++ b/rust/tvm-ffi/src/optional.rs
@@ -172,6 +172,33 @@ impl<T: OptionalCompatible> Optional<T> {
     }
 }
 
+impl<T: OptionalCompatible + crate::type_traits::RefFromAnyUnchecked> 
Optional<T> {
+    /// Borrow the value in place, like [`Option::as_ref`].
+    ///
+    /// Available for values the cell stores as themselves, such as `String`,
+    /// `i64` and `f64`; use [`get`](Self::get) for the others.
+    ///
+    /// # Returns
+    ///
+    /// The borrowed value, or `None` when `nullopt`.
+    ///
+    /// ```
+    /// use tvm_ffi::Optional;
+    /// let value = Optional::some(7_i64);
+    /// assert_eq!(value.as_ref(), Some(&7));
+    /// assert_eq!(Optional::<i64>::none().as_ref(), None);
+    /// ```
+    #[inline]
+    pub fn as_ref(&self) -> Option<&T> {
+        if self.is_some() {
+            // SAFETY: an engaged `Optional<T>` cell holds a `T`.
+            Some(unsafe { 
T::ref_from_any_unchecked(self.data.as_raw_ffi_any()) })
+        } else {
+            None
+        }
+    }
+}
+
 impl Optional<String> {
     /// Borrows the engaged string as `&str`, or `None` when `nullopt`.
     ///
diff --git a/rust/tvm-ffi/src/type_traits.rs b/rust/tvm-ffi/src/type_traits.rs
index 0d41244f..56b9cd8c 100644
--- a/rust/tvm-ffi/src/type_traits.rs
+++ b/rust/tvm-ffi/src/type_traits.rs
@@ -18,7 +18,7 @@
  */
 use crate::any::{Any, AnyView};
 use tvm_ffi_sys::TVMFFITypeIndex as TypeIndex;
-use tvm_ffi_sys::{TVMFFIAny, TVMFFIGetTypeInfo};
+use tvm_ffi_sys::{TVMFFIAny, TVMFFIGetTypeInfo, TVMFFIObject};
 
 //-----------------------------------------------------
 // AnyCompatible
@@ -224,55 +224,185 @@ impl ContainerElement for Any {}
 /// # Safety
 ///
 /// For a cell that passes `container_check_any_strict` for `Self`,
-/// `ref_from_any` must return a reference into that cell that is a valid
+/// `ref_from_any_unchecked` must return a reference into that cell that is a 
valid
 /// `Self` for as long as the cell is borrowed.
 #[doc(hidden)]
-pub unsafe trait RefFromAny: ContainerElement {
+pub unsafe trait RefFromAnyUnchecked: ContainerElement {
     /// Borrow the value stored in `cell`.
     ///
     /// # Safety
     ///
     /// `cell` must hold a `Self`.
-    unsafe fn ref_from_any(cell: &TVMFFIAny) -> &Self;
+    unsafe fn ref_from_any_unchecked(cell: &TVMFFIAny) -> &Self;
 }
 
 // The whole cell is the value.
-unsafe impl RefFromAny for Any {
+unsafe impl RefFromAnyUnchecked for Any {
     #[inline(always)]
-    unsafe fn ref_from_any(cell: &TVMFFIAny) -> &Self {
+    unsafe fn ref_from_any_unchecked(cell: &TVMFFIAny) -> &Self {
         &*(cell as *const TVMFFIAny as *const Any)
     }
 }
 
-unsafe impl RefFromAny for crate::String {
+unsafe impl RefFromAnyUnchecked for crate::String {
     #[inline(always)]
-    unsafe fn ref_from_any(cell: &TVMFFIAny) -> &Self {
+    unsafe fn ref_from_any_unchecked(cell: &TVMFFIAny) -> &Self {
         &*(cell as *const TVMFFIAny as *const crate::String)
     }
 }
 
-unsafe impl RefFromAny for crate::string::Bytes {
+unsafe impl RefFromAnyUnchecked for crate::string::Bytes {
     #[inline(always)]
-    unsafe fn ref_from_any(cell: &TVMFFIAny) -> &Self {
+    unsafe fn ref_from_any_unchecked(cell: &TVMFFIAny) -> &Self {
         &*(cell as *const TVMFFIAny as *const crate::string::Bytes)
     }
 }
 
 // The payload word holds the value in its own representation.
-unsafe impl RefFromAny for i64 {
+unsafe impl RefFromAnyUnchecked for i64 {
     #[inline(always)]
-    unsafe fn ref_from_any(cell: &TVMFFIAny) -> &Self {
+    unsafe fn ref_from_any_unchecked(cell: &TVMFFIAny) -> &Self {
         &cell.data_union.v_int64
     }
 }
 
-unsafe impl RefFromAny for f64 {
+unsafe impl RefFromAnyUnchecked for f64 {
     #[inline(always)]
-    unsafe fn ref_from_any(cell: &TVMFFIAny) -> &Self {
+    unsafe fn ref_from_any_unchecked(cell: &TVMFFIAny) -> &Self {
         &cell.data_union.v_float64
     }
 }
 
+/// Types that [`crate::Any::downcast_ref`] and 
[`crate::AnyView::downcast_ref`]
+/// can borrow from an FFI cell in place: object types (`ArrayObj`), and the
+/// [`RefFromAnyUnchecked`] types (managed references such as `Array<T>`, 
`String`,
+/// `i64`).
+///
+/// # Safety
+///
+/// `downcast_ref_from_any` must return `Some` only when `cell` holds a 
`Self`, with
+/// a reference that stays valid while `cell` is borrowed.
+#[doc(hidden)]
+pub unsafe trait DowncastRefFromAny {
+    /// Borrow `cell` as `Self` when it holds one.
+    ///
+    /// # Safety
+    ///
+    /// `cell` must be a valid FFI cell.
+    unsafe fn downcast_ref_from_any(cell: &TVMFFIAny) -> Option<&Self>;
+}
+
+// An object type borrows the object the cell points to.
+unsafe impl<T: crate::object::ObjectCore> DowncastRefFromAny for T {
+    #[inline(always)]
+    unsafe fn downcast_ref_from_any(cell: &TVMFFIAny) -> Option<&Self> {
+        if cell.type_index < TypeIndex::kTVMFFIStaticObjectBegin as i32
+            || !crate::object::is_instance_of::<T>(cell.type_index)
+        {
+            return None;
+        }
+        // SAFETY: the type check establishes T's layout, and the cell keeps 
the
+        // object alive while it is borrowed.
+        Some(&*cell.data_union.v_obj.cast::<T>())
+    }
+}
+
+/// Borrow `cell` as a [`RefFromAnyUnchecked`] type when it strictly holds one.
+///
+/// # Safety
+///
+/// `cell` must be a valid FFI cell.
+#[doc(hidden)]
+#[inline(always)]
+pub unsafe fn downcast_ref_from_any_in_place<T: RefFromAnyUnchecked>(
+    cell: &TVMFFIAny,
+) -> Option<&T> {
+    if T::container_check_any_strict(cell) {
+        Some(T::ref_from_any_unchecked(cell))
+    } else {
+        None
+    }
+}
+
+/// Types that an object reference's [`ObjectRefCore::downcast_ref`] can borrow
+/// from the reference's object pointer: object types (`ArrayObj`), and managed
+/// references that are exactly one object pointer (`Array<T>`, every
+/// `#[derive(ObjectRef)]` type).
+///
+/// [`ObjectRefCore::downcast_ref`]: crate::object::ObjectRefCore::downcast_ref
+///
+/// # Safety
+///
+/// `downcast_ref_from_object` must return `Some` only when the object at
+/// `*place` is a `Self`, with a reference that stays valid while `place` is
+/// borrowed.
+#[doc(hidden)]
+pub unsafe trait DowncastRefFromObject {
+    /// Borrow the object at `*place` as `Self` when it is one.
+    ///
+    /// # Safety
+    ///
+    /// `place` must hold a pointer to a live object.
+    unsafe fn downcast_ref_from_object(place: &*mut TVMFFIObject) -> 
Option<&Self>;
+}
+
+// An object type borrows the object itself.
+unsafe impl<T: crate::object::ObjectCore> DowncastRefFromObject for T {
+    #[inline(always)]
+    unsafe fn downcast_ref_from_object(place: &*mut TVMFFIObject) -> 
Option<&Self> {
+        let object = *place;
+        if !crate::object::is_instance_of::<T>((*object).type_index) {
+            return None;
+        }
+        // SAFETY: the type check establishes T's layout.
+        Some(&*object.cast::<T>())
+    }
+}
+
+/// Borrow the object pointer at `place` as the one-pointer reference type `T`
+/// when the object strictly is a `T`.
+///
+/// # Safety
+///
+/// `place` must hold a pointer to a live object, and `T` must be exactly one
+/// object pointer.
+#[doc(hidden)]
+#[inline(always)]
+pub unsafe fn downcast_ref_from_object_in_place<T: RefFromAnyUnchecked>(
+    place: &*mut TVMFFIObject,
+) -> Option<&T> {
+    const {
+        assert!(
+            std::mem::size_of::<T>() == std::mem::size_of::<*mut 
TVMFFIObject>(),
+            "a reference downcast target must be exactly one object pointer"
+        )
+    };
+    let object = *place;
+    let mut cell = TVMFFIAny::new();
+    cell.type_index = (*object).type_index;
+    cell.data_union.v_obj = object;
+    if T::container_check_any_strict(&cell) {
+        // SAFETY: `T` is exactly one object pointer, and the check shows the
+        // object satisfies `T`.
+        Some(&*(place as *const *mut TVMFFIObject as *const T))
+    } else {
+        None
+    }
+}
+
+macro_rules! impl_downcast_ref_from_any {
+    ($($t:ty),* $(,)?) => {
+        $(unsafe impl DowncastRefFromAny for $t {
+            #[inline(always)]
+            unsafe fn downcast_ref_from_any(cell: &TVMFFIAny) -> Option<&Self> 
{
+                downcast_ref_from_any_in_place::<Self>(cell)
+            }
+        })*
+    };
+}
+
+impl_downcast_ref_from_any!(Any, crate::String, crate::string::Bytes, i64, 
f64);
+
 mod container_element_ops {
     use super::{Any, AnyCompatible, AnyView, TVMFFIAny};
 
diff --git a/rust/tvm-ffi/tests/test_match_any.rs 
b/rust/tvm-ffi/tests/test_match_any.rs
index db9f3bc3..17bae80a 100644
--- a/rust/tvm-ffi/tests/test_match_any.rs
+++ b/rust/tvm-ffi/tests/test_match_any.rs
@@ -71,7 +71,7 @@ fn matches_concrete_object_containers_in_source_order() {
     let size = match_any! {
         &source {
             Array::<i64>(ref handle) if {
-                assert_eq!(count(), 2);
+                assert_eq!(count(), 1);
                 assert_eq!(handle.len(), 3);
                 false
             } => unreachable!(),
@@ -95,7 +95,7 @@ fn matches_concrete_object_containers_in_source_order() {
         &source {
             Array::<i64>(ref handle) => {
                 let _: &Array<i64> = handle;
-                assert_eq!(count(), 2);
+                assert_eq!(count(), 1);
                 handle.len()
             },
             _ => 0,
diff --git a/rust/tvm-ffi/tests/test_match_any_ast_lookup.rs 
b/rust/tvm-ffi/tests/test_match_any_ast_lookup.rs
index e459c254..5385270b 100644
--- a/rust/tvm-ffi/tests/test_match_any_ast_lookup.rs
+++ b/rust/tvm-ffi/tests/test_match_any_ast_lookup.rs
@@ -324,25 +324,25 @@ fn dispatches_representative_ast_leaf_nodes() {
         match_any! {
             value {
                 T(ref node) => checked(node, value, 1, "add"),
-                SubExpr(ref handle) => checked(handle, value, 2, "sub"),
+                SubExpr(ref handle) => checked(handle, value, 1, "sub"),
                 MulExprObj(ref node) => checked(node, value, 1, "mul"),
-                DivExpr(ref handle) => checked(handle, value, 2, "div"),
+                DivExpr(ref handle) => checked(handle, value, 1, "div"),
                 ModExprObj(ref node) => checked(node, value, 1, "mod"),
-                NegExpr(ref handle) => checked(handle, value, 2, "neg"),
+                NegExpr(ref handle) => checked(handle, value, 1, "neg"),
                 CallExprObj(ref node) => checked(node, value, 1, "call"),
-                LetExpr(ref handle) => checked(handle, value, 2, "let"),
+                LetExpr(ref handle) => checked(handle, value, 1, "let"),
                 IfExprObj(ref node) => checked(node, value, 1, "if"),
-                TupleExpr(ref handle) => checked(handle, value, 2, "tuple"),
+                TupleExpr(ref handle) => checked(handle, value, 1, "tuple"),
                 TupleGetItemExprObj(ref node) => checked(node, value, 1, 
"tuple_get_item"),
-                CastExpr(ref handle) => checked(handle, value, 2, "cast"),
+                CastExpr(ref handle) => checked(handle, value, 1, "cast"),
                 LoadExprObj(ref node) => checked(node, value, 1, "load"),
-                StoreExpr(ref handle) => checked(handle, value, 2, "store"),
+                StoreExpr(ref handle) => checked(handle, value, 1, "store"),
                 ForExprObj(ref node) => checked(node, value, 1, "for"),
-                WhileExpr(ref handle) => checked(handle, value, 2, "while"),
+                WhileExpr(ref handle) => checked(handle, value, 1, "while"),
                 SeqExprObj(ref node) => checked(node, value, 1, "seq"),
-                ReturnExpr(ref handle) => checked(handle, value, 2, "return"),
+                ReturnExpr(ref handle) => checked(handle, value, 1, "return"),
                 ConstantExprObj(ref node) => checked(node, value, 1, 
"constant"),
-                VarExpr(ref handle) => checked(handle, value, 2, "var"),
+                VarExpr(ref handle) => checked(handle, value, 1, "var"),
                 AddExpr(_) => "duplicate_must_not_win",
                 _ => "unsupported",
             }

Reply via email to