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