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

Reply via email to