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 34fafd9e [Rust] Add IntoObjectRef<T>: owned or borrowed conversion 
into an object reference (#933)
34fafd9e is described below

commit 34fafd9ee80003a725851dd9adc8cd186fb0daf3
Author: Tianqi Chen <[email protected]>
AuthorDate: Thu Nov 5 10:35:56 2026 -0500

    [Rust] Add IntoObjectRef<T>: owned or borrowed conversion into an object 
reference (#933)
    
    Adds `IntoObjectRef<T>`, a conversion into the object reference `T` that
    moves an owned reference and borrows a borrowed one.
    
    ```rust
    pub trait IntoObjectRef<T> {
        type Output: Borrow<T> + Into<T>;   // `T` for an owned value, `&T` for 
a borrowed one
        fn into_object_ref(self) -> Self::Output;
        fn into_owned_object_ref(self) -> T { self.into_object_ref().into() }   
// provided
    }
    ```
    
    - **Generated per pair.** `impl_object_upcast!(Sub => Base)` implements
    `IntoObjectRef<Base>` for `Sub` (moved through the existing `From`) and
    for `&Sub` (borrowed through `AsRef`, with no reference-count change).
    `#[derive(ObjectRef)]` implements it for the type itself, so a function
    taking `impl IntoObjectRef<Base>` accepts `Base`, `&Base` and every
    declared subtype, owned or borrowed.
    - **Concrete impls.** Because each impl is for a specific type pair,
    owned and borrowed impls never overlap. Blanket impls over both `T` and
    `&T` would be rejected by Rust's coherence rules, which forced
    hand-written per-type lists downstream. A function that only reads the
    value borrows the output with `.borrow()`; one that keeps it calls
    `into_owned_object_ref()`, as with `Cow::into_owned`, which clones only
    a borrowed reference.
    - **Infallible.** Hierarchy conversions cannot fail. Downstream traits
    that need fallibility, such as TIRx's `IntoExpr` with its `ExprResult`,
    add it on top.
---
 rust/tvm-ffi-macros/src/object_macros.rs | 16 ++++++++
 rust/tvm-ffi/src/lib.rs                  |  3 +-
 rust/tvm-ffi/src/macros.rs               | 21 +++++++++-
 rust/tvm-ffi/src/object.rs               | 67 ++++++++++++++++++++++++++++++++
 4 files changed, 104 insertions(+), 3 deletions(-)

diff --git a/rust/tvm-ffi-macros/src/object_macros.rs 
b/rust/tvm-ffi-macros/src/object_macros.rs
index 4808d130..3994074b 100644
--- a/rust/tvm-ffi-macros/src/object_macros.rs
+++ b/rust/tvm-ffi-macros/src/object_macros.rs
@@ -601,6 +601,22 @@ pub(crate) fn expand_object_ref(
             }
         }
 
+        impl #tvm_ffi_crate::object::IntoObjectRef<#struct_name> for 
#struct_name {
+            type Output = Self;
+            #[inline(always)]
+            fn into_object_ref(self) -> Self {
+                self
+            }
+        }
+
+        impl<'a> #tvm_ffi_crate::object::IntoObjectRef<#struct_name> for &'a 
#struct_name {
+            type Output = &'a #struct_name;
+            #[inline(always)]
+            fn into_object_ref(self) -> &'a #struct_name {
+                self
+            }
+        }
+
         impl ::std::convert::From<&#struct_name> for #struct_name {
             #[inline]
             fn from(value: &#struct_name) -> Self {
diff --git a/rust/tvm-ffi/src/lib.rs b/rust/tvm-ffi/src/lib.rs
index 951a00a1..0c433d51 100644
--- a/rust/tvm-ffi/src/lib.rs
+++ b/rust/tvm-ffi/src/lib.rs
@@ -71,7 +71,8 @@ pub use crate::extra::unchanged::{Unchanged, UnchangedOr};
 pub use crate::function::Function;
 pub use crate::object::ObjectRefCast;
 pub use crate::object::{
-    Object, ObjectArc, ObjectCore, ObjectCoreWithExtraItems, ObjectIdentity, 
ObjectRefCore,
+    IntoObjectRef, Object, ObjectArc, ObjectCore, ObjectCoreWithExtraItems, 
ObjectIdentity,
+    ObjectRefCore,
 };
 pub use crate::optional::{Optional, OptionalCompatible};
 pub use crate::reflection::{
diff --git a/rust/tvm-ffi/src/macros.rs b/rust/tvm-ffi/src/macros.rs
index cc371779..7a6811a5 100644
--- a/rust/tvm-ffi/src/macros.rs
+++ b/rust/tvm-ffi/src/macros.rs
@@ -62,8 +62,9 @@ macro_rules! cached_global_func {
 
 /// Implement zero-copy conversions from a derived object reference to one of
 /// its base object-reference types: `From<Source>` and `From<&Source>` into
-/// `Target`, and `AsRef<Target>`, which borrows a `&Source` as `&Target`
-/// without touching the reference count.
+/// `Target`, `AsRef<Target>`, which borrows a `&Source` as `&Target`
+/// without touching the reference count, and
+/// [`IntoObjectRef<Target>`](crate::object::IntoObjectRef) for `Source` and 
`&Source`.
 ///
 /// # Safety
 ///
@@ -103,6 +104,22 @@ macro_rules! impl_object_upcast {
                 }
             }
 
+            impl $crate::object::IntoObjectRef<$target> for $source {
+                type Output = $target;
+                #[inline(always)]
+                fn into_object_ref(self) -> $target {
+                    ::core::convert::Into::into(self)
+                }
+            }
+
+            impl<'a> $crate::object::IntoObjectRef<$target> for &'a $source {
+                type Output = &'a $target;
+                #[inline(always)]
+                fn into_object_ref(self) -> &'a $target {
+                    ::core::convert::AsRef::<$target>::as_ref(self)
+                }
+            }
+
             impl ::core::convert::AsRef<$target> for $source {
                 #[inline(always)]
                 fn as_ref(&self) -> &$target {
diff --git a/rust/tvm-ffi/src/object.rs b/rust/tvm-ffi/src/object.rs
index af14e45d..4706a6e8 100644
--- a/rust/tvm-ffi/src/object.rs
+++ b/rust/tvm-ffi/src/object.rs
@@ -184,6 +184,73 @@ pub unsafe trait ObjectRefCore: Sized + Clone {
     }
 }
 
+/// A value that converts into the object reference `T`, owned or borrowed.
+///
+/// An owned reference converts into an owned `T`, and a borrowed one borrows
+/// as `&T` without changing a reference count, as [`IntoIterator`] consumes a
+/// `Vec` but borrows from a `&Vec`. 
[`impl_object_upcast!`](crate::impl_object_upcast)
+/// implements it for each derived reference and its reference into the base,
+/// and `#[derive(ObjectRef)]` for the type itself. A function that only reads
+/// the value borrows the output with `.borrow()`; one that keeps it takes
+/// ownership with [`into_owned_object_ref`](Self::into_owned_object_ref), 
which clones only a
+/// borrowed reference.
+///
+/// # Examples
+///
+/// ```
+/// use std::borrow::Borrow;
+/// use tvm_ffi::object::{IntoObjectRef, ObjectRef, ObjectRefCore};
+/// use tvm_ffi::{def_object_ref, rs_object, Object};
+///
+/// #[rs_object("my_ext.MyPair", final)]
+/// pub struct MyPairObj {
+///     base: Object,
+///     pub first: i64,
+///     pub second: i64,
+/// }
+/// def_object_ref!(pub MyPair, ObjectRef, MyPairObj);
+///
+/// fn same(a: impl IntoObjectRef<ObjectRef>, b: &ObjectRef) -> bool {
+///     a.into_object_ref().borrow().same_as(b)
+/// }
+///
+/// let pair = MyPair::from(MyPairObj::new(Object::new(), 1, 2));
+/// let base: ObjectRef = pair.clone().into();
+/// assert!(same(&pair, &base)); // borrowed as `&ObjectRef`, no new reference
+/// assert!(same(pair.clone(), &base)); // moved into an `ObjectRef`
+///
+/// // A function that keeps the value takes ownership.
+/// fn keep(value: impl IntoObjectRef<ObjectRef>) -> ObjectRef {
+///     value.into_owned_object_ref()
+/// }
+/// assert!(keep(&pair).same_as(&base));
+/// ```
+pub trait IntoObjectRef<T> {
+    /// `T` for an owned value, or `&T` for a borrowed one.
+    type Output: std::borrow::Borrow<T> + Into<T>;
+
+    /// Convert into the reference `T`.
+    ///
+    /// # Returns
+    ///
+    /// The owned or borrowed reference.
+    fn into_object_ref(self) -> Self::Output;
+
+    /// Convert into an owned `T`, as 
[`Cow::into_owned`](std::borrow::Cow::into_owned)
+    /// does: an owned value moves, and only a borrowed reference is cloned.
+    ///
+    /// # Returns
+    ///
+    /// The owned reference.
+    #[inline(always)]
+    fn into_owned_object_ref(self) -> T
+    where
+        Self: Sized,
+    {
+        self.into_object_ref().into()
+    }
+}
+
 /// An owning, hashable identity key for an FFI object.
 ///
 /// The retained strong reference prevents the allocation address from being

Reply via email to