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",
}