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

Seven-Streams 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 ecf09bdb [Rust] Array/Map conversions from references, literals and 
subtype containers (#952)
ecf09bdb is described below

commit ecf09bdbd72ee4f8bc2bfea77c49e8e5544973ff
Author: Linzhang Li <[email protected]>
AuthorDate: Fri Nov 6 21:37:41 2026 -0500

    [Rust] Array/Map conversions from references, literals and subtype 
containers (#952)
    
    ## Summary
    
    This PR lets `Array` and `Map` arguments convert the way object
    references do. Before, an
    `impl Into<Array<T>>` parameter, such as a stored field of a generated
    `new` or `from_fields`,
    accepted only `Array<T>` and `Vec<T>`. An `impl Into<Map<K, V>>`
    parameter accepted only an
    owned `Map<K, V>`. Now both also take a reference, an array literal and,
    through a named
    method, a container of a subtype:
    
    ```rust
    fn keep_array(_: impl Into<Array<Base>>) {}
    fn keep_map(_: impl Into<Map<String, Base>>) {}
    
    keep_array(&bases);                  // reference-count bump
    keep_array([a, b]);                  // [T; N]
    keep_array(subs.upcast());           // Array<Sub> -> Array<Base>, no copy
    keep_map(&map);                      // no `.clone()`
    keep_map([("a".into(), a)]);         // [(K, V); N]
    keep_map(sub_map.upcast());          // Map<String, Sub> -> Map<String, 
Base>, no copy
    ```
    
    ### Details
    
    - **`From` conversions.** `Array<T>` gains `From<[T; N]>`, `From<&[T]>`
    and `From<&Array<T>>`.
    `Map<K, V>` gains `From<[(K, V); N]>`, as `HashMap` has, and
    `From<&Map<K, V>>`. The
    reference forms clone the handle, the same as `From<&T> for T` from
    `#[derive(ObjectRef)]`.
    - **`TypeSubsumes<Source>`.** A new `unsafe` marker trait, re-exported
    at the crate root and
    named after C++ `type_subsumes_v`. `Self: TypeSubsumes<Source>` means
    every `Source` value is
    a valid `Self` as stored. A blanket impl covers every type and itself,
    and
    `unsafe_impl_object_ref_upcast!` now also declares `Target:
    TypeSubsumes<Source>`. Existing
      derived and stubgen-generated references get it without changes.
    - **Element upcasts.** `Array::upcast` and `upcast_ref`, and
    `Map::upcast` and `upcast_ref`,
    turn a container into one of subsuming element types without a copy, the
    same as C++
    `Array(const Array<U>&)` and `Map(const Map<KU, VU>&)`. A blanket
    `From<Array<U>> for Array<T>`
    would overlap the reflexive `From<T> for T`, so the upcast is a named
    method.
    - **`IntoObjectRef`.** `Array<T>` and `&Array<T>` implement
    `IntoObjectRef<Array<U>>` for each
    `U: TypeSubsumes<T>`, and `Map` does the same for its keys and values. A
    read-only API can
    take `impl IntoObjectRef<Map<String, Base>>` instead of `&Map<String,
    Base>`, and accept an
      owned or borrowed map of `Base` or of a subtype.
    - **Not covered.** An upcast to `Any` (`Array<i64>` to `Array<Any>`) and
    a nested upcast
    (`Array<Array<Sub>>` to `Array<Array<Base>>`), which C++ allows. Either
    impl would overlap the
      blanket reflexive impl.
---
 rust/tvm-ffi/src/collections/array.rs | 75 ++++++++++++++++++++++++++++++++++-
 rust/tvm-ffi/src/collections/map.rs   | 57 +++++++++++++++++++++++++-
 rust/tvm-ffi/src/lib.rs               |  2 +-
 rust/tvm-ffi/src/macros.rs            | 10 ++++-
 rust/tvm-ffi/src/type_traits.rs       | 17 ++++++++
 rust/tvm-ffi/tests/test_rs_object.rs  | 40 +++++++++++++++++++
 6 files changed, 195 insertions(+), 6 deletions(-)

diff --git a/rust/tvm-ffi/src/collections/array.rs 
b/rust/tvm-ffi/src/collections/array.rs
index 91c9f65d..344ebeeb 100644
--- a/rust/tvm-ffi/src/collections/array.rs
+++ b/rust/tvm-ffi/src/collections/array.rs
@@ -25,8 +25,8 @@ use std::ops::Deref;
 use crate::any::TryFromTemp;
 use crate::collections::list::seq_cells;
 use crate::derive::Object;
-use crate::object::{Object, ObjectArc};
-use crate::type_traits::{ContainerElement, RefFromAnyUnchecked};
+use crate::object::{IntoObjectRef, Object, ObjectArc};
+use crate::type_traits::{ContainerElement, RefFromAnyUnchecked, TypeSubsumes};
 use crate::{Any, AnyCompatible, AnyView, ObjectCoreWithExtraItems, 
ObjectRefCore};
 use tvm_ffi_sys::TVMFFITypeIndex as TypeIndex;
 use tvm_ffi_sys::{TVMFFIAny, TVMFFIObject};
@@ -136,6 +136,44 @@ impl<T: ContainerElement + Clone> From<Vec<T>> for 
Array<T> {
     }
 }
 
+impl<T: ContainerElement + Clone, const N: usize> From<[T; N]> for Array<T> {
+    fn from(items: [T; N]) -> Self {
+        Self::from_vec(items.into())
+    }
+}
+
+impl<T: ContainerElement + Clone> From<&[T]> for Array<T> {
+    fn from(items: &[T]) -> Self {
+        Self::from_vec(items.to_vec())
+    }
+}
+
+/// A reference-count bump, as `From<&T> for T` of an object reference.
+impl<T: ContainerElement + Clone> From<&Array<T>> for Array<T> {
+    #[inline]
+    fn from(value: &Array<T>) -> Self {
+        value.clone()
+    }
+}
+
+impl<T, U: TypeSubsumes<T>> IntoObjectRef<Array<U>> for Array<T> {
+    type Output = Array<U>;
+    #[inline(always)]
+    fn into_object_ref(self) -> Array<U> {
+        self.upcast()
+    }
+}
+
+impl<'a, T, U: TypeSubsumes<T> + ContainerElement + Clone + 'a> 
IntoObjectRef<Array<U>>
+    for &'a Array<T>
+{
+    type Output = &'a Array<U>;
+    #[inline(always)]
+    fn into_object_ref(self) -> &'a Array<U> {
+        self.upcast_ref()
+    }
+}
+
 /// An array upcasts to the root [`ObjectRef`](crate::object::ObjectRef).
 impl<T: ContainerElement + Clone> From<Array<T>> for crate::object::ObjectRef {
     #[inline]
@@ -161,6 +199,39 @@ impl<T: ContainerElement + Clone> 
TryFrom<&crate::object::ObjectRef> for Array<T
     }
 }
 
+impl<T> Array<T> {
+    /// View this array as an `Array<U>` without a copy, as C++
+    /// `Array<U>(const Array<T>&)` does for a base `U` of `T`.
+    ///
+    /// It is a method, not `From`: `From<Array<T>> for Array<U>` would overlap
+    /// `From<T> for T` at `T = U` (E0119), and a crate that defines `T` and 
`U`
+    /// cannot implement it for `Array` either (orphan rule, E0117).
+    ///
+    /// ```
+    /// use tvm_ffi::object::{ObjectRef, ObjectRefCore};
+    /// use tvm_ffi::{Array, Shape};
+    ///
+    /// let shapes = Array::from([Shape::from([1_i64, 2])]);
+    /// let objects: Array<ObjectRef> = shapes.clone().upcast();
+    /// assert!(objects[0].same_as(&shapes[0]));
+    /// ```
+    #[inline]
+    pub fn upcast<U: TypeSubsumes<T>>(self) -> Array<U> {
+        Array {
+            data: self.data,
+            _marker: PhantomData,
+        }
+    }
+
+    /// Borrow this array as an `Array<U>`, as [`upcast`](Self::upcast) does.
+    #[inline]
+    pub fn upcast_ref<U: TypeSubsumes<T>>(&self) -> &Array<U> {
+        // SAFETY: `Array<T>` and `Array<U>` are both `repr(C)` over one
+        // `ObjectArc<ArrayObj>`, and every `T` cell is a valid `U` cell.
+        unsafe { &*(self as *const Self as *const Array<U>) }
+    }
+}
+
 unsafe impl<T: ContainerElement + Clone> ObjectRefCore for Array<T> {
     type ContainerType = ArrayObj;
 
diff --git a/rust/tvm-ffi/src/collections/map.rs 
b/rust/tvm-ffi/src/collections/map.rs
index fd250cd4..2da14c0d 100644
--- a/rust/tvm-ffi/src/collections/map.rs
+++ b/rust/tvm-ffi/src/collections/map.rs
@@ -36,8 +36,9 @@ use std::ops::Deref;
 use crate::any::TryFromTemp;
 use crate::derive::Object;
 use crate::function::Function;
+use crate::object::IntoObjectRef;
 use crate::object::{Object, ObjectArc};
-use crate::type_traits::ContainerElement;
+use crate::type_traits::{ContainerElement, TypeSubsumes};
 use crate::{Any, AnyCompatible, AnyView, Error, ObjectRefCore, Result};
 use tvm_ffi_sys::TVMFFITypeIndex as TypeIndex;
 use tvm_ffi_sys::{TVMFFIAny, TVMFFIObject};
@@ -412,6 +413,60 @@ where
     }
 }
 
+impl<K: ContainerElement, V: ContainerElement, const N: usize> From<[(K, V); 
N]> for Map<K, V> {
+    fn from(pairs: [(K, V); N]) -> Self {
+        Self::from_pairs(&pairs).expect("ffi.Map() failed to construct a map")
+    }
+}
+
+/// A reference-count bump, as `From<&T> for T` of an object reference.
+impl<K, V> From<&Map<K, V>> for Map<K, V> {
+    #[inline]
+    fn from(value: &Map<K, V>) -> Self {
+        value.clone()
+    }
+}
+
+impl<K, V> Map<K, V> {
+    /// View this map as a `Map<KU, VU>` without a copy, as C++
+    /// `Map<KU, VU>(const Map<K, V>&)` does for bases `KU` and `VU`.
+    ///
+    /// It is a method, not `From`, for the reasons on 
[`Array::upcast`](crate::Array::upcast).
+    #[inline]
+    pub fn upcast<KU: TypeSubsumes<K>, VU: TypeSubsumes<V>>(self) -> Map<KU, 
VU> {
+        Map {
+            data: self.data,
+            _marker: PhantomData,
+        }
+    }
+
+    /// Borrow this map as a `Map<KU, VU>`, as [`upcast`](Self::upcast) does.
+    #[inline]
+    pub fn upcast_ref<KU: TypeSubsumes<K>, VU: TypeSubsumes<V>>(&self) -> 
&Map<KU, VU> {
+        // SAFETY: both are `repr(C)` over one `ObjectArc<MapObj>`, and every
+        // entry of `Map<K, V>` is a valid entry of `Map<KU, VU>`.
+        unsafe { &*(self as *const Self as *const Map<KU, VU>) }
+    }
+}
+
+impl<K, V, KU: TypeSubsumes<K>, VU: TypeSubsumes<V>> IntoObjectRef<Map<KU, 
VU>> for Map<K, V> {
+    type Output = Map<KU, VU>;
+    #[inline(always)]
+    fn into_object_ref(self) -> Map<KU, VU> {
+        self.upcast()
+    }
+}
+
+impl<'a, K, V, KU: TypeSubsumes<K> + 'a, VU: TypeSubsumes<V> + 'a> 
IntoObjectRef<Map<KU, VU>>
+    for &'a Map<K, V>
+{
+    type Output = &'a Map<KU, VU>;
+    #[inline(always)]
+    fn into_object_ref(self) -> &'a Map<KU, VU> {
+        self.upcast_ref()
+    }
+}
+
 // --- Iterators ---
 //
 // The functor from `ffi.MapForwardIterFunctor` does not keep the map alive, so
diff --git a/rust/tvm-ffi/src/lib.rs b/rust/tvm-ffi/src/lib.rs
index 36651f58..102f53ad 100644
--- a/rust/tvm-ffi/src/lib.rs
+++ b/rust/tvm-ffi/src/lib.rs
@@ -111,7 +111,7 @@ pub use crate::reflection::{
 pub use crate::rvalue_ref::RValueRef;
 pub use crate::static_init::run_static_init_blocks;
 pub use crate::string::{Bytes, String};
-pub use crate::type_traits::AnyCompatible;
+pub use crate::type_traits::{AnyCompatible, TypeSubsumes};
 /// Generate runtime type dispatch from the `prefix_*` methods of an impl.
 ///
 /// `#[dispatch(prefix, (First, Args...), Ret)]` on an inherent impl makes its
diff --git a/rust/tvm-ffi/src/macros.rs b/rust/tvm-ffi/src/macros.rs
index 13e0383d..4b59ecb4 100644
--- a/rust/tvm-ffi/src/macros.rs
+++ b/rust/tvm-ffi/src/macros.rs
@@ -228,8 +228,10 @@ macro_rules! attach_context {
 /// its base object-reference types: `From<Source>` and `From<&Source>` into
 /// `Target`, `AsRef<Target>`, which borrows a `&Source` as `&Target`
 /// without touching the reference count, and
-/// [`IntoObjectRef<Target>`](crate::object::IntoObjectRef) for `Source` and 
`&Source`.
-/// It also implements the strict downcast back, `TryFrom<Target>` and
+/// [`IntoObjectRef<Target>`](crate::object::IntoObjectRef) for `Source` and 
`&Source`,
+/// and [`TypeSubsumes<Source>`](crate::TypeSubsumes) for `Target`, so
+/// [`Array::upcast`](crate::Array::upcast) turns an `Array<Source>` into an
+/// `Array<Target>`. It also implements the strict downcast back, 
`TryFrom<Target>` and
 /// `TryFrom<&Target>` for `Source`, which return a `TypeError` naming both 
types.
 ///
 /// # Safety
@@ -272,6 +274,10 @@ macro_rules! unsafe_impl_object_ref_upcast {
                 }
             }
 
+            // SAFETY: the declaration promises every `source` is a valid
+            // `target`, and an object reference never changes its object.
+            unsafe impl $crate::type_traits::TypeSubsumes<$source> for $target 
{}
+
             impl $crate::object::IntoObjectRef<$target> for $source {
                 type Output = $target;
                 #[inline(always)]
diff --git a/rust/tvm-ffi/src/type_traits.rs b/rust/tvm-ffi/src/type_traits.rs
index 428b37c0..30ebf504 100644
--- a/rust/tvm-ffi/src/type_traits.rs
+++ b/rust/tvm-ffi/src/type_traits.rs
@@ -248,6 +248,23 @@ pub trait ContainerElement: container_element_ops::Ops {}
 impl<T: AnyCompatible> ContainerElement for T {}
 impl ContainerElement for Any {}
 
+/// Every `Source` value is a valid `Self` as stored, so a container of
+/// `Source` is a container of `Self` without a copy, as C++
+/// `type_subsumes_v<Self, Source>`. [`Array::upcast`](crate::Array::upcast)
+/// and [`Map::upcast`](crate::Map::upcast) use it.
+///
+/// It holds for every type and itself, and for a base object reference and
+/// each derived one, which 
[`unsafe_impl_object_ref_upcast!`](crate::unsafe_impl_object_ref_upcast)
+/// declares.
+///
+/// # Safety
+///
+/// Every cell that `Source` strictly accepts, `Self` strictly accepts, also
+/// after a change made through `Self`.
+pub unsafe trait TypeSubsumes<Source: ?Sized> {}
+
+unsafe impl<T: ?Sized> TypeSubsumes<T> for T {}
+
 /// A type whose value an FFI cell stores in place, so `&Self` borrows from the
 /// cell, as [`Array`](crate::Array) indexing does.
 ///
diff --git a/rust/tvm-ffi/tests/test_rs_object.rs 
b/rust/tvm-ffi/tests/test_rs_object.rs
index ab334b95..7a4aadb4 100644
--- a/rust/tvm-ffi/tests/test_rs_object.rs
+++ b/rust/tvm-ffi/tests/test_rs_object.rs
@@ -16,6 +16,8 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+use std::borrow::Borrow;
+
 use tvm_ffi::derive::{Object, ObjectRef};
 use tvm_ffi::object::{ObjectArc, ObjectRef, ObjectRefCore};
 use tvm_ffi::reflection::TypeAttrColumn;
@@ -406,6 +408,44 @@ fn test_constructors_deref_and_upcast() {
     assert!(ObjectRef::from(&root).same_as(&root));
 }
 
+#[test]
+fn test_containers_upcast_without_a_copy() {
+    register_types();
+    fn keep_array(value: impl Into<Array<RsPoint>>) -> Array<RsPoint> {
+        value.into()
+    }
+    fn keep_map(value: impl Into<Map<tvm_ffi::String, RsPoint>>) -> 
Map<tvm_ffi::String, RsPoint> {
+        value.into()
+    }
+    fn len(value: impl IntoObjectRef<Array<RsPoint>>) -> usize {
+        value.into_object_ref().borrow().len()
+    }
+
+    let (a, b) = (point(1, "a"), point(2, "b"));
+    let array = keep_array([a.clone(), b.clone()]);
+    assert!(keep_array(&array).same_as(&array));
+    assert!(keep_array(vec![a.clone()])[0].same_as(&a));
+
+    let points3 = Array::from([point3(1, 3)]);
+    let upcast: Array<RsPoint> = points3.clone().upcast();
+    assert!(upcast.same_as(&points3));
+    assert!(upcast[0].same_as(&points3[0]));
+    assert!(points3.upcast_ref::<RsPoint>().same_as(&points3));
+    assert_eq!(
+        (len(&points3), len(points3.clone()), len(&array)),
+        (1, 1, 2)
+    );
+
+    let map = keep_map([("a".into(), a.clone())]);
+    assert!(keep_map(&map).same_as(&map));
+    let map3: Map<tvm_ffi::String, RsPoint3> = [("p".into(), point3(1, 
3))].into();
+    let upcast: Map<tvm_ffi::String, RsPoint> = map3.clone().upcast();
+    assert!(upcast.same_as(&map3));
+    assert_eq!(upcast.get(&"p".into()).unwrap().unwrap().x, 1);
+    let roots: &Map<tvm_ffi::String, ObjectRef> = map3.upcast_ref();
+    assert!(roots.same_as(&map3));
+}
+
 #[test]
 fn test_new_takes_literals_of_the_field_types() {
     register_types();

Reply via email to