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 783db932 [FEAT][RUST] Support Any elements in FFI containers (#715)
783db932 is described below
commit 783db93284270a814b0e6cf2f013ba3012837ca4
Author: Shushi Hong <[email protected]>
AuthorDate: Sat Aug 29 14:39:07 2026 -0400
[FEAT][RUST] Support Any elements in FFI containers (#715)
This PR adds support for heterogeneous Rust FFI containers such as:
- `Array<Any>`
- `Map<String, Any>`
- `Map<Any, Any>`
`Any` cannot implement `AnyCompatible` because doing so would conflict
with Rust's existing identity conversion from `Any` to `Any`. This PR
therefore introduces a sealed internal `ContainerElement` trait.
All existing `AnyCompatible` types implement `ContainerElement`
automatically, while `Any` receives a dedicated implementation. Regular
typed containers continue to use their existing conversion behavior.
This allows Rust bindings and generated IR wrappers to represent
heterogeneous TVM fields such as attributes and annotations using the
standard `Array` and `Map` types, without introducing a separate
`AnyMap` abstraction.
The PR covers:
- Container construction and iteration
- Strict type checking and element-wise conversion
- `Any` and `AnyView` round trips
- Object reference-count preservation
- Passing heterogeneous containers through typed packed calls
The complete Rust workspace test suite passes.
---
rust/tvm-ffi/src/collections/array.rs | 51 ++++++++--------
rust/tvm-ffi/src/collections/map.rs | 107 ++++++++++++++++++++++------------
rust/tvm-ffi/src/function_internal.rs | 39 ++++++++++---
rust/tvm-ffi/src/type_traits.rs | 103 ++++++++++++++++++++++++++++++++
rust/tvm-ffi/tests/test_array.rs | 39 +++++++++++++
rust/tvm-ffi/tests/test_map.rs | 92 +++++++++++++++++++++++++++++
6 files changed, 362 insertions(+), 69 deletions(-)
diff --git a/rust/tvm-ffi/src/collections/array.rs
b/rust/tvm-ffi/src/collections/array.rs
index 6f259ba1..614125ff 100644
--- a/rust/tvm-ffi/src/collections/array.rs
+++ b/rust/tvm-ffi/src/collections/array.rs
@@ -23,6 +23,7 @@ use std::ops::Deref;
use crate::any::TryFromTemp;
use crate::derive::Object;
use crate::object::{Object, ObjectArc};
+use crate::type_traits::ContainerElement;
use crate::{Any, AnyCompatible, AnyView, ObjectCoreWithExtraItems,
ObjectRefCore};
use tvm_ffi_sys::TVMFFITypeIndex as TypeIndex;
use tvm_ffi_sys::{TVMFFIAny, TVMFFIObject};
@@ -50,12 +51,12 @@ unsafe impl ObjectCoreWithExtraItems for ArrayObj {
#[repr(C)]
#[derive(Clone)]
-pub struct Array<T: AnyCompatible + Clone> {
+pub struct Array<T: ContainerElement + Clone> {
data: ObjectArc<ArrayObj>,
_marker: PhantomData<T>,
}
-impl<T: AnyCompatible + Clone> Debug for Array<T> {
+impl<T: ContainerElement + Clone> Debug for Array<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let full_name = std::any::type_name::<T>();
let short_name = full_name.split("::").last().unwrap_or(full_name);
@@ -63,13 +64,13 @@ impl<T: AnyCompatible + Clone> Debug for Array<T> {
}
}
-impl<T: AnyCompatible + Clone> Default for Array<T> {
+impl<T: ContainerElement + Clone> Default for Array<T> {
fn default() -> Self {
Self::new(vec![])
}
}
-unsafe impl<T: AnyCompatible + Clone> ObjectRefCore for Array<T> {
+unsafe impl<T: ContainerElement + Clone> ObjectRefCore for Array<T> {
type ContainerType = ArrayObj;
fn data(this: &Self) -> &ObjectArc<Self::ContainerType> {
@@ -88,7 +89,7 @@ unsafe impl<T: AnyCompatible + Clone> ObjectRefCore for
Array<T> {
}
}
-impl<T: AnyCompatible + Clone> Array<T> {
+impl<T: ContainerElement + Clone> Array<T> {
/// Creates a new Array from a vector of items.
pub fn new(items: Vec<T>) -> Self {
let capacity = items.len();
@@ -116,8 +117,8 @@ impl<T: AnyCompatible + Clone> Array<T> {
container.data = base_ptr as *mut _;
for (i, item) in items.into_iter().enumerate() {
- let any: Any = Any::from(item);
- let raw = Any::into_raw_ffi_any(any);
+ let mut raw = TVMFFIAny::new();
+ T::container_move_to_any(item, &mut raw);
core::ptr::write(base_ptr.add(i), raw);
}
}
@@ -142,13 +143,13 @@ impl<T: AnyCompatible + Clone> Array<T> {
let base_ptr = container.data as *const TVMFFIAny;
let raw_any_ref = &*base_ptr.add(index);
- match T::try_cast_from_any_view(raw_any_ref) {
+ match T::container_try_cast_from_any_view(raw_any_ref) {
Ok(val) => Ok(val),
Err(_) => crate::bail!(
crate::error::TYPE_ERROR,
"Failed to cast element at {} to {}",
index,
- T::type_str()
+ T::container_type_str()
),
}
}
@@ -173,7 +174,7 @@ impl<T: AnyCompatible + Clone> Array<T> {
// --- Index Implementation ---
-impl<T: AnyCompatible + Clone> std::ops::Index<usize> for Array<T> {
+impl<T: ContainerElement + Clone> std::ops::Index<usize> for Array<T> {
type Output = AnyView<'static>;
fn index(&self, index: usize) -> &Self::Output {
@@ -194,13 +195,13 @@ impl<T: AnyCompatible + Clone> std::ops::Index<usize> for
Array<T> {
// --- Iterator Implementations ---
-pub struct ArrayIterator<'a, T: AnyCompatible + Clone> {
+pub struct ArrayIterator<'a, T: ContainerElement + Clone> {
array: &'a Array<T>,
index: usize,
len: usize,
}
-impl<'a, T: AnyCompatible + Clone> Iterator for ArrayIterator<'a, T> {
+impl<'a, T: ContainerElement + Clone> Iterator for ArrayIterator<'a, T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
@@ -214,7 +215,7 @@ impl<'a, T: AnyCompatible + Clone> Iterator for
ArrayIterator<'a, T> {
}
}
-impl<'a, T: AnyCompatible + Clone> IntoIterator for &'a Array<T> {
+impl<'a, T: ContainerElement + Clone> IntoIterator for &'a Array<T> {
type Item = T;
type IntoIter = ArrayIterator<'a, T>;
@@ -223,7 +224,7 @@ impl<'a, T: AnyCompatible + Clone> IntoIterator for &'a
Array<T> {
}
}
-impl<T: AnyCompatible + Clone> FromIterator<T> for Array<T> {
+impl<T: ContainerElement + Clone> FromIterator<T> for Array<T> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
let items: Vec<T> = iter.into_iter().collect();
Self::new(items)
@@ -234,10 +235,10 @@ impl<T: AnyCompatible + Clone> FromIterator<T> for
Array<T> {
unsafe impl<T> AnyCompatible for Array<T>
where
- T: AnyCompatible + Clone + 'static,
+ T: ContainerElement + Clone,
{
fn type_str() -> String {
- format!("Array<{}>", T::type_str())
+ format!("Array<{}>", T::container_type_str())
}
unsafe fn check_any_strict(data: &TVMFFIAny) -> bool {
@@ -245,15 +246,11 @@ where
return false;
}
- if std::any::TypeId::of::<T>() == std::any::TypeId::of::<Any>() {
- return true;
- }
-
let container = &*(data.data_union.v_obj as *const ArrayObj);
let base_ptr = container.data as *const TVMFFIAny;
for i in 0..container.size {
let elem_any = &*base_ptr.add(i as usize);
- if !T::check_any_strict(elem_any) {
+ if !T::container_check_any_strict(elem_any) {
return false;
}
}
@@ -294,8 +291,10 @@ where
}
// Fast path: if types match exactly, we can just copy the reference.
- if Self::check_any_strict(data) {
- return Ok(Self::copy_from_any_view_after_check(data));
+ if <Self as AnyCompatible>::check_any_strict(data) {
+ return Ok(<Self as AnyCompatible>::copy_from_any_view_after_check(
+ data,
+ ));
}
// Slow path: try to convert element by element.
@@ -305,7 +304,7 @@ where
for i in 0..container.size {
let any_v = &*base_ptr.add(i as usize);
- if let Ok(item) = T::try_cast_from_any_view(any_v) {
+ if let Ok(item) = T::container_try_cast_from_any_view(any_v) {
items.push(item);
} else {
return Err(());
@@ -318,7 +317,7 @@ where
impl<T> TryFrom<Any> for Array<T>
where
- T: AnyCompatible + Clone + 'static,
+ T: ContainerElement + Clone,
{
type Error = crate::error::Error;
@@ -330,7 +329,7 @@ where
impl<'a, T> TryFrom<AnyView<'a>> for Array<T>
where
- T: AnyCompatible + Clone + 'static,
+ T: ContainerElement + Clone,
{
type Error = crate::error::Error;
diff --git a/rust/tvm-ffi/src/collections/map.rs
b/rust/tvm-ffi/src/collections/map.rs
index cdb87adc..64154bb1 100644
--- a/rust/tvm-ffi/src/collections/map.rs
+++ b/rust/tvm-ffi/src/collections/map.rs
@@ -37,10 +37,39 @@ use crate::any::TryFromTemp;
use crate::derive::Object;
use crate::function::Function;
use crate::object::{Object, ObjectArc};
+use crate::type_traits::ContainerElement;
use crate::{Any, AnyCompatible, AnyView, Error, ObjectRefCore, Result};
use tvm_ffi_sys::TVMFFITypeIndex as TypeIndex;
use tvm_ffi_sys::{TVMFFIAny, TVMFFIObject};
+#[inline]
+fn element_view<T: ContainerElement>(value: &T) -> AnyView<'_> {
+ unsafe {
+ let mut data = TVMFFIAny::new();
+ T::container_copy_to_any_view(value, &mut data);
+ AnyView::from_raw_ffi_any(data)
+ }
+}
+
+fn element_from_any<T: ContainerElement>(value: Any) -> Result<T> {
+ unsafe {
+ if T::container_check_any_strict(value.as_raw_ffi_any()) {
+ let mut value = std::mem::ManuallyDrop::new(value);
+ return Ok(T::container_move_from_any_after_check(
+ &mut *value.as_data_ptr(),
+ ));
+ }
+
T::container_try_cast_from_any_view(value.as_raw_ffi_any()).map_err(|()| {
+ let message = format!(
+ "Cannot convert from type `{}` to `{}`",
+ T::container_get_mismatch_type_info(value.as_raw_ffi_any()),
+ T::container_type_str()
+ );
+ Error::new(crate::error::TYPE_ERROR, &message, "")
+ })
+ }
+}
+
/// Container object for [`Map`]. The header fields mirror C++ `MapBaseObj`
/// (`include/tvm/ffi/container/map_base.h`) so [`Map::len`] can read `size`
/// without an FFI call; the storage `data` points to stays opaque.
@@ -118,8 +147,8 @@ impl<K, V> Deref for Map<K, V> {
impl<K, V> Map<K, V>
where
- K: AnyCompatible,
- V: AnyCompatible,
+ K: ContainerElement,
+ V: ContainerElement,
{
/// Creates a new, empty map (via an `ffi.Map()` call to the C++ runtime).
pub fn new() -> Self {
@@ -131,8 +160,8 @@ where
fn from_pairs(pairs: &[(K, V)]) -> Result<Self> {
let mut args: Vec<AnyView<'_>> = Vec::with_capacity(pairs.len() * 2);
for (k, v) in pairs {
- args.push(AnyView::from(k));
- args.push(AnyView::from(v));
+ args.push(element_view(k));
+ args.push(element_view(v));
}
let result = crate::cached_global_func!("ffi.Map").call_packed(&args)?;
Self::try_from(result)
@@ -154,7 +183,7 @@ where
/// [`Map::contains_key`] and [`Map::get`].
fn try_contains_key(&self, key: &K) -> Result<bool> {
let result = crate::cached_global_func!("ffi.MapCount")
- .call_packed(&[AnyView::from(self), AnyView::from(key)])?;
+ .call_packed(&[AnyView::from(self), element_view(key)])?;
Ok(i64::try_from(result)? != 0)
}
@@ -177,7 +206,7 @@ where
.call_packed(&[AnyView::from(&0i64)])
.expect("map iterator: reading current key failed");
assert!(
- first_key.try_as::<K>().is_some(),
+ unsafe {
K::container_check_any_strict(first_key.as_raw_ffi_any()) },
"Map lookup: key type `{}` does not match the map's stored
key type",
std::any::type_name::<K>(),
);
@@ -219,8 +248,8 @@ where
return Ok(None);
}
let result = crate::cached_global_func!("ffi.MapGetItem")
- .call_packed(&[AnyView::from(self), AnyView::from(key)])?;
- let value =
TryFromTemp::<V>::try_from(result).map(TryFromTemp::into_value)?;
+ .call_packed(&[AnyView::from(self), element_view(key)])?;
+ let value = element_from_any(result)?;
Ok(Some(value))
}
@@ -289,8 +318,8 @@ where
impl<K, V> Default for Map<K, V>
where
- K: AnyCompatible,
- V: AnyCompatible,
+ K: ContainerElement,
+ V: ContainerElement,
{
fn default() -> Self {
Self::new()
@@ -299,8 +328,8 @@ where
impl<K, V> Debug for Map<K, V>
where
- K: AnyCompatible,
- V: AnyCompatible,
+ K: ContainerElement,
+ V: ContainerElement,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn short(name: &str) -> &str {
@@ -318,8 +347,8 @@ where
impl<K, V> FromIterator<(K, V)> for Map<K, V>
where
- K: AnyCompatible,
- V: AnyCompatible,
+ K: ContainerElement,
+ V: ContainerElement,
{
/// Duplicate keys follow C++ `ffi.Map` semantics: a later pair overwrites
an
/// earlier one, so the resulting map may be smaller than the iterator.
@@ -342,12 +371,11 @@ where
/// Reads the functor's current key (`command` 0) or value (`command` 1) as
`T`,
/// panicking on a type mismatch (see the note above on `ExactSizeIterator`).
-fn iter_read<T: AnyCompatible>(functor: &Function, command: i64, kind: &str)
-> T {
+fn iter_read<T: ContainerElement>(functor: &Function, command: i64, kind:
&str) -> T {
let any = functor
.call_packed(&[AnyView::from(&command)])
.expect("map iterator: reading current element failed");
- TryFromTemp::<T>::try_from(any)
- .map(TryFromTemp::into_value)
+ element_from_any(any)
.unwrap_or_else(|_| panic!("map iterator: {kind} does not match the
map's {kind} type"))
}
@@ -413,8 +441,8 @@ pub type MapValues<V> = MapIter<V>;
impl<K, V> IntoIterator for &Map<K, V>
where
- K: AnyCompatible,
- V: AnyCompatible,
+ K: ContainerElement,
+ V: ContainerElement,
{
type Item = (K, V);
type IntoIter = MapItems<K, V>;
@@ -428,11 +456,15 @@ where
unsafe impl<K, V> AnyCompatible for Map<K, V>
where
- K: AnyCompatible,
- V: AnyCompatible,
+ K: ContainerElement,
+ V: ContainerElement,
{
fn type_str() -> String {
- format!("Map<{}, {}>", K::type_str(), V::type_str())
+ format!(
+ "Map<{}, {}>",
+ K::container_type_str(),
+ V::container_type_str()
+ )
}
unsafe fn check_any_strict(data: &TVMFFIAny) -> bool {
@@ -442,11 +474,12 @@ where
if data.type_index != TypeIndex::kTVMFFIMap as i32 {
return false;
}
- let map = Self::copy_from_any_view_after_check(data);
+ let map = <Self as
AnyCompatible>::copy_from_any_view_after_check(data);
match map.try_raw_entries() {
- Ok(entries) => entries
- .iter()
- .all(|(k, v)| k.try_as::<K>().is_some() &&
v.try_as::<V>().is_some()),
+ Ok(entries) => entries.iter().all(|(k, v)| unsafe {
+ K::container_check_any_strict(k.as_raw_ffi_any())
+ && V::container_check_any_strict(v.as_raw_ffi_any())
+ }),
Err(_) => false,
}
}
@@ -483,18 +516,20 @@ where
}
// Fast path: if all entries match strictly, we can just copy the
reference.
- if Self::check_any_strict(data) {
- return Ok(Self::copy_from_any_view_after_check(data));
+ if <Self as AnyCompatible>::check_any_strict(data) {
+ return Ok(<Self as AnyCompatible>::copy_from_any_view_after_check(
+ data,
+ ));
}
// Slow path: try to convert entry by entry into a new map, as C++
// `TryCastFromAnyView` does.
- let src = Self::copy_from_any_view_after_check(data);
+ let src = <Self as
AnyCompatible>::copy_from_any_view_after_check(data);
let mut pairs = Vec::with_capacity(src.len());
for (k, v) in src.try_raw_entries().map_err(|_| ())? {
- let k = TryFromTemp::<K>::try_from(k).map_err(|_| ())?;
- let v = TryFromTemp::<V>::try_from(v).map_err(|_| ())?;
- pairs.push((TryFromTemp::into_value(k),
TryFromTemp::into_value(v)));
+ let k = element_from_any::<K>(k).map_err(|_| ())?;
+ let v = element_from_any::<V>(v).map_err(|_| ())?;
+ pairs.push((k, v));
}
Self::from_pairs(&pairs).map_err(|_| ())
}
@@ -502,8 +537,8 @@ where
impl<K, V> TryFrom<Any> for Map<K, V>
where
- K: AnyCompatible,
- V: AnyCompatible,
+ K: ContainerElement,
+ V: ContainerElement,
{
type Error = Error;
@@ -515,8 +550,8 @@ where
impl<'a, K, V> TryFrom<AnyView<'a>> for Map<K, V>
where
- K: AnyCompatible,
- V: AnyCompatible,
+ K: ContainerElement,
+ V: ContainerElement,
{
type Error = Error;
diff --git a/rust/tvm-ffi/src/function_internal.rs
b/rust/tvm-ffi/src/function_internal.rs
index ffd0b634..43aab97c 100644
--- a/rust/tvm-ffi/src/function_internal.rs
+++ b/rust/tvm-ffi/src/function_internal.rs
@@ -19,7 +19,7 @@
use crate::any::{Any, AnyView, ArgTryFromAnyView};
use crate::error::Result;
use crate::string::{Bytes, String};
-use crate::type_traits::AnyCompatible;
+use crate::type_traits::{AnyCompatible, ContainerElement};
//------------------------------------------------------------------------
// PackedCallable
@@ -152,27 +152,52 @@ crate::impl_arg_into_ref!(
bool, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize, f32, f64,
String, Bytes
);
-// `Map<K, V>` passes by value/reference like the scalars above, but its type
-// parameters keep it out of the `impl_*!` macros, so the impls are spelled
out.
-impl<K: AnyCompatible, V: AnyCompatible> IntoArgHolder for crate::Map<K, V> {
+// Parametric containers pass by value/reference like the scalars above, but
+// their type parameters keep them out of the `impl_*!` macros.
+impl<T: ContainerElement + Clone> IntoArgHolder for crate::Array<T> {
+ type Target = crate::Array<T>;
+ fn into_arg_holder(self) -> Self::Target {
+ self
+ }
+}
+impl<'a, T: ContainerElement + Clone> IntoArgHolder for &'a crate::Array<T> {
+ type Target = &'a crate::Array<T>;
+ fn into_arg_holder(self) -> Self::Target {
+ self
+ }
+}
+impl<T: ContainerElement + Clone> ArgIntoRef for crate::Array<T> {
+ type Target = crate::Array<T>;
+ fn to_ref(&self) -> &Self::Target {
+ self
+ }
+}
+impl<T: ContainerElement + Clone> ArgIntoRef for &crate::Array<T> {
+ type Target = crate::Array<T>;
+ fn to_ref(&self) -> &Self::Target {
+ self
+ }
+}
+
+impl<K: ContainerElement, V: ContainerElement> IntoArgHolder for crate::Map<K,
V> {
type Target = crate::Map<K, V>;
fn into_arg_holder(self) -> Self::Target {
self
}
}
-impl<'a, K: AnyCompatible, V: AnyCompatible> IntoArgHolder for &'a
crate::Map<K, V> {
+impl<'a, K: ContainerElement, V: ContainerElement> IntoArgHolder for &'a
crate::Map<K, V> {
type Target = &'a crate::Map<K, V>;
fn into_arg_holder(self) -> Self::Target {
self
}
}
-impl<K: AnyCompatible, V: AnyCompatible> ArgIntoRef for crate::Map<K, V> {
+impl<K: ContainerElement, V: ContainerElement> ArgIntoRef for crate::Map<K, V>
{
type Target = crate::Map<K, V>;
fn to_ref(&self) -> &Self::Target {
self
}
}
-impl<K: AnyCompatible, V: AnyCompatible> ArgIntoRef for &crate::Map<K, V> {
+impl<K: ContainerElement, V: ContainerElement> ArgIntoRef for &crate::Map<K,
V> {
type Target = crate::Map<K, V>;
fn to_ref(&self) -> &Self::Target {
self
diff --git a/rust/tvm-ffi/src/type_traits.rs b/rust/tvm-ffi/src/type_traits.rs
index f5ff4540..63662454 100644
--- a/rust/tvm-ffi/src/type_traits.rs
+++ b/rust/tvm-ffi/src/type_traits.rs
@@ -83,6 +83,109 @@ pub unsafe trait AnyCompatible: Sized {
}
}
+/// Marker for a value that can be stored in an FFI container.
+///
+/// This is an implementation detail of [`crate::Array`] and [`crate::Map`].
+/// Users should implement [`AnyCompatible`]; the blanket implementation below
+/// then makes that type a container element automatically. [`Any`] is handled
+/// separately so heterogeneous containers such as `Array<Any>` also work.
+///
+/// The private supertrait seals this marker and owns all conversion
operations,
+/// keeping them out of the user-facing trait API.
+#[doc(hidden)]
+pub trait ContainerElement: container_element_ops::Ops {}
+
+impl<T: AnyCompatible> ContainerElement for T {}
+impl ContainerElement for Any {}
+
+mod container_element_ops {
+ use super::{Any, AnyCompatible, AnyView, TVMFFIAny};
+
+ pub trait Ops: Sized {
+ unsafe fn container_copy_to_any_view(src: &Self, data: &mut TVMFFIAny);
+ unsafe fn container_move_to_any(src: Self, data: &mut TVMFFIAny);
+ unsafe fn container_check_any_strict(data: &TVMFFIAny) -> bool;
+ unsafe fn container_move_from_any_after_check(data: &mut TVMFFIAny) ->
Self;
+ unsafe fn container_try_cast_from_any_view(data: &TVMFFIAny) ->
Result<Self, ()>;
+ fn container_get_mismatch_type_info(data: &TVMFFIAny) -> String;
+ fn container_type_str() -> String;
+ }
+
+ impl<T: AnyCompatible> Ops for T {
+ #[inline]
+ unsafe fn container_copy_to_any_view(src: &Self, data: &mut TVMFFIAny)
{
+ <T as AnyCompatible>::copy_to_any_view(src, data)
+ }
+
+ #[inline]
+ unsafe fn container_move_to_any(src: Self, data: &mut TVMFFIAny) {
+ <T as AnyCompatible>::move_to_any(src, data)
+ }
+
+ #[inline]
+ unsafe fn container_check_any_strict(data: &TVMFFIAny) -> bool {
+ <T as AnyCompatible>::check_any_strict(data)
+ }
+
+ #[inline]
+ unsafe fn container_move_from_any_after_check(data: &mut TVMFFIAny) ->
Self {
+ <T as AnyCompatible>::move_from_any_after_check(data)
+ }
+
+ #[inline]
+ unsafe fn container_try_cast_from_any_view(data: &TVMFFIAny) ->
Result<Self, ()> {
+ <T as AnyCompatible>::try_cast_from_any_view(data)
+ }
+
+ #[inline]
+ fn container_get_mismatch_type_info(data: &TVMFFIAny) -> String {
+ <T as AnyCompatible>::get_mismatch_type_info(data)
+ }
+
+ #[inline]
+ fn container_type_str() -> String {
+ <T as AnyCompatible>::type_str()
+ }
+ }
+
+ impl Ops for Any {
+ #[inline]
+ unsafe fn container_copy_to_any_view(src: &Self, data: &mut TVMFFIAny)
{
+ *data = *src.as_raw_ffi_any();
+ }
+
+ #[inline]
+ unsafe fn container_move_to_any(src: Self, data: &mut TVMFFIAny) {
+ *data = Any::into_raw_ffi_any(src);
+ }
+
+ #[inline]
+ unsafe fn container_check_any_strict(_data: &TVMFFIAny) -> bool {
+ true
+ }
+
+ #[inline]
+ unsafe fn container_move_from_any_after_check(data: &mut TVMFFIAny) ->
Self {
+ Any::from_raw_ffi_any(std::mem::replace(data, TVMFFIAny::new()))
+ }
+
+ #[inline]
+ unsafe fn container_try_cast_from_any_view(data: &TVMFFIAny) ->
Result<Self, ()> {
+ Ok(Any::from(AnyView::from_raw_ffi_any(*data)))
+ }
+
+ #[inline]
+ fn container_get_mismatch_type_info(_data: &TVMFFIAny) -> String {
+ "Any".to_string()
+ }
+
+ #[inline]
+ fn container_type_str() -> String {
+ "Any".to_string()
+ }
+ }
+}
+
/// AnyCompatible for bool
unsafe impl AnyCompatible for bool {
unsafe fn copy_to_any_view(src: &Self, data: &mut TVMFFIAny) {
diff --git a/rust/tvm-ffi/tests/test_array.rs b/rust/tvm-ffi/tests/test_array.rs
index fe87c5fd..a76e8f13 100644
--- a/rust/tvm-ffi/tests/test_array.rs
+++ b/rust/tvm-ffi/tests/test_array.rs
@@ -78,6 +78,45 @@ fn test_array_any_conversions() {
assert_eq!(back_from_view.len(), 3);
}
+#[test]
+fn test_array_with_any_elements() {
+ let array = Array::new(vec![
+ Any::from(7i64),
+ Any::from(String::from("value")),
+ Any::from(Array::new(vec![1i64, 2])),
+ ]);
+
+ assert_eq!(i64::try_from(array.get(0).unwrap()).unwrap(), 7);
+ assert_eq!(
+ String::try_from(array.get(1).unwrap()).unwrap().as_str(),
+ "value"
+ );
+ assert_eq!(
+ Array::<i64>::try_from(array.get(2).unwrap())
+ .unwrap()
+ .iter()
+ .collect::<Vec<_>>(),
+ vec![1, 2]
+ );
+
+ let round_trip = Array::<Any>::try_from(Any::from(array)).unwrap();
+ assert_eq!(round_trip.len(), 3);
+
+ let array_size = Function::get_global("ffi.ArraySize").unwrap();
+ let by_value =
i64::try_from(array_size.call_tuple((round_trip.clone(),)).unwrap()).unwrap();
+ let by_reference =
i64::try_from(array_size.call_tuple((&round_trip,)).unwrap()).unwrap();
+ assert_eq!((by_value, by_reference), (3, 3));
+
+ let widened = Array::<Any>::try_from(Any::from(Array::new(vec![3i64,
4]))).unwrap();
+ assert_eq!(
+ widened
+ .iter()
+ .map(|value| i64::try_from(value).unwrap())
+ .collect::<Vec<_>>(),
+ vec![3, 4]
+ );
+}
+
#[test]
fn test_array_recursive_type_checking() {
// 1. Create an Array of Shapes
diff --git a/rust/tvm-ffi/tests/test_map.rs b/rust/tvm-ffi/tests/test_map.rs
index ff50c576..d163e00b 100644
--- a/rust/tvm-ffi/tests/test_map.rs
+++ b/rust/tvm-ffi/tests/test_map.rs
@@ -126,6 +126,98 @@ fn test_map_any_roundtrip() {
assert_eq!(back.get(&2).unwrap(), Some(20));
}
+#[test]
+fn test_map_with_any_values() {
+ let empty = Map::<String, Any>::default();
+ assert!(empty.is_empty());
+
+ let array = Array::new(vec![1i64, 2, 3]);
+ let array_base_count = AnyView::from(&array).debug_strong_count().unwrap();
+ let map: Map<String, Any> = [
+ (String::from("number"), Any::from(7i64)),
+ (String::from("text"), Any::from(String::from("value"))),
+ (String::from("array"), Any::from(array.clone())),
+ ]
+ .into_iter()
+ .collect();
+
+ assert_eq!(map.len(), 3);
+ assert!(map.contains_key(&String::from("number")));
+ assert!(!map.contains_key(&String::from("missing")));
+ assert_eq!(format!("{map:?}"), "Map<String, Any>[3]");
+ assert_eq!(AnyView::from(&array).debug_strong_count().unwrap(), 2);
+ assert_eq!(
+
i64::try_from(map.get(&String::from("number")).unwrap().unwrap()).unwrap(),
+ 7
+ );
+ assert_eq!(
+ String::try_from(map.get(&String::from("text")).unwrap().unwrap())
+ .unwrap()
+ .as_str(),
+ "value"
+ );
+
+ let fetched_array =
+
Array::<i64>::try_from(map.get(&String::from("array")).unwrap().unwrap()).unwrap();
+ assert_eq!(fetched_array.iter().collect::<Vec<_>>(), vec![1, 2, 3]);
+ assert_eq!(AnyView::from(&array).debug_strong_count().unwrap(), 3);
+ drop(fetched_array);
+
+ let round_trip = Map::<String,
Any>::try_from(Any::from(map.clone())).unwrap();
+ let view_round_trip = Map::<String,
Any>::try_from(AnyView::from(&map)).unwrap();
+ assert_eq!(round_trip.iter().count(), 3);
+ assert_eq!(round_trip.keys().count(), 3);
+ assert_eq!(round_trip.values().count(), 3);
+ assert_eq!((&round_trip).into_iter().count(), 3);
+
+ let map_size = Function::get_global("ffi.MapSize").unwrap();
+ let by_value: i64 = map_size
+ .call_tuple((round_trip.clone(),))
+ .unwrap()
+ .try_into()
+ .unwrap();
+ let by_reference: i64 = map_size
+ .call_tuple((&view_round_trip,))
+ .unwrap()
+ .try_into()
+ .unwrap();
+ assert_eq!((by_value, by_reference), (3, 3));
+
+ drop(round_trip);
+ drop(view_round_trip);
+ drop(map);
+ assert_eq!(
+ AnyView::from(&array).debug_strong_count().unwrap(),
+ array_base_count
+ );
+}
+
+#[test]
+fn test_map_with_any_keys_and_values() {
+ let integer_key = Any::from(1i64);
+ let string_key = Any::from(String::from("name"));
+ let map: Map<Any, Any> = [
+ (integer_key.clone(), Any::from(String::from("one"))),
+ (string_key.clone(), Any::from(2i64)),
+ ]
+ .into_iter()
+ .collect();
+
+ assert_eq!(
+ String::try_from(map.get(&integer_key).unwrap().unwrap())
+ .unwrap()
+ .as_str(),
+ "one"
+ );
+ assert_eq!(
+ i64::try_from(map.get(&string_key).unwrap().unwrap()).unwrap(),
+ 2
+ );
+
+ let round_trip = Map::<Any, Any>::try_from(Any::from(map)).unwrap();
+ assert_eq!(round_trip.iter().count(), 2);
+}
+
#[test]
fn test_map_shares_underlying_object() {
let map: Map<i64, i64> = [(1i64, 10i64)].into_iter().collect();