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 658bd6c5 [Rust] ObjectRefAnyCompatible and derive-only object
reference declaration (#945)
658bd6c5 is described below
commit 658bd6c584aa462e37a9e8ce69ce74c1a519f9bf
Author: Tianqi Chen <[email protected]>
AuthorDate: Fri Nov 6 13:35:53 2026 -0500
[Rust] ObjectRefAnyCompatible and derive-only object reference declaration
(#945)
**[Rust] ObjectRefAnyCompatible: overridable AnyCompatible base for
object references**
A reference with a stricter check than its object type, such as a typed
expression view, had to hand-write its whole `AnyCompatible` over raw
`TVMFFIAny` cells. `ObjectRefAnyCompatible` is the object-reference base
of
`AnyCompatible`, as C++ `TypeTraits<T>` derives from
`ObjectRefTypeTraitsBase<T>`: its hooks are safe and defaulted, and a
blanket
impl makes each implementer `AnyCompatible` with the object cell
encoding.
- `extra_check(&ContainerType) -> bool` runs after the object-type
check, in
every strict and converting cast.
- `convert_fallback(AnyView) -> Option<Self>` converts other values, as
C++
`ConvertFallbackValue` does, for converting casts only (`TryFrom<Any>`,
typed function arguments); `downcast_ref`, `downcast` and `match_any!`
never call it.
- `custom_type_str()` and `custom_mismatch_type_info(AnyView)` replace
the
names in conversion errors and type schemas.
- The hidden `MATCH_ANY_EXACT` defaults to false, so a hand-written impl
is
never routed by type index in `match_any!`; `#[derive(ObjectRef)]`,
whose
impl overrides nothing, sets it, and the blanket impl adds the object
type's finality.
`#[derive(ObjectRef)]` now emits `unsafe impl ObjectRefAnyCompatible for
T`
with the defaults instead of its own `AnyCompatible`. Its helper
attributes
become one namespaced `#[object_ref(skip_debug)]`; the unused `type_key`
and `type_index` declarations are dropped. Containers keep their own
`AnyCompatible`.
**[Rust] Declare object references with #[derive(ObjectRef)] only**
An object reference had two declaration forms: `def_object_ref!`, which
also added `Deref`, `From<Obj>` and the parent upcast, and
`#[derive(ObjectRef)]`, which left those to hand-written code. The
derive
becomes the one form, configured by one namespaced helper attribute
`#[object_ref(...)]`:
- `Deref<Target = Obj>` for field reads, as C++ `operator->`;
`skip_deref`
opts out for a reference whose object is internal (`Error`, `Function`,
`Tensor`, `Shape`, `Module`, `ObjectRef`, ...).
- A private `fn from_object(object: Obj) -> Self` for the type's own
constructors instead of a public `From<Obj>`, so construction stays with
the type's author.
- `parent = Ref` adds the upcast to the parent's reference.
- `skip_any_compatible` leaves `ObjectRefAnyCompatible` to a
hand-written impl, so a typed reference derives everything else.
- `skip_debug` as before, and `crate = path`.
The derive's in-place downcasts now come from
`unsafe_impl_object_ref_downcast_ref!`. `def_object_ref!` is removed;
tvm-ffi's
own docs, tests and `VisitInterrupt` use the derive.
The Rust stub generator no longer emits a reference `Deref`, and the
`rust_stubgen` example bindings are regenerated; bindings generated
before
this change define that `Deref` themselves and need regenerating.
`unsafe_impl_object_upcast!` is renamed
`unsafe_impl_object_ref_upcast!`; the stub generator emits the new name.
The in-place borrow macro is renamed
`unsafe_impl_object_ref_downcast_ref!`, and the new
`impl_any_compatible_conversions!` emits the `TryFrom`, `call_tuple` and
typed-function argument impls for a type that implements `AnyCompatible`
by hand; the derive uses it, and its three building blocks are hidden.
---
docs/guides/rust_lang_guide.rst | 40 ++-
.../rust/src/generated/rust_stubgen/mod.rs | 8 -
python/tvm_ffi/stub/rust_generator/codegen.py | 13 +-
rust/tvm-ffi-macros/src/def_object_ref.rs | 148 ----------
rust/tvm-ffi-macros/src/lib.rs | 13 +-
rust/tvm-ffi-macros/src/object_macros.rs | 311 ++++++++++-----------
rust/tvm-ffi/src/any.rs | 8 +-
rust/tvm-ffi/src/collections/shape.rs | 1 +
rust/tvm-ffi/src/collections/tensor.rs | 1 +
rust/tvm-ffi/src/derive.rs | 72 ++++-
rust/tvm-ffi/src/error.rs | 2 +-
rust/tvm-ffi/src/extra/module.rs | 1 +
rust/tvm-ffi/src/extra/structural_mutate.rs | 5 +-
rust/tvm-ffi/src/extra/structural_visit.rs | 35 +--
rust/tvm-ffi/src/function.rs | 1 +
rust/tvm-ffi/src/function_internal.rs | 48 +---
rust/tvm-ffi/src/lib.rs | 60 +---
rust/tvm-ffi/src/macros.rs | 121 ++++----
rust/tvm-ffi/src/object.rs | 193 ++++++++++++-
rust/tvm-ffi/src/reflection.rs | 2 +
rust/tvm-ffi/src/type_traits.rs | 17 +-
rust/tvm-ffi/tests/test_cast.rs | 2 +-
rust/tvm-ffi/tests/test_object_ref_any.rs | 159 +++++++++++
rust/tvm-ffi/tests/test_object_reflection.rs | 2 +-
rust/tvm-ffi/tests/test_object_registration.rs | 2 +-
rust/tvm-ffi/tests/test_rs_object.rs | 102 +++++--
tests/python/test_stubgen_rust.py | 59 +---
27 files changed, 811 insertions(+), 615 deletions(-)
diff --git a/docs/guides/rust_lang_guide.rst b/docs/guides/rust_lang_guide.rst
index 7a810785..1332452a 100644
--- a/docs/guides/rust_lang_guide.rst
+++ b/docs/guides/rust_lang_guide.rst
@@ -141,6 +141,9 @@ borrows it in place, with the same rules as ``Any`` and
``AnyView``. ``try_from`
``Any`` may convert by the FFI's conversion rules, such as an integer into a
float, while
``try_from`` a reference only checks.
+``AnyCompatible`` is the root contract for crossing into ``Any``;
``#[derive(ObjectRef)]``
+is its object-reference default, and ``ObjectRefAnyCompatible`` customizes it.
+
Loading a Module
~~~~~~~~~~~~~~~~
@@ -336,7 +339,7 @@ first ``type_index()``, which allocating an instance calls:
impl PointObj {
fn reflect_methods(def: &mut ObjectDef<Self>) {
- def.def("x_abs", |p: Point| -> Result<i64> { Ok(p.data.x.abs()) },
"The |x|.")
+ def.def("x_abs", |p: Point| -> Result<i64> { Ok(p.x.abs()) }, "The
|x|.")
.def_static("zero", || -> Result<i64> { Ok(0) }, "");
}
}
@@ -366,12 +369,13 @@ Defining Object Types from Their Fields
``py_class``. The first field, ``base``, is the parent's object; every other
field
is reflected, and ``#[reflect(...)]`` sets its flags, name and doc string.
``reflection = path`` after the type key is ``#[type_reflection(path)]``.
-``def_object_ref!(TypeName, ParentType, ObjectName)`` defines the reference:
+``#[derive(ObjectRef)]`` declares the reference, and ``#[object_ref(parent =
Point)]``
+names the reference to the parent's object:
.. code-block:: rust
- use tvm_ffi::object::ObjectRef;
- use tvm_ffi::{def_object_ref, rs_object, Object};
+ use tvm_ffi::derive::ObjectRef;
+ use tvm_ffi::{rs_object, Object, ObjectArc};
#[rs_object("my.Point", s_eq_hash_kind = "TreeNode")]
pub struct PointObj {
@@ -381,7 +385,10 @@ is reflected, and ``#[reflect(...)]`` sets its flags, name
and doc string.
pub label: tvm_ffi::String,
}
- def_object_ref!(pub Point, ObjectRef, PointObj);
+ #[derive(Clone, ObjectRef)]
+ pub struct Point {
+ data: ObjectArc<PointObj>,
+ }
#[rs_object("my.Point3", final)]
pub struct Point3Obj {
@@ -389,9 +396,19 @@ is reflected, and ``#[reflect(...)]`` sets its flags, name
and doc string.
pub z: i64,
}
- def_object_ref!(pub Point3, Point, Point3Obj);
+ #[derive(Clone, ObjectRef)]
+ #[object_ref(parent = Point)]
+ pub struct Point3 {
+ data: ObjectArc<Point3Obj>,
+ }
+
+ impl Point3 {
+ pub fn new(x: i64, label: &str, z: i64) -> Self {
+ Self::from_object(Point3Obj::new(PointObj::new(Object::new(), x,
label), z))
+ }
+ }
- let point3 = Point3::from(Point3Obj::new(PointObj::new(Object::new(), 1,
"p"), 3));
+ let point3 = Point3::new(1, "p", 3);
assert_eq!((point3.x, point3.z), (1, 3));
let point: Point = point3.into();
@@ -399,10 +416,11 @@ is reflected, and ``#[reflect(...)]`` sets its flags,
name and doc string.
``Point3Obj::new``, which takes the parent's object, then the fields: a scalar
(``bool``, an integer or a float) as its own type, so that a literal infers as
it,
and any other field from anything that converts into it, as the ``new`` of a
type
-bound by ``tvm-ffi-stubgen`` does. The reference dereferences to the object,
and
-the object converts into it. The reference also converts into ``ParentType``,
the
-reference to its parent, and every reference converts into ``ObjectRef``;
-``unsafe_impl_object_upcast!`` converts it into the references to further
ancestors.
+bound by ``tvm-ffi-stubgen`` does. The reference dereferences to the object
+(``#[object_ref(skip_deref)]`` opts out), and its private ``from_object``
wraps a new
+object for the type's constructors. The reference also converts into
``parent``,
+and every reference converts into ``ObjectRef``;
``unsafe_impl_object_ref_upcast!``
+converts it into the references to further ancestors.
The type also registers a creator, and from it ``__ffi_new__`` and
``__ffi_init__``
(unless ``reflection = path`` defines ``__ffi_init__``), so that a JSON graph
reads
diff --git a/examples/rust_stubgen/rust/src/generated/rust_stubgen/mod.rs
b/examples/rust_stubgen/rust/src/generated/rust_stubgen/mod.rs
index 3d660241..a920ad01 100644
--- a/examples/rust_stubgen/rust/src/generated/rust_stubgen/mod.rs
+++ b/examples/rust_stubgen/rust/src/generated/rust_stubgen/mod.rs
@@ -24,7 +24,6 @@
// tvm-ffi-stubgen(prefix): rust_stubgen
// tvm-ffi-stubgen(begin): import-section
-use std::ops::Deref;
use tvm_ffi::Error;
use tvm_ffi::Object;
use tvm_ffi::ObjectArc;
@@ -87,13 +86,6 @@ pub struct IntPair {
base: ObjectArc<IntPairObj>,
}
-impl Deref for IntPair {
- type Target = IntPairObj;
- fn deref(&self) -> &IntPairObj {
- &self.base
- }
-}
-
impl IntPairObj {
pub(crate) fn new(a: i64, b: i64, kind: PairKind) -> Self {
let base = Object::new();
diff --git a/python/tvm_ffi/stub/rust_generator/codegen.py
b/python/tvm_ffi/stub/rust_generator/codegen.py
index a8bf35c4..e4378814 100644
--- a/python/tvm_ffi/stub/rust_generator/codegen.py
+++ b/python/tvm_ffi/stub/rust_generator/codegen.py
@@ -16,8 +16,9 @@
# under the License.
"""Rust code generation for ``tvm-ffi-stubgen``.
-Every reflected object gets a ``#[repr(C)]`` object struct, a reference
wrapper,
-``Deref``, and the upcasts along its ancestor chain. The struct's contents
follow
+Every reflected object gets a ``#[repr(C)]`` object struct, a reference wrapper
+(whose ``#[derive(ObjectRef)]`` adds ``Deref`` to the object), and the upcasts
+along its ancestor chain. The struct's contents follow
the verdict of :mod:`tvm_ffi.stub.layout`:
- *complete*: the struct mirrors every physical field at its real offset and
@@ -416,7 +417,7 @@ class _ObjectRenderer:
]
def _upcast_lines(self) -> list[str]:
- """``unsafe_impl_object_upcast!`` to every ancestor's wrapper, then
the ``upcast`` directives."""
+ """``unsafe_impl_object_ref_upcast!`` to every ancestor's wrapper,
then the ``upcast`` directives."""
targets: list[str] = []
for key in self.info.ancestors:
provider = self._provider(key)
@@ -429,7 +430,7 @@ class _ObjectRenderer:
if not targets:
return []
pairs = ", ".join(f"{self.leaf} => {target}" for target in targets)
- return [f"tvm_ffi::unsafe_impl_object_upcast!({pairs});"]
+ return [f"tvm_ffi::unsafe_impl_object_ref_upcast!({pairs});"]
def _struct_lines(self, verdict: Verdict, base: str) -> list[str]:
"""Render the object struct: every field when complete, the parent
alone when opaque."""
@@ -655,9 +656,10 @@ class _ObjectRenderer:
self.imports.reserved.update((self.leaf, self.obj_struct))
verdict = self.classify()
# Derive macros are spelled by full path: their leaves collide with
`Object` / `ObjectRef`.
- self.imports.record("std::ops::Deref")
self.imports.record("tvm_ffi::ObjectArc")
base, has_parent = self._base_type()
+ if has_parent:
+ self.imports.record("std::ops::Deref")
fields = self.info.fields
has_accessors = bool(fields) and not verdict.is_complete
if has_accessors:
@@ -682,7 +684,6 @@ class _ObjectRenderer:
"}",
]
)
- sections.append(self._deref_lines(self.leaf, self.obj_struct, "base"))
if has_parent:
sections.append(self._deref_lines(self.obj_struct, base, "base"))
if has_accessors:
diff --git a/rust/tvm-ffi-macros/src/def_object_ref.rs
b/rust/tvm-ffi-macros/src/def_object_ref.rs
deleted file mode 100644
index 14b01eab..00000000
--- a/rust/tvm-ffi-macros/src/def_object_ref.rs
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-use proc_macro::TokenStream;
-use proc_macro2::TokenStream as TokenStream2;
-use quote::quote;
-use syn::parse::{Parse, ParseStream};
-use syn::{parse_macro_input, Attribute, DeriveInput, Ident, Path, Token, Type,
Visibility};
-
-use crate::object_macros::expand_object_ref;
-use crate::utils::get_tvm_ffi_crate;
-
-pub(crate) fn def_object_ref(input: TokenStream) -> TokenStream {
- let args = parse_macro_input!(input as DefObjectRefArgs);
- expand(&args)
- .unwrap_or_else(|error| error.to_compile_error())
- .into()
-}
-
-/// `def_object_ref!(#[attrs] pub TypeName, ParentType, ObjectName, crate =
path)`.
-struct DefObjectRefArgs {
- attrs: Vec<Attribute>,
- vis: Visibility,
- type_name: Ident,
- parent_type: Type,
- object_name: Type,
- tvm_ffi_crate: Option<Path>,
-}
-
-impl Parse for DefObjectRefArgs {
- fn parse(input: ParseStream) -> syn::Result<Self> {
- let attrs = input.call(Attribute::parse_outer)?;
- let vis = input.parse()?;
- let type_name = input.parse()?;
- input.parse::<Token![,]>()?;
- let parent_type = input.parse()?;
- input.parse::<Token![,]>()?;
- let object_name = input.parse()?;
- let mut tvm_ffi_crate = None;
- if input.parse::<Option<Token![,]>>()?.is_some() &&
input.peek(Token![crate]) {
- input.parse::<Token![crate]>()?;
- input.parse::<Token![=]>()?;
- tvm_ffi_crate = Some(Path::parse_mod_style(input)?);
- input.parse::<Option<Token![,]>>()?;
- }
- if !input.is_empty() {
- return Err(input.error("expected `crate = <path>` after the object
type"));
- }
- Ok(DefObjectRefArgs {
- attrs,
- vis,
- type_name,
- parent_type,
- object_name,
- tvm_ffi_crate,
- })
- }
-}
-
-fn expand(args: &DefObjectRefArgs) -> syn::Result<TokenStream2> {
- let tvm_ffi = match &args.tvm_ffi_crate {
- Some(path) => quote!(#path),
- None => get_tvm_ffi_crate(),
- };
- let DefObjectRefArgs {
- attrs,
- vis,
- type_name,
- parent_type,
- object_name,
- ..
- } = args;
- let ref_input: DeriveInput = syn::parse2(quote! {
- #vis struct #type_name {
- base: #tvm_ffi::object::ObjectArc<#object_name>,
- }
- })?;
- let ref_impl = expand_object_ref(&ref_input, &tvm_ffi);
- // `#[derive(ObjectRef)]` already upcasts every reference to `ObjectRef`.
- let parent_is_root = match parent_type {
- syn::Type::Path(path) => path
- .path
- .segments
- .last()
- .is_some_and(|s| s.ident == "ObjectRef"),
- _ => false,
- };
- let parent_upcast = if parent_is_root {
- None
- } else {
- Some(quote!(#tvm_ffi::unsafe_impl_object_upcast!(#type_name =>
#parent_type);))
- };
- let doc = if attrs.iter().any(|a| a.path.is_ident("doc")) {
- None
- } else {
- let doc = format!(
- "The reference to [`{}`].",
- quote!(#object_name).to_string().replace(' ', "")
- );
- Some(quote!(#[doc = #doc]))
- };
-
- Ok(quote! {
- #doc
- #(#attrs)*
- #[repr(C)]
- #[derive(::core::clone::Clone)]
- #vis struct #type_name {
- base: #tvm_ffi::object::ObjectArc<#object_name>,
- }
-
- #ref_impl
-
- impl ::core::ops::Deref for #type_name {
- type Target = #object_name;
- #[inline]
- fn deref(&self) -> &#object_name {
- &self.base
- }
- }
-
- impl ::core::convert::From<#object_name> for #type_name {
- #[inline]
- fn from(object: #object_name) -> Self {
- Self {
- base: #tvm_ffi::object::ObjectArc::new(object),
- }
- }
- }
-
- #parent_upcast
- })
-}
diff --git a/rust/tvm-ffi-macros/src/lib.rs b/rust/tvm-ffi-macros/src/lib.rs
index d589c777..ee52a0a1 100644
--- a/rust/tvm-ffi-macros/src/lib.rs
+++ b/rust/tvm-ffi-macros/src/lib.rs
@@ -20,7 +20,6 @@
use proc_macro::TokenStream;
use proc_macro_error::proc_macro_error;
-mod def_object_ref;
mod dispatch;
mod match_any;
mod object_macros;
@@ -105,8 +104,10 @@ pub fn derive_object(input: TokenStream) -> TokenStream {
object_macros::derive_object(input)
}
+// Documented on the `tvm_ffi::derive::ObjectRef` re-export, whose examples
+// run as tvm-ffi doctests.
#[proc_macro_error]
-#[proc_macro_derive(ObjectRef, attributes(type_key, type_index, custom_debug))]
+#[proc_macro_derive(ObjectRef, attributes(object_ref))]
pub fn derive_object_ref(input: TokenStream) -> TokenStream {
object_macros::derive_object_ref(input)
}
@@ -118,11 +119,3 @@ pub fn derive_object_ref(input: TokenStream) ->
TokenStream {
pub fn rs_object(args: TokenStream, item: TokenStream) -> TokenStream {
rs_object::rs_object(args, item)
}
-
-// Documented on the `tvm_ffi::def_object_ref` re-export, whose examples run as
-// tvm-ffi doctests.
-#[proc_macro_error]
-#[proc_macro]
-pub fn def_object_ref(input: TokenStream) -> TokenStream {
- def_object_ref::def_object_ref(input)
-}
diff --git a/rust/tvm-ffi-macros/src/object_macros.rs
b/rust/tvm-ffi-macros/src/object_macros.rs
index 607b0cb0..b6d3aba1 100644
--- a/rust/tvm-ffi-macros/src/object_macros.rs
+++ b/rust/tvm-ffi-macros/src/object_macros.rs
@@ -18,6 +18,7 @@
*/
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
+use proc_macro_error::abort;
use quote::{format_ident, quote};
use syn::ext::IdentExt;
use syn::DeriveInput;
@@ -515,13 +516,18 @@ pub fn derive_object_ref(input: proc_macro::TokenStream)
-> TokenStream {
TokenStream::from(expand_object_ref(&derive_input, &get_tvm_ffi_crate()))
}
-/// The `ObjectRefCore` and `AnyCompatible` impls of a reference struct,
+/// The impls of a reference struct, as configured by `#[object_ref(...)]`,
/// naming the tvm-ffi crate `tvm_ffi_crate`.
pub(crate) fn expand_object_ref(
derive_input: &DeriveInput,
tvm_ffi_crate: &TokenStream2,
) -> TokenStream2 {
let struct_name = derive_input.ident.clone();
+ let options = object_ref_options(derive_input);
+ let tvm_ffi_crate = &match &options.tvm_ffi_crate {
+ Some(path) => quote!(#path),
+ None => tvm_ffi_crate.clone(),
+ };
// The `ObjectArc<T>` slot is the first field; its name is the struct's
choice
// (`data` in this crate, `base` in stubgen output, so that reflected
fields
@@ -535,10 +541,13 @@ pub(crate) fn expand_object_ref(
_ => panic!("derive only works for structs"),
}
.expect("First field must be `<name>: ObjectArc<T>`");
+ // Spell the object type as written, so rustdoc shows `Target = FooObj`.
+ let object_ty = object_arc_argument(&data_ty)
+ .unwrap_or_else(|| quote!(<#data_ty as ::core::ops::Deref>::Target));
let mut expanded = quote! {
unsafe impl #tvm_ffi_crate::object::ObjectRefCore for #struct_name {
- type ContainerType = <#data_ty as std::ops::Deref>::Target;
+ type ContainerType = #object_ty;
#[inline]
fn data(this: &Self) ->
&#tvm_ffi_crate::object::ObjectArc<Self::ContainerType> {
&this.#data_id
@@ -555,6 +564,15 @@ pub(crate) fn expand_object_ref(
}
}
+ impl #struct_name {
+ /// The reference to a new object, for the type's constructors.
+ #[allow(dead_code)]
+ #[inline(always)]
+ fn from_object(object: #object_ty) -> Self {
+ Self { #data_id:
#tvm_ffi_crate::object::ObjectArc::new(object) }
+ }
+ }
+
// The handle is exactly its object pointer, which an FFI cell's
// payload word holds, so an `Array` cell can lend `&Self` in place.
const _: () = {
@@ -567,12 +585,8 @@ pub(crate) fn expand_object_ref(
);
};
- unsafe impl #tvm_ffi_crate::type_traits::RefFromAnyUnchecked for
#struct_name {
- #[inline(always)]
- unsafe fn ref_from_any_unchecked(cell:
&#tvm_ffi_crate::tvm_ffi_sys::TVMFFIAny) -> &Self {
- &*(::core::ptr::addr_of!(cell.data_union) as *const Self)
- }
- }
+ // SAFETY: one `ObjectArc` (asserted above), checked by
`check_any_strict`.
+ #tvm_ffi_crate::unsafe_impl_object_ref_downcast_ref!(#struct_name);
// As `str`, `Path` and `[T]` do, so `impl AsRef<Base>` bounds accept
the
// base itself; upcasts add `AsRef` to each base.
@@ -583,24 +597,6 @@ pub(crate) fn expand_object_ref(
}
}
- unsafe impl #tvm_ffi_crate::type_traits::DowncastRefFromAny for
#struct_name {
- #[inline(always)]
- unsafe fn downcast_ref_from_any(
- cell: &#tvm_ffi_crate::tvm_ffi_sys::TVMFFIAny,
- ) -> ::core::option::Option<&Self> {
-
#tvm_ffi_crate::type_traits::downcast_ref_from_any_in_place::<Self>(cell)
- }
- }
-
- unsafe impl #tvm_ffi_crate::type_traits::DowncastRefFromObject for
#struct_name {
- #[inline(always)]
- unsafe fn downcast_ref_from_object(
- place: &*mut #tvm_ffi_crate::tvm_ffi_sys::TVMFFIObject,
- ) -> ::core::option::Option<&Self> {
-
#tvm_ffi_crate::type_traits::downcast_ref_from_object_in_place::<Self>(place)
- }
- }
-
impl #tvm_ffi_crate::object::IntoObjectRef<#struct_name> for
#struct_name {
type Output = Self;
#[inline(always)]
@@ -623,157 +619,146 @@ pub(crate) fn expand_object_ref(
value.clone()
}
}
-
- // implement AnyCompatible for #struct_name
- unsafe impl #tvm_ffi_crate::type_traits::AnyCompatible for
#struct_name {
- const MATCH_ANY_EXACT: bool = {
- type ContainerType =
- <#struct_name as
#tvm_ffi_crate::object::ObjectRefCore>::ContainerType;
- <ContainerType as
#tvm_ffi_crate::object::ObjectCore>::TYPE_FINAL
- };
-
- const MATCH_ANY_STRICT_EXACT: bool = Self::MATCH_ANY_EXACT;
-
- #[inline]
- fn match_any_exact_type_index() -> i32 {
- type ContainerType = <#struct_name as
#tvm_ffi_crate::object::ObjectRefCore>
- ::ContainerType;
- <ContainerType as
#tvm_ffi_crate::object::ObjectCore>::type_index()
- }
-
- #[inline]
- unsafe fn is_undefined(field: *const Self) -> bool {
-
#tvm_ffi_crate::object::ObjectArc::is_null(::core::ptr::addr_of!((*field).#data_id))
- }
-
- fn type_str() -> std::string::String {
- type ContainerType = <#struct_name as
#tvm_ffi_crate::object::ObjectRefCore>
- ::ContainerType;
- <ContainerType as
#tvm_ffi_crate::object::ObjectCore>::TYPE_KEY.into()
- }
-
- #[inline(always)]
- unsafe fn copy_to_any_view(
- src: &Self,
- data: &mut #tvm_ffi_crate::tvm_ffi_sys::TVMFFIAny
- ) {
- type ContainerType = <#struct_name as
#tvm_ffi_crate::object::ObjectRefCore>
- ::ContainerType;
- let data_ptr =
#tvm_ffi_crate::object::ObjectArc::<ContainerType>::as_ptr(
- &src.#data_id
- );
- let object_ptr =
- data_ptr as *mut ContainerType as *mut
#tvm_ffi_crate::tvm_ffi_sys::TVMFFIObject;
- data.type_index = (*object_ptr).type_index;
- // SAFETY: ObjectRefCore guarantees a live allocation whose
header
- // contains its registered object-range dynamic type index.
- ::core::hint::assert_unchecked(
- data.type_index >=
-
#tvm_ffi_crate::tvm_ffi_sys::TVMFFITypeIndex::kTVMFFIStaticObjectBegin as i32
- );
- data.small_str_len = 0;
- data.data_union.v_obj = object_ptr;
- }
-
- #[inline(always)]
- unsafe fn check_any_strict(
- data: & #tvm_ffi_crate::tvm_ffi_sys::TVMFFIAny
- ) -> bool {
- type ContainerType = <#struct_name as
#tvm_ffi_crate::object::ObjectRefCore>
- ::ContainerType;
-
#tvm_ffi_crate::object::is_instance_of::<ContainerType>(data.type_index)
- }
-
- unsafe fn copy_from_any_view_after_check(
- data: & #tvm_ffi_crate::tvm_ffi_sys::TVMFFIAny
- ) -> Self {
- type ContainerType = <#struct_name as
#tvm_ffi_crate::object::ObjectRefCore>
- ::ContainerType;
- let data_ptr = data.data_union.v_obj;
- // need to increase ref because original weak ptr
- // do not own the code
- #tvm_ffi_crate::object::unsafe_::inc_ref(
- data_ptr as *mut #tvm_ffi_crate::tvm_ffi_sys::TVMFFIObject
- );
- Self {
- #data_id: #tvm_ffi_crate::object::ObjectArc::from_raw(
- data_ptr as *mut ContainerType
- )
- }
- }
-
- #[inline(always)]
- unsafe fn move_to_any(
- src: Self,
- data: &mut #tvm_ffi_crate::tvm_ffi_sys::TVMFFIAny
- ) {
- type ContainerType = <#struct_name as
#tvm_ffi_crate::object::ObjectRefCore>
- ::ContainerType;
- let data_ptr = #tvm_ffi_crate::object::ObjectArc::into_raw(
- src.#data_id
- );
- let object_ptr =
- data_ptr as *mut ContainerType as *mut
#tvm_ffi_crate::tvm_ffi_sys::TVMFFIObject;
- data.type_index = (*object_ptr).type_index;
- // SAFETY: ObjectRefCore guarantees a live allocation whose
header
- // contains its registered object-range dynamic type index.
- ::core::hint::assert_unchecked(
- data.type_index >=
-
#tvm_ffi_crate::tvm_ffi_sys::TVMFFITypeIndex::kTVMFFIStaticObjectBegin as i32
- );
- data.small_str_len = 0;
- data.data_union.v_obj = object_ptr;
- }
-
- #[inline(always)]
- unsafe fn move_from_any_after_check(
- data: &mut #tvm_ffi_crate::tvm_ffi_sys::TVMFFIAny
- ) -> Self {
- type ContainerType = <#struct_name as
#tvm_ffi_crate::object::ObjectRefCore>
- ::ContainerType;
- let data_ptr = data.data_union.v_obj as *mut ContainerType;
- Self {
- #data_id:
#tvm_ffi_crate::object::ObjectArc::<ContainerType>::from_raw(data_ptr)
- }
+ };
+ // The default `AnyCompatible`: the object type check alone, which
+ // `match_any!` may route by.
+ if !options.skip_any_compatible {
+ expanded.extend(quote! {
+ unsafe impl #tvm_ffi_crate::object::ObjectRefAnyCompatible for
#struct_name {
+ const MATCH_ANY_EXACT: bool = true;
}
-
- unsafe fn try_cast_from_any_view(
- data: & #tvm_ffi_crate::tvm_ffi_sys::TVMFFIAny
- ) -> Result<Self, ()> {
- type ContainerType = <#struct_name as
#tvm_ffi_crate::object::ObjectRefCore>
- ::ContainerType;
- if
#tvm_ffi_crate::object::is_instance_of::<ContainerType>(data.type_index) {
- Ok(Self::copy_from_any_view_after_check(data))
- } else {
- Err(())
+ });
+ }
+ // Field reads through the reference, as C++ `operator->`.
+ if !options.skip_deref {
+ expanded.extend(quote! {
+ impl ::core::ops::Deref for #struct_name {
+ type Target = #object_ty;
+ #[inline(always)]
+ fn deref(&self) -> &Self::Target {
+ &self.#data_id
}
}
- }
- };
+ });
+ }
// The runtime type key and object address, so `Result<_, Self>` from
- // `downcast` can be unwrapped. `#[custom_debug]` keeps a hand-written one.
- let custom_debug = derive_input
- .attrs
- .iter()
- .any(|attr| attr.path.is_ident("custom_debug"));
- if !custom_debug {
+ // `downcast` can be unwrapped.
+ if !options.skip_debug {
expanded.extend(quote! {
- impl ::core::fmt::Debug for #struct_name {
- fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) ->
::core::fmt::Result {
+ impl ::core::fmt::Debug for #struct_name {
+ fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) ->
::core::fmt::Result {
#tvm_ffi_crate::object::fmt_object_ref(self, f)
}
}
});
}
- // skip ObjectRef since it can create circular dependency with any.rs
+ // `ObjectRef` itself skips these: `any.rs` implements them for it.
if struct_name != "ObjectRef" {
expanded.extend(quote! {
- #tvm_ffi_crate::impl_try_from_any!(#struct_name);
- #tvm_ffi_crate::impl_arg_into_ref!(#struct_name);
- #tvm_ffi_crate::impl_into_arg_holder_default!(#struct_name);
+ #tvm_ffi_crate::impl_any_compatible_conversions!(#struct_name);
// Every object reference upcasts to the root `ObjectRef`.
- #tvm_ffi_crate::unsafe_impl_object_upcast!(#struct_name =>
#tvm_ffi_crate::object::ObjectRef);
+ #tvm_ffi_crate::unsafe_impl_object_ref_upcast!(#struct_name =>
#tvm_ffi_crate::object::ObjectRef);
+ });
+ }
+ // The root upcast is already above.
+ if let Some(parent) = options.parent.filter(|parent|
!is_object_ref_root(parent)) {
+ expanded.extend(quote! {
+ #tvm_ffi_crate::unsafe_impl_object_ref_upcast!(#struct_name =>
#parent);
});
}
expanded
}
+
+/// `T` of a field type spelled `ObjectArc<T>`.
+fn object_arc_argument(ty: &syn::Type) -> Option<TokenStream2> {
+ let syn::Type::Path(path) = ty else {
+ return None;
+ };
+ let last = path.path.segments.last()?;
+ if last.ident != "ObjectArc" {
+ return None;
+ }
+ match &last.arguments {
+ syn::PathArguments::AngleBracketed(args) => match args.args.first()? {
+ syn::GenericArgument::Type(object) if args.args.len() == 1 =>
Some(quote!(#object)),
+ _ => None,
+ },
+ _ => None,
+ }
+}
+
+/// Whether `ty` names the root `ObjectRef`.
+fn is_object_ref_root(ty: &syn::Type) -> bool {
+ match ty {
+ syn::Type::Path(path) => path
+ .path
+ .segments
+ .last()
+ .is_some_and(|s| s.ident == "ObjectRef"),
+ _ => false,
+ }
+}
+
+/// The options of `#[object_ref(...)]` on an `ObjectRef` derive.
+#[derive(Default)]
+struct ObjectRefOptions {
+ tvm_ffi_crate: Option<syn::Path>,
+ parent: Option<syn::Type>,
+ skip_deref: bool,
+ skip_debug: bool,
+ skip_any_compatible: bool,
+}
+
+/// One `#[object_ref(...)]` item: a flag, `parent = Type` or `crate = path`.
+enum ObjectRefOption {
+ Flag(syn::Ident),
+ Parent(Box<syn::Type>),
+ Crate(syn::Path),
+}
+
+impl syn::parse::Parse for ObjectRefOption {
+ fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
+ let name: syn::Ident = input.call(syn::Ident::parse_any)?;
+ if name == "parent" {
+ input.parse::<syn::Token![=]>()?;
+ return Ok(ObjectRefOption::Parent(Box::new(input.parse()?)));
+ }
+ if name == "crate" {
+ input.parse::<syn::Token![=]>()?;
+ return
Ok(ObjectRefOption::Crate(syn::Path::parse_mod_style(input)?));
+ }
+ Ok(ObjectRefOption::Flag(name))
+ }
+}
+
+fn object_ref_options(derive_input: &DeriveInput) -> ObjectRefOptions {
+ let mut options = ObjectRefOptions::default();
+ for attr in derive_input
+ .attrs
+ .iter()
+ .filter(|a| a.path.is_ident("object_ref"))
+ {
+ let items = attr
+ .parse_args_with(
+ syn::punctuated::Punctuated::<ObjectRefOption,
syn::Token![,]>::parse_terminated,
+ )
+ .unwrap_or_else(|error| abort!(error.span(), "{}", error));
+ for item in items {
+ match item {
+ ObjectRefOption::Parent(parent) => options.parent =
Some(*parent),
+ ObjectRefOption::Crate(path) => options.tvm_ffi_crate =
Some(path),
+ ObjectRefOption::Flag(flag) if flag == "skip_deref" =>
options.skip_deref = true,
+ ObjectRefOption::Flag(flag) if flag == "skip_debug" =>
options.skip_debug = true,
+ ObjectRefOption::Flag(flag) if flag == "skip_any_compatible"
=> {
+ options.skip_any_compatible = true
+ }
+ ObjectRefOption::Flag(flag) => abort!(
+ flag,
+ "unknown `object_ref` option; expected `parent = ...`,
`skip_deref`, \
+ `skip_debug`, `skip_any_compatible` or `crate = ...`"
+ ),
+ }
+ }
+ }
+ options
+}
diff --git a/rust/tvm-ffi/src/any.rs b/rust/tvm-ffi/src/any.rs
index 12f34939..31a2e061 100644
--- a/rust/tvm-ffi/src/any.rs
+++ b/rust/tvm-ffi/src/any.rs
@@ -503,7 +503,8 @@ impl<T: AnyCompatible> TryFrom<Any> for TryFromTemp<T> {
}
}
-crate::impl_try_from_any!(
+crate::impl_any_compatible_conversions!(
+ (),
bool,
i8,
i16,
@@ -517,15 +518,14 @@ crate::impl_try_from_any!(
usize,
f32,
f64,
- (),
- *mut core::ffi::c_void,
crate::string::String,
crate::string::Bytes,
crate::big_int::BigInt,
- crate::object::ObjectRef,
tvm_ffi_sys::dlpack::DLDataType,
tvm_ffi_sys::dlpack::DLDevice,
);
+// Only out of `Any`: a raw pointer and the root reference are not call
arguments.
+crate::impl_try_from_any!(*mut core::ffi::c_void, crate::object::ObjectRef);
crate::impl_try_from_any_for_parametric!(Option<T>);
diff --git a/rust/tvm-ffi/src/collections/shape.rs
b/rust/tvm-ffi/src/collections/shape.rs
index 55b9e715..18bea9b2 100644
--- a/rust/tvm-ffi/src/collections/shape.rs
+++ b/rust/tvm-ffi/src/collections/shape.rs
@@ -50,6 +50,7 @@ pub struct ShapeObj {
/// ```
#[repr(C)]
#[derive(ObjectRef, Clone)]
+#[object_ref(skip_deref)]
pub struct Shape {
data: ObjectArc<ShapeObj>,
}
diff --git a/rust/tvm-ffi/src/collections/tensor.rs
b/rust/tvm-ffi/src/collections/tensor.rs
index a2cfad7e..fe486b64 100644
--- a/rust/tvm-ffi/src/collections/tensor.rs
+++ b/rust/tvm-ffi/src/collections/tensor.rs
@@ -118,6 +118,7 @@ pub struct TensorObj {
/// ```
#[repr(C)]
#[derive(ObjectRef, Clone)]
+#[object_ref(skip_deref)]
pub struct Tensor {
data: ObjectArc<TensorObj>,
}
diff --git a/rust/tvm-ffi/src/derive.rs b/rust/tvm-ffi/src/derive.rs
index 629b9495..4d882b82 100644
--- a/rust/tvm-ffi/src/derive.rs
+++ b/rust/tvm-ffi/src/derive.rs
@@ -16,5 +16,73 @@
* specific language governing permissions and limitations
* under the License.
*/
-/// namespace to re-export derive macros
-pub use tvm_ffi_macros::{Object, ObjectRef};
+//! The derive macros of object types and object references.
+
+pub use tvm_ffi_macros::Object;
+
+/// Declare an object reference: the managed handle to an object type, as a
+/// C++ `ObjectRef` subclass is.
+///
+/// The struct holds one field, `ObjectArc<FooObj>`. The derive implements
+/// [`ObjectRefCore`](crate::ObjectRefCore), the default
+/// [`ObjectRefAnyCompatible`](crate::ObjectRefAnyCompatible) (conversions to
+/// and from [`Any`](crate::Any), in-place downcasts, typed function
+/// arguments), the upcast to [`ObjectRef`](crate::object::ObjectRef),
+/// `Deref<Target = FooObj>` for field reads, `Debug`, and a private
+/// `fn from_object(object: FooObj) -> Self` for the type's constructors.
+///
+/// # Options
+///
+/// `#[object_ref(...)]` takes:
+///
+/// * `parent = Ref` - The reference to the parent's object; `Self` converts
+/// into it by
[`unsafe_impl_object_ref_upcast!`](crate::unsafe_impl_object_ref_upcast),
+/// whose requirement it carries. A further ancestor takes its own
+/// `unsafe_impl_object_ref_upcast!`.
+/// * `skip_deref` - No `Deref`, for a reference whose object is internal.
+/// * `skip_debug` - `Debug` is written by hand.
+/// * `skip_any_compatible` - `ObjectRefAnyCompatible` is written by hand,
+/// for a typed reference with a stricter check or fallback conversions.
+/// * `crate = path` - Where the generated code finds tvm-ffi, as for
+/// [`rs_object`](macro@crate::rs_object).
+///
+/// # Examples
+///
+/// ```
+/// use tvm_ffi::derive::ObjectRef;
+/// use tvm_ffi::{rs_object, Object, ObjectArc};
+///
+/// #[rs_object("my_ext.Point")]
+/// pub struct PointObj {
+/// base: Object,
+/// pub x: i64,
+/// }
+///
+/// #[derive(Clone, ObjectRef)]
+/// pub struct Point {
+/// data: ObjectArc<PointObj>,
+/// }
+///
+/// #[rs_object("my_ext.Point3", final)]
+/// pub struct Point3Obj {
+/// base: PointObj,
+/// pub z: i64,
+/// }
+///
+/// #[derive(Clone, ObjectRef)]
+/// #[object_ref(parent = Point)]
+/// pub struct Point3 {
+/// data: ObjectArc<Point3Obj>,
+/// }
+///
+/// impl Point3 {
+/// pub fn new(x: i64, z: i64) -> Self {
+/// Self::from_object(Point3Obj::new(PointObj::new(Object::new(), x),
z))
+/// }
+/// }
+///
+/// let p = Point3::new(1, 2);
+/// assert_eq!(p.x + p.z, 3);
+/// let _parent: Point = p.into();
+/// ```
+pub use tvm_ffi_macros::ObjectRef;
diff --git a/rust/tvm-ffi/src/error.rs b/rust/tvm-ffi/src/error.rs
index 1fc4e7bd..f4777f06 100644
--- a/rust/tvm-ffi/src/error.rs
+++ b/rust/tvm-ffi/src/error.rs
@@ -111,7 +111,7 @@ pub struct ErrorObj {
/// An `Error` is `Send + Sync`, so a `Result` can cross threads.
#[repr(C)]
#[derive(Clone, ObjectRef)]
-#[custom_debug]
+#[object_ref(skip_deref, skip_debug)]
pub struct Error {
data: ObjectArc<ErrorObj>,
}
diff --git a/rust/tvm-ffi/src/extra/module.rs b/rust/tvm-ffi/src/extra/module.rs
index 59ef322c..a0820f94 100644
--- a/rust/tvm-ffi/src/extra/module.rs
+++ b/rust/tvm-ffi/src/extra/module.rs
@@ -40,6 +40,7 @@ pub struct ModuleObj {
/// ABI-stable owned Module for FFI operations.
#[repr(C)]
#[derive(ObjectRef, Clone)]
+#[object_ref(skip_deref)]
pub struct Module {
data: ObjectArc<ModuleObj>,
}
diff --git a/rust/tvm-ffi/src/extra/structural_mutate.rs
b/rust/tvm-ffi/src/extra/structural_mutate.rs
index 7a5c433a..eb348284 100644
--- a/rust/tvm-ffi/src/extra/structural_mutate.rs
+++ b/rust/tvm-ffi/src/extra/structural_mutate.rs
@@ -557,7 +557,8 @@ impl<T: object::ObjectRefCore> MutationView<'_, T> {
/// # use tvm_ffi::*;
/// # #[rs_object("my_ext.MyPair", final)]
/// # pub struct MyPairObj { base: Object, pub first: Array<i64>, pub
second: Array<i64> }
- /// # def_object_ref!(pub MyPair, ObjectRef, MyPairObj);
+ /// # #[derive(Clone, tvm_ffi::derive::ObjectRef)]
+ /// # pub struct MyPair { data: ObjectArc<MyPairObj> }
/// # struct Pass;
/// # #[dispatch(mutate, (MutationView<'_>, &mut StructuralMutatorView<'_,
Self>), Result<UnchangedOr<Any>>)]
/// # impl Pass {
@@ -567,7 +568,7 @@ impl<T: object::ObjectRefCore> MutationView<'_, T> {
/// # }
/// # }
/// # let pair = MyPairObj::new(Object::new(),
Array::from_vec(vec![1_i64]), Array::from_vec(vec![2_i64]));
- /// # structural_mutate(MyPair::from(pair), Pass)?;
+ /// # structural_mutate(MyPair::from_object(pair), Pass)?;
/// # Ok::<(), tvm_ffi::Error>(())
/// ```
///
diff --git a/rust/tvm-ffi/src/extra/structural_visit.rs
b/rust/tvm-ffi/src/extra/structural_visit.rs
index 38e96019..1a72734b 100644
--- a/rust/tvm-ffi/src/extra/structural_visit.rs
+++ b/rust/tvm-ffi/src/extra/structural_visit.rs
@@ -62,7 +62,7 @@ use super::cold_init_lazy_lock::ColdInitLazyLock;
pub use super::structural_walk::*;
use crate::any::{Any, AnyView};
-use crate::derive::Object;
+use crate::derive::{Object, ObjectRef};
use crate::error::{Error, Result, TYPE_ERROR};
use crate::function::Function;
use crate::object::{Object, ObjectArc};
@@ -238,19 +238,21 @@ const _: () = {
assert!(DefRegionKind::Simple as i32 ==
TVMFFIDefRegionKind::kTVMFFIDefRegionKindSimple as i32);
};
-crate::def_object_ref! {
- /// Interrupt state of a traversal, mirroring C++ `ffi.VisitInterrupt`.
- ///
- /// Entry points and visitor-layer calls return
- /// `Result<Option<VisitInterrupt>>`: `Ok(None)` means the (sub)graph was
- /// visited completely, `Ok(Some(..))` means a handler halted the traversal
- /// with this interrupt, and `Err` means it failed.
- ///
- /// This is an owning object reference, like [`Error`]. Payload reads
through
- /// `interrupt.value` borrow the shared [`VisitInterruptObj`]; use
[`Self::new`]
- /// instead of a payload-only struct literal. Moving or replacing the
payload
- /// through this reference is not supported; clone it to retain a separate
owner.
- pub VisitInterrupt, crate::object::ObjectRef, VisitInterruptObj
+/// Interrupt state of a traversal, mirroring C++ `ffi.VisitInterrupt`.
+///
+/// Entry points and visitor-layer calls return
+/// `Result<Option<VisitInterrupt>>`: `Ok(None)` means the (sub)graph was
+/// visited completely, `Ok(Some(..))` means a handler halted the traversal
+/// with this interrupt, and `Err` means it failed.
+///
+/// This is an owning object reference, like [`Error`]. Payload reads through
+/// `interrupt.value` borrow the shared [`VisitInterruptObj`]; use
[`Self::new`]
+/// instead of a payload-only struct literal. Moving or replacing the payload
+/// through this reference is not supported; clone it to retain a separate
owner.
+#[repr(C)]
+#[derive(Clone, ObjectRef)]
+pub struct VisitInterrupt {
+ data: ObjectArc<VisitInterruptObj>,
}
impl VisitInterrupt {
@@ -266,11 +268,10 @@ impl VisitInterrupt {
/// The interrupt.
#[inline(always)]
pub fn new<T: Into<Any>>(payload: T) -> Self {
- VisitInterruptObj {
+ Self::from_object(VisitInterruptObj {
base: Object::new(),
value: payload.into(),
- }
- .into()
+ })
}
}
diff --git a/rust/tvm-ffi/src/function.rs b/rust/tvm-ffi/src/function.rs
index 520fe300..c6a1d26f 100644
--- a/rust/tvm-ffi/src/function.rs
+++ b/rust/tvm-ffi/src/function.rs
@@ -68,6 +68,7 @@ pub struct FunctionObj {
/// tvm_ffi::Function::from_typed(move || -> tvm_ffi::Result<i64> { Ok(*count)
});
/// ```
#[derive(Clone, ObjectRef)]
+#[object_ref(skip_deref)]
pub struct Function {
data: ObjectArc<FunctionObj>,
}
diff --git a/rust/tvm-ffi/src/function_internal.rs
b/rust/tvm-ffi/src/function_internal.rs
index 4fce5b18..9fdbc192 100644
--- a/rust/tvm-ffi/src/function_internal.rs
+++ b/rust/tvm-ffi/src/function_internal.rs
@@ -164,28 +164,8 @@ pub trait IntoArgHolder {
fn into_arg_holder(self) -> Self::Target;
}
-crate::impl_into_arg_holder_default!(
- (),
- bool,
- i8,
- i16,
- i32,
- i64,
- isize,
- u8,
- u16,
- u32,
- u64,
- usize,
- f32,
- f64,
- String,
- Bytes,
- crate::big_int::BigInt,
- Any,
- crate::DLDataType,
- crate::DLDevice
-);
+// `Any` itself is an argument; every other value type is listed in `any.rs`.
+crate::impl_into_arg_holder_default!(Any);
// string will be converted to String for argument passing
impl IntoArgHolder for &str {
@@ -277,28 +257,8 @@ where
}
}
-crate::impl_arg_into_ref!(
- (),
- bool,
- i8,
- i16,
- i32,
- i64,
- isize,
- u8,
- u16,
- u32,
- u64,
- usize,
- f32,
- f64,
- String,
- Bytes,
- crate::big_int::BigInt,
- Any,
- crate::DLDataType,
- crate::DLDevice
-);
+// `Any` itself is an argument; every other value type is listed in `any.rs`.
+crate::impl_arg_into_ref!(Any);
// Generic holders require explicit implementations rather than scalar macro
entries.
impl<T: AnyCompatible> IntoArgHolder for Option<T> {
diff --git a/rust/tvm-ffi/src/lib.rs b/rust/tvm-ffi/src/lib.rs
index 7431787c..c3672c82 100644
--- a/rust/tvm-ffi/src/lib.rs
+++ b/rust/tvm-ffi/src/lib.rs
@@ -74,7 +74,7 @@ pub use crate::function::Function;
pub use crate::function_internal::AsPackedCallable;
pub use crate::object::{
ImmutableObject, IntoObjectRef, Object, ObjectArc, ObjectCore,
ObjectCoreWithExtraItems,
- ObjectIdentity, ObjectRefCore,
+ ObjectIdentity, ObjectRefAnyCompatible, ObjectRefCore,
};
pub use crate::optional::{Optional, OptionalCompatible};
pub use crate::reflection::{
@@ -205,8 +205,8 @@ pub use tvm_ffi_macros::match_any;
/// type with the reflection of every field, so that C++ structural equality
/// and hashing, `repr`, deep copy and Python see the fields. The first field,
/// `base`, is the parent's object, as the base class of a C++ object is:
-/// `base: Object` for a type under `ffi.Object`. Define the managed reference
-/// with [`def_object_ref!`].
+/// `base: Object` for a type under `ffi.Object`. Declare the managed reference
+/// with [`#[derive(ObjectRef)]`](derive::ObjectRef).
///
/// The macro adds to the struct:
///
@@ -246,8 +246,8 @@ pub use tvm_ffi_macros::match_any;
/// # Examples
///
/// ```
-/// use tvm_ffi::object::ObjectRef;
-/// use tvm_ffi::{def_object_ref, rs_object, Object};
+/// use tvm_ffi::derive::ObjectRef;
+/// use tvm_ffi::{rs_object, Object, ObjectArc};
///
/// #[rs_object("my_ext.MyPair", final)]
/// pub struct MyPairObj {
@@ -256,56 +256,18 @@ pub use tvm_ffi_macros::match_any;
/// #[reflect(seq_hash_ignore)]
/// pub second: tvm_ffi::String,
/// }
-/// def_object_ref!(pub MyPair, ObjectRef, MyPairObj);
///
-/// let pair = MyPair::from(MyPairObj::new(Object::new(), 1, "one"));
+/// #[derive(Clone, ObjectRef)]
+/// pub struct MyPair {
+/// data: ObjectArc<MyPairObj>,
+/// }
+///
+/// let pair = MyPair::from_object(MyPairObj::new(Object::new(), 1, "one"));
/// assert_eq!(pair.first, 1);
/// assert_eq!(pair.second.as_str(), "one");
/// ```
pub use tvm_ffi_macros::rs_object;
-/// Define the managed reference to an object type, as C++
-/// `TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE` does.
-///
-/// `def_object_ref!(TypeName, ParentType, ObjectName)` defines `TypeName`, a
-/// `derive(ObjectRef)` type that holds an `ObjectName`. It dereferences to
-/// `ObjectName`, and an `ObjectName` converts into it.
-///
-/// # Arguments
-///
-/// * `TypeName` - The reference type to define. Attributes and visibility
-/// before it go on the struct.
-/// * `ParentType` - The reference to the parent's object, `ObjectRef` for a
-/// type under `ffi.Object`. `TypeName` converts into it by
-/// `unsafe_impl_object_upcast!`, and `&TypeName` borrows as `&ParentType`
through
-/// `AsRef`; a conversion into a further ancestor takes its own
-/// `unsafe_impl_object_upcast!`.
-/// * `ObjectName` - The object type.
-/// * `crate = path` - Optional, after `ObjectName`: where the generated code
-/// finds tvm-ffi, as for [`rs_object`](macro@rs_object).
-///
-/// # Examples
-///
-/// ```
-/// use tvm_ffi::object::ObjectRef;
-/// 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);
-///
-/// let pair = MyPair::from(MyPairObj::new(Object::new(), 1, 2));
-/// assert_eq!(pair.first + pair.second, 3);
-/// let _borrowed: &ObjectRef = pair.as_ref();
-/// let _parent: ObjectRef = pair.into();
-/// ```
-pub use tvm_ffi_macros::def_object_ref;
-
pub use tvm_ffi_sys::TVMFFITypeIndex as TypeIndex;
pub use tvm_ffi_sys::{
DLDataType, DLDataTypeCode, DLDevice, DLDeviceType, TVMFFIAny,
TVMFFIObject, TVMFFIStreamHandle,
diff --git a/rust/tvm-ffi/src/macros.rs b/rust/tvm-ffi/src/macros.rs
index 47a43e81..c87d65f3 100644
--- a/rust/tvm-ffi/src/macros.rs
+++ b/rust/tvm-ffi/src/macros.rs
@@ -90,7 +90,7 @@ macro_rules! cached_global_func {
/// the generated safe conversions unsound. The Rust stub generator writes
these
/// from the C++ type hierarchy.
#[macro_export]
-macro_rules! unsafe_impl_object_upcast {
+macro_rules! unsafe_impl_object_ref_upcast {
($($source:ty => $target:ty),+ $(,)?) => {
$(
impl ::std::convert::From<$source> for $target {
@@ -185,9 +185,10 @@ macro_rules! unsafe_impl_object_upcast {
};
}
-/// Implement the in-place reference downcasts (`array[i]`,
`Any::downcast_ref`,
+/// Implement the in-place `downcast_ref` borrows (`array[i]`,
`Any::downcast_ref`,
/// [`ObjectRefCore::downcast_ref`](crate::ObjectRefCore::downcast_ref)) for
each
-/// listed reference type implemented by hand, as `#[derive(ObjectRef)]` does.
+/// listed object reference type.
[`#[derive(ObjectRef)]`](crate::derive::ObjectRef)
+/// emits it; a reference that implements `ObjectRefCore` by hand calls it.
///
/// # Safety
///
@@ -198,9 +199,9 @@ macro_rules! unsafe_impl_object_upcast {
/// # Examples
///
/// ```
-/// use tvm_ffi::object::{ObjectArc, ObjectRef};
-/// use tvm_ffi::{def_object_ref, rs_object, unsafe_impl_ref_downcast, Object,
ObjectRefCore};
-/// # use tvm_ffi::{tvm_ffi_sys::TVMFFIAny, AnyCompatible};
+/// use tvm_ffi::derive::ObjectRef;
+/// use tvm_ffi::{rs_object, unsafe_impl_object_ref_downcast_ref, Any};
+/// use tvm_ffi::{Object, ObjectArc, ObjectRefAnyCompatible, ObjectRefCore};
///
/// #[rs_object("my_ext.MyPair", final)]
/// pub struct MyPairObj {
@@ -208,47 +209,42 @@ macro_rules! unsafe_impl_object_upcast {
/// pub first: i64,
/// pub second: i64,
/// }
-/// def_object_ref!(pub MyPair, ObjectRef, MyPairObj);
+///
+/// #[derive(Clone, ObjectRef)]
+/// pub struct MyPair {
+/// data: ObjectArc<MyPairObj>,
+/// }
///
/// /// A `MyPair` whose `first` is positive.
-/// #[repr(transparent)]
/// #[derive(Clone)]
+/// #[repr(transparent)]
/// pub struct PositivePair(ObjectArc<MyPairObj>);
-/// # unsafe impl ObjectRefCore for PositivePair {
-/// # type ContainerType = MyPairObj;
-/// # fn data(this: &Self) -> &ObjectArc<MyPairObj> { &this.0 }
-/// # fn into_data(this: Self) -> ObjectArc<MyPairObj> { this.0 }
-/// # unsafe fn from_data(data: ObjectArc<MyPairObj>) -> Self { Self(data)
}
-/// # }
-/// # unsafe impl AnyCompatible for PositivePair {
-/// # fn type_str() -> String { "PositivePair".into() }
-/// # unsafe fn copy_to_any_view(src: &Self, data: &mut TVMFFIAny) {
-/// # MyPair::copy_to_any_view(std::mem::transmute::<&Self,
&MyPair>(src), data)
-/// # }
-/// # unsafe fn move_to_any(src: Self, data: &mut TVMFFIAny) {
-/// # MyPair::move_to_any(MyPair::from_data(src.0), data)
-/// # }
-/// # unsafe fn check_any_strict(data: &TVMFFIAny) -> bool {
-/// # MyPair::check_any_strict(data) &&
(*data.data_union.v_obj.cast::<MyPairObj>()).first > 0
-/// # }
-/// # unsafe fn copy_from_any_view_after_check(data: &TVMFFIAny) -> Self {
-/// #
Self(MyPair::into_data(MyPair::copy_from_any_view_after_check(data)))
-/// # }
-/// # unsafe fn move_from_any_after_check(data: &mut TVMFFIAny) -> Self {
-/// # Self(MyPair::into_data(MyPair::move_from_any_after_check(data)))
-/// # }
-/// # unsafe fn try_cast_from_any_view(data: &TVMFFIAny) -> Result<Self,
()> {
-/// # if Self::check_any_strict(data) {
Ok(Self::copy_from_any_view_after_check(data)) } else { Err(()) }
-/// # }
-/// # }
-/// // (`ObjectRefCore` and `AnyCompatible` for `PositivePair` are written by
hand.)
-/// unsafe_impl_ref_downcast!(PositivePair);
-///
-/// let pair: ObjectRef = MyPair::from(MyPairObj::new(Object::new(), 1,
2)).into();
+///
+/// unsafe impl ObjectRefCore for PositivePair {
+/// type ContainerType = MyPairObj;
+/// fn data(this: &Self) -> &ObjectArc<MyPairObj> {
+/// &this.0
+/// }
+/// fn into_data(this: Self) -> ObjectArc<MyPairObj> {
+/// this.0
+/// }
+/// unsafe fn from_data(data: ObjectArc<MyPairObj>) -> Self {
+/// Self(data)
+/// }
+/// }
+///
+/// unsafe impl ObjectRefAnyCompatible for PositivePair {
+/// fn extra_check(pair: &MyPairObj) -> bool {
+/// pair.first > 0
+/// }
+/// }
+/// unsafe_impl_object_ref_downcast_ref!(PositivePair);
+///
+/// let pair = Any::from(MyPair::from_object(MyPairObj::new(Object::new(), 1,
2)));
/// assert!(pair.downcast_ref::<PositivePair>().is_some());
/// ```
#[macro_export]
-macro_rules! unsafe_impl_ref_downcast {
+macro_rules! unsafe_impl_object_ref_downcast_ref {
($($name:ty),+ $(,)?) => {
$(
// SAFETY: the macro declaration promises that `$name` is one
object
@@ -452,15 +448,26 @@ macro_rules! attach_context {
// Macros for any definitions
// ----------------------------------------------------------------------------
-/// Implement `TryFrom<AnyView>`, `TryFrom<Any>` and `TryFrom<&Any>` for each
listed
-/// [`AnyCompatible`](crate::AnyCompatible) type, so it converts out of FFI
values
-/// by its `AnyCompatible` checks.
+/// Implement the conversions between each listed
[`AnyCompatible`](crate::AnyCompatible)
+/// type and FFI values: `TryFrom<AnyView>`, `TryFrom<Any>` and
`TryFrom<&Any>`,
+/// and use as an argument of
[`Function::call_tuple`](crate::Function::call_tuple)
+/// and of functions from [`into_typed_fn!`](crate::into_typed_fn).
///
-/// `#[derive(ObjectRef)]` calls it. A reference type implemented by hand, such
-/// as a typed view over another reference, calls it with
-/// [`impl_arg_into_ref!`](crate::impl_arg_into_ref),
-/// [`impl_into_arg_holder_default!`](crate::impl_into_arg_holder_default) and
-/// [`unsafe_impl_ref_downcast!`](crate::unsafe_impl_ref_downcast).
+/// `#[derive(ObjectRef)]` calls it; a type that implements `AnyCompatible` by
hand
+/// calls it after that impl. See also
+///
[`unsafe_impl_object_ref_downcast_ref!`](crate::unsafe_impl_object_ref_downcast_ref)
+/// for an object reference implemented by hand.
+#[macro_export]
+macro_rules! impl_any_compatible_conversions {
+ ($($t:ty),* $(,)?) => {
+ $crate::impl_try_from_any!($($t),*);
+ $crate::impl_arg_into_ref!($($t),*);
+ $crate::impl_into_arg_holder_default!($($t),*);
+ };
+}
+
+/// `TryFrom` from FFI values; part of
[`impl_any_compatible_conversions!`](crate::impl_any_compatible_conversions).
+#[doc(hidden)]
#[macro_export]
macro_rules! impl_try_from_any {
($($t:ty),* $(,)?) => {
@@ -537,13 +544,8 @@ macro_rules! impl_try_from_any_for_parametric {
};
}
-/// Let each listed [`AnyCompatible`](crate::AnyCompatible) type, owned or
-/// borrowed, be an argument of a function from
-/// [`into_typed_fn!`](crate::into_typed_fn).
-///
-/// A reference type implemented by hand calls it with
-/// [`impl_try_from_any!`](crate::impl_try_from_any) and
-/// [`impl_arg_into_ref!`](crate::impl_arg_into_ref).
+/// Typed-function arguments; part of
[`impl_any_compatible_conversions!`](crate::impl_any_compatible_conversions).
+#[doc(hidden)]
#[macro_export]
macro_rules! impl_into_arg_holder_default {
($($t:ty),*) => {
@@ -564,13 +566,8 @@ macro_rules! impl_into_arg_holder_default {
};
}
-/// Let each listed [`AnyCompatible`](crate::AnyCompatible) type, owned or
-/// borrowed, be an element of the arguments of
-/// [`Function::call_tuple`](crate::Function::call_tuple).
-///
-/// A reference type implemented by hand calls it with
-/// [`impl_try_from_any!`](crate::impl_try_from_any) and
-/// [`impl_into_arg_holder_default!`](crate::impl_into_arg_holder_default).
+/// `call_tuple` arguments; part of
[`impl_any_compatible_conversions!`](crate::impl_any_compatible_conversions).
+#[doc(hidden)]
#[macro_export]
macro_rules! impl_arg_into_ref {
($($t:ty),*) => {
diff --git a/rust/tvm-ffi/src/object.rs b/rust/tvm-ffi/src/object.rs
index ccfc8cc6..51ab2919 100644
--- a/rust/tvm-ffi/src/object.rs
+++ b/rust/tvm-ffi/src/object.rs
@@ -20,6 +20,7 @@ use std::ops::Deref;
use std::ptr::NonNull;
use std::sync::atomic::AtomicU64;
+use crate::any::AnyView;
use crate::derive::ObjectRef;
use crate::type_traits::AnyCompatible;
pub use tvm_ffi_sys::TVMFFITypeIndex as TypeIndex;
@@ -253,11 +254,189 @@ pub unsafe trait ObjectRefCore: Sized + Clone +
AnyCompatible {
}
}
+/// The [`AnyCompatible`] implementation of an object reference, with every
+/// hook defaulted, as C++ `TypeTraits<T>` derives from
+/// `ObjectRefTypeTraitsBase<T>`: every implementer is `AnyCompatible`.
+///
+/// `#[derive(ObjectRef)]` implements it with the defaults. A typed reference
+/// that accepts only some objects of its type, or converts other values,
+/// implements it by hand and overrides only what differs, as C++ `IntExpr`
+/// overrides `CheckAnyStrict` and `ConvertFallbackValue`. A type with another
+/// representation, such as [`Array`](crate::Array), implements
+/// `AnyCompatible` instead.
+///
+/// # Safety
+///
+/// `Self` is exactly one [`ObjectArc<Self::ContainerType>`](ObjectArc). The
+/// hooks are safe and cannot break the [`AnyCompatible`] contract.
+///
+/// # Examples
+///
+/// ```
+/// use tvm_ffi::derive::ObjectRef;
+/// use tvm_ffi::{rs_object, Any, AnyView, Object, ObjectArc,
ObjectRefAnyCompatible};
+///
+/// #[rs_object("my_ext.MyPair", final)]
+/// pub struct MyPairObj {
+/// base: Object,
+/// pub first: i64,
+/// pub second: i64,
+/// }
+///
+/// #[derive(Clone, ObjectRef)]
+/// pub struct MyPair {
+/// data: ObjectArc<MyPairObj>,
+/// }
+///
+/// /// A `MyPair` whose `first` is positive.
+/// #[derive(Clone, ObjectRef)]
+/// #[object_ref(skip_any_compatible)]
+/// pub struct PositivePair {
+/// data: ObjectArc<MyPairObj>,
+/// }
+///
+/// unsafe impl ObjectRefAnyCompatible for PositivePair {
+/// fn extra_check(pair: &MyPairObj) -> bool {
+/// pair.first > 0
+/// }
+///
+/// // A positive integer `n` converts to the pair `(n, n)`.
+/// fn convert_fallback(value: AnyView<'_>) -> Option<Self> {
+/// let n = i64::try_from(value).ok().filter(|n| *n > 0)?;
+/// Some(Self::from_object(MyPairObj::new(Object::new(), n, n)))
+/// }
+/// }
+///
+/// let negative = Any::from(MyPair::from_object(MyPairObj::new(Object::new(),
-1, 2)));
+/// assert!(negative.downcast_ref::<PositivePair>().is_none());
+/// assert!(PositivePair::try_from(negative).is_err());
+/// assert_eq!(PositivePair::try_from(Any::from(3_i64))?.second, 3);
+/// # Ok::<(), tvm_ffi::Error>(())
+/// ```
+pub unsafe trait ObjectRefAnyCompatible: ObjectRefCore {
+ /// Whether no hook is overridden, so that `match_any!` may route a final
+ /// object type by its type index alone; only `#[derive(ObjectRef)]` sets
+ /// it. See [`AnyCompatible::MATCH_ANY_EXACT`].
+ #[doc(hidden)]
+ const MATCH_ANY_EXACT: bool = false;
+
+ /// Whether `obj`, an instance of `ContainerType`, is a value of `Self`.
+ /// Every strict and converting cast checks it, after the object type.
+ #[inline]
+ fn extra_check(obj: &Self::ContainerType) -> bool {
+ let _ = obj;
+ true
+ }
+
+ /// Convert a value that is not a `Self` object, as C++
+ /// `ConvertFallbackValue` does. Only converting casts (`TryFrom<Any>`,
+ /// typed function arguments) call it; `downcast_ref`, `downcast` and
+ /// `match_any!` never do.
+ #[inline]
+ fn convert_fallback(value: AnyView<'_>) -> Option<Self> {
+ let _ = value;
+ None
+ }
+
+ /// The type name in conversion errors and type schemas; by default the
+ /// object's type key. See [`AnyCompatible::type_str`].
+ fn custom_type_str() -> String {
+ Self::ContainerType::TYPE_KEY.into()
+ }
+
+ /// The name of the mismatched `value` in conversion errors; by default its
+ /// runtime type key. See [`AnyCompatible::get_mismatch_type_info`].
+ fn custom_mismatch_type_info(value: AnyView<'_>) -> String {
+ crate::type_traits::runtime_type_key(value.type_index())
+ }
+}
+
+/// Write the object cell of the live object `ptr`.
+#[inline(always)]
+unsafe fn write_object_cell<T: ObjectCore>(ptr: *const T, data: &mut
TVMFFIAny) {
+ let object = ptr as *mut TVMFFIObject;
+ data.type_index = (*object).type_index;
+ // SAFETY: a live object's header holds its registered object-range type
index.
+ std::hint::assert_unchecked(data.type_index >=
TypeIndex::kTVMFFIStaticObjectBegin as i32);
+ data.small_str_len = 0;
+ data.data_union.v_obj = object;
+}
+
+// The object cell encoding, with the strict check `is_instance_of` then
+// `extra_check`, and `convert_fallback` after it in converting casts.
+unsafe impl<T: ObjectRefAnyCompatible> AnyCompatible for T {
+ // An overridden hook makes the type inexact; a non-final object type
always is.
+ const MATCH_ANY_EXACT: bool = <T as
ObjectRefAnyCompatible>::MATCH_ANY_EXACT
+ && <T::ContainerType as ObjectCore>::TYPE_FINAL;
+ const MATCH_ANY_STRICT_EXACT: bool = <T as AnyCompatible>::MATCH_ANY_EXACT;
+
+ #[inline]
+ fn match_any_exact_type_index() -> i32 {
+ T::ContainerType::type_index()
+ }
+
+ #[inline]
+ unsafe fn is_undefined(field: *const Self) -> bool {
+ // `Self` is one `ObjectArc`, so its null form is the undefined field.
+ ObjectArc::is_null(field.cast::<ObjectArc<T::ContainerType>>())
+ }
+
+ #[inline(always)]
+ unsafe fn copy_to_any_view(src: &Self, data: &mut TVMFFIAny) {
+ write_object_cell(ObjectArc::as_ptr(T::data(src)), data);
+ }
+
+ #[inline(always)]
+ unsafe fn move_to_any(src: Self, data: &mut TVMFFIAny) {
+ write_object_cell(ObjectArc::into_raw(T::into_data(src)), data);
+ }
+
+ #[inline(always)]
+ unsafe fn check_any_strict(data: &TVMFFIAny) -> bool {
+ // An instance of `ContainerType` points to one, so `extra_check` may
read it.
+ is_instance_of::<T::ContainerType>(data.type_index)
+ &&
T::extra_check(&*data.data_union.v_obj.cast::<T::ContainerType>())
+ }
+
+ #[inline]
+ unsafe fn copy_from_any_view_after_check(data: &TVMFFIAny) -> Self {
+ // The view does not own the object, so the new reference takes one.
+ unsafe_::inc_ref(data.data_union.v_obj);
+ T::from_data(ObjectArc::from_raw(data.data_union.v_obj.cast()))
+ }
+
+ #[inline(always)]
+ unsafe fn move_from_any_after_check(data: &mut TVMFFIAny) -> Self {
+ T::from_data(ObjectArc::from_raw(data.data_union.v_obj.cast()))
+ }
+
+ #[inline]
+ unsafe fn try_cast_from_any_view(data: &TVMFFIAny) -> Result<Self, ()> {
+ if Self::check_any_strict(data) {
+ return Ok(Self::copy_from_any_view_after_check(data));
+ }
+ // A moved-from rvalue slot is consumed only by an exact object match.
+ if data.type_index == TypeIndex::kTVMFFIObjectRValueRef as i32 {
+ return Err(());
+ }
+ T::convert_fallback(AnyView::from_raw_ffi_any(*data)).ok_or(())
+ }
+
+ fn get_mismatch_type_info(data: &TVMFFIAny) -> String {
+ // SAFETY: the caller's cell is valid for the borrow.
+ T::custom_mismatch_type_info(unsafe { AnyView::from_raw_ffi_any(*data)
})
+ }
+
+ fn type_str() -> String {
+ T::custom_type_str()
+ }
+}
+
/// 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`.
[`unsafe_impl_object_upcast!`](crate::unsafe_impl_object_upcast)
+/// `Vec` but borrows from a `&Vec`.
[`unsafe_impl_object_ref_upcast!`](crate::unsafe_impl_object_ref_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
@@ -268,8 +447,9 @@ pub unsafe trait ObjectRefCore: Sized + Clone +
AnyCompatible {
///
/// ```
/// use std::borrow::Borrow;
+/// use tvm_ffi::derive::ObjectRef;
/// use tvm_ffi::object::{IntoObjectRef, ObjectRef, ObjectRefCore};
-/// use tvm_ffi::{def_object_ref, rs_object, Object};
+/// use tvm_ffi::{rs_object, Object, ObjectArc};
///
/// #[rs_object("my_ext.MyPair", final)]
/// pub struct MyPairObj {
@@ -277,13 +457,17 @@ pub unsafe trait ObjectRefCore: Sized + Clone +
AnyCompatible {
/// pub first: i64,
/// pub second: i64,
/// }
-/// def_object_ref!(pub MyPair, ObjectRef, MyPairObj);
+///
+/// #[derive(Clone, ObjectRef)]
+/// pub struct MyPair {
+/// data: ObjectArc<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 pair = MyPair::from_object(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`
@@ -503,6 +687,7 @@ pub(crate) fn type_key_of_index(type_index: i32) -> String {
/// through reference counting.
#[repr(C)]
#[derive(ObjectRef, Clone)]
+#[object_ref(skip_deref)]
pub struct ObjectRef {
data: ObjectArc<Object>,
}
diff --git a/rust/tvm-ffi/src/reflection.rs b/rust/tvm-ffi/src/reflection.rs
index 0ce9d30c..85f25ff5 100644
--- a/rust/tvm-ffi/src/reflection.rs
+++ b/rust/tvm-ffi/src/reflection.rs
@@ -196,6 +196,7 @@ pub struct AccessStepObj {
#[repr(C)]
#[derive(ObjectRef, Clone)]
+#[object_ref(skip_deref)]
pub struct AccessStep {
data: ObjectArc<AccessStepObj>,
}
@@ -211,6 +212,7 @@ pub struct AccessPathObj {
#[repr(C)]
#[derive(ObjectRef, Clone)]
+#[object_ref(skip_deref)]
pub struct AccessPath {
data: ObjectArc<AccessPathObj>,
}
diff --git a/rust/tvm-ffi/src/type_traits.rs b/rust/tvm-ffi/src/type_traits.rs
index 268a82aa..3ed6349e 100644
--- a/rust/tvm-ffi/src/type_traits.rs
+++ b/rust/tvm-ffi/src/type_traits.rs
@@ -129,11 +129,7 @@ pub unsafe trait AnyCompatible: Sized {
unsafe fn try_cast_from_any_view(data: &TVMFFIAny) -> Result<Self, ()>;
/// Get the type key of a type when TryCastFromAnyView fails.
fn get_mismatch_type_info(data: &TVMFFIAny) -> String {
- unsafe {
- let info = TVMFFIGetTypeInfo(data.type_index);
- assert!(!info.is_null(), "TVMFFIGetTypeInfo returned null");
- (*info).type_key.as_str().to_string()
- }
+ runtime_type_key(data.type_index)
}
/// the type string of the type
fn type_str() -> String;
@@ -161,6 +157,15 @@ pub unsafe trait AnyCompatible: Sized {
}
}
+/// The type key of the runtime type index `type_index`.
+pub(crate) fn runtime_type_key(type_index: i32) -> String {
+ unsafe {
+ let info = TVMFFIGetTypeInfo(type_index);
+ assert!(!info.is_null(), "TVMFFIGetTypeInfo returned null");
+ (*info).type_key.as_str().to_string()
+ }
+}
+
/// Writes a type schema, `{"type":"<origin>"}` or, with type arguments,
/// `{"type":"<origin>","args":[...]}`, as C++ `TypeTraits<T>::TypeSchema`
does.
pub(crate) fn type_schema(origin: &str, args: &[String]) -> String {
@@ -244,7 +249,7 @@ impl ContainerElement for Any {}
/// cell, as [`Array`](crate::Array) indexing does.
///
/// A reference type implemented by hand gets it from
-/// [`unsafe_impl_ref_downcast!`](crate::unsafe_impl_ref_downcast).
+///
[`unsafe_impl_object_ref_downcast_ref!`](crate::unsafe_impl_object_ref_downcast_ref).
///
/// # Safety
///
diff --git a/rust/tvm-ffi/tests/test_cast.rs b/rust/tvm-ffi/tests/test_cast.rs
index 6e1e1386..f4159806 100644
--- a/rust/tvm-ffi/tests/test_cast.rs
+++ b/rust/tvm-ffi/tests/test_cast.rs
@@ -66,7 +66,7 @@ struct TestDerived {
data: ObjectArc<TestDerivedObj>,
}
-tvm_ffi::unsafe_impl_object_upcast!(TestDerived => TestBase);
+tvm_ffi::unsafe_impl_object_ref_upcast!(TestDerived => TestBase);
// unwrap_err() requires the Ok type to implement Debug, which ObjectRef types
do not
fn expect_err<T>(res: Result<T>) -> Error {
diff --git a/rust/tvm-ffi/tests/test_object_ref_any.rs
b/rust/tvm-ffi/tests/test_object_ref_any.rs
new file mode 100644
index 00000000..c3c18582
--- /dev/null
+++ b/rust/tvm-ffi/tests/test_object_ref_any.rs
@@ -0,0 +1,159 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// The three ways a downstream crate makes an object reference `AnyCompatible`.
+
+use tvm_ffi::derive::ObjectRef;
+use tvm_ffi::object::ObjectArc;
+use tvm_ffi::tvm_ffi_sys::TVMFFIAny;
+use tvm_ffi::{impl_any_compatible_conversions, match_any, rs_object};
+use tvm_ffi::{Any, AnyCompatible, AnyView, Object, ObjectRefCore};
+
+// A derived reference.
+#[rs_object("testing.object_ref_any.Pair", final)]
+pub struct PairObj {
+ base: Object,
+ pub first: i64,
+ pub second: i64,
+}
+#[derive(Clone, ObjectRef)]
+pub struct Pair {
+ data: ObjectArc<PairObj>,
+}
+
+fn pair(first: i64, second: i64) -> Pair {
+ Pair::from_object(PairObj::new(Object::new(), first, second))
+}
+
+// A typed reference: a `Pair` whose `first` is positive; a positive integer
+// `n` converts to `(n, n)`.
+#[derive(Clone, ObjectRef)]
+#[object_ref(skip_any_compatible)]
+pub struct PositivePair {
+ data: ObjectArc<PairObj>,
+}
+
+unsafe impl tvm_ffi::ObjectRefAnyCompatible for PositivePair {
+ fn extra_check(pair: &PairObj) -> bool {
+ pair.first > 0
+ }
+
+ fn convert_fallback(value: AnyView<'_>) -> Option<Self> {
+ let n = i64::try_from(value).ok().filter(|n| *n > 0)?;
+ Some(Self::from_object(PairObj::new(Object::new(), n, n)))
+ }
+
+ fn custom_type_str() -> String {
+ "PositivePair".into()
+ }
+
+ fn custom_mismatch_type_info(value: AnyView<'_>) -> String {
+ match value.downcast_ref::<Pair>() {
+ Some(pair) => format!("Pair(first={})", pair.first),
+ None => "non-pair".into(),
+ }
+ }
+}
+
+// A reference with a hand-written `AnyCompatible`, as the containers have.
+#[derive(Clone)]
+#[repr(transparent)]
+pub struct RawPair(Pair);
+
+unsafe impl ObjectRefCore for RawPair {
+ type ContainerType = PairObj;
+ fn data(this: &Self) -> &ObjectArc<PairObj> {
+ Pair::data(&this.0)
+ }
+ fn into_data(this: Self) -> ObjectArc<PairObj> {
+ Pair::into_data(this.0)
+ }
+ unsafe fn from_data(data: ObjectArc<PairObj>) -> Self {
+ Self(Pair::from_data(data))
+ }
+}
+
+unsafe impl AnyCompatible for RawPair {
+ fn type_str() -> String {
+ "RawPair".into()
+ }
+ unsafe fn copy_to_any_view(src: &Self, data: &mut TVMFFIAny) {
+ Pair::copy_to_any_view(&src.0, data)
+ }
+ unsafe fn move_to_any(src: Self, data: &mut TVMFFIAny) {
+ Pair::move_to_any(src.0, data)
+ }
+ unsafe fn check_any_strict(data: &TVMFFIAny) -> bool {
+ Pair::check_any_strict(data)
+ }
+ unsafe fn copy_from_any_view_after_check(data: &TVMFFIAny) -> Self {
+ Self(Pair::copy_from_any_view_after_check(data))
+ }
+ unsafe fn move_from_any_after_check(data: &mut TVMFFIAny) -> Self {
+ Self(Pair::move_from_any_after_check(data))
+ }
+ unsafe fn try_cast_from_any_view(data: &TVMFFIAny) -> Result<Self, ()> {
+ Pair::try_cast_from_any_view(data).map(Self)
+ }
+}
+
+impl_any_compatible_conversions!(RawPair);
+
+// The trait's doc example covers the strict check and the fallback; here, what
+// it does not: borrows never fall back, and the mismatch message is custom.
+#[test]
+fn typed_reference_borrow_never_falls_back_and_names_the_mismatch() {
+ assert!(Any::from(3_i64).downcast_ref::<PositivePair>().is_none());
+ let error = PositivePair::try_from(Any::from(pair(-1, 2)))
+ .err()
+ .unwrap();
+ assert_eq!(
+ error.message(),
+ "Cannot convert from type `Pair(first=-1)` to `PositivePair`"
+ );
+}
+
+// With 20 or more arms, `match_any!` routes final object types by type index,
+// which must not skip the typed arm's check.
+macro_rules! classify {
+ ($value:expr, $($pair:ident)*) => {
+ match_any! {
+ $value {
+ PositivePair(_) => "positive",
+ $(Pair($pair) => "pair",)*
+ _ => "other",
+ }
+ }
+ };
+}
+
+#[test]
+fn match_any_checks_a_typed_arm_of_a_final_type() {
+ let classify =
+ |value: Any| classify!(value, _a _b _c _d _e _f _g _h _i _j _k _l _m
_n _o _p _q _r _s);
+ assert_eq!(classify(Any::from(pair(1, 2))), "positive");
+ assert_eq!(classify(Any::from(pair(-1, 2))), "pair");
+ assert_eq!(classify(Any::from(3_i64)), "other");
+}
+
+#[test]
+fn hand_written_any_compatible_round_trips() {
+ let raw = RawPair::try_from(Any::from(pair(1, 2))).unwrap();
+ assert_eq!(Any::from(raw).downcast_ref::<Pair>().unwrap().second, 2);
+}
diff --git a/rust/tvm-ffi/tests/test_object_reflection.rs
b/rust/tvm-ffi/tests/test_object_reflection.rs
index 2f2f63ee..cd4cc99c 100644
--- a/rust/tvm-ffi/tests/test_object_reflection.rs
+++ b/rust/tvm-ffi/tests/test_object_reflection.rs
@@ -100,7 +100,7 @@ struct Point3 {
data: ObjectArc<Point3Obj>,
}
-tvm_ffi::unsafe_impl_object_upcast!(Point3 => Point);
+tvm_ffi::unsafe_impl_object_ref_upcast!(Point3 => Point);
fn point_obj(x: i64, y: f64, label: &str) -> PointObj {
PointObj {
diff --git a/rust/tvm-ffi/tests/test_object_registration.rs
b/rust/tvm-ffi/tests/test_object_registration.rs
index 21684172..03403289 100644
--- a/rust/tvm-ffi/tests/test_object_registration.rs
+++ b/rust/tvm-ffi/tests/test_object_registration.rs
@@ -58,7 +58,7 @@ struct RegisteredLeaf {
data: ObjectArc<RegisteredLeafObj>,
}
-tvm_ffi::unsafe_impl_object_upcast!(RegisteredLeaf => RegisteredBase);
+tvm_ffi::unsafe_impl_object_ref_upcast!(RegisteredLeaf => RegisteredBase);
fn registered_index(type_key: &str) -> i32 {
let key = unsafe { TVMFFIByteArray::from_str(type_key) };
diff --git a/rust/tvm-ffi/tests/test_rs_object.rs
b/rust/tvm-ffi/tests/test_rs_object.rs
index a968e3c3..e3dc68b8 100644
--- a/rust/tvm-ffi/tests/test_rs_object.rs
+++ b/rust/tvm-ffi/tests/test_rs_object.rs
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-use tvm_ffi::derive::Object;
+use tvm_ffi::derive::{Object, ObjectRef};
use tvm_ffi::object::{ObjectArc, ObjectRef, ObjectRefCore};
use tvm_ffi::reflection::TypeAttrColumn;
use tvm_ffi::reflection::{drop_if_defined, FieldFlags, ObjectDef};
@@ -40,7 +40,10 @@ pub struct RsPointObj {
pub label: tvm_ffi::String,
}
-def_object_ref!(pub RsPoint, ObjectRef, RsPointObj);
+#[derive(Clone, ObjectRef)]
+pub struct RsPoint {
+ data: ObjectArc<RsPointObj>,
+}
impl RsPointObj {
fn reflect_methods(def: &mut ObjectDef<Self>) {
@@ -63,12 +66,12 @@ pub struct RsPoint3Obj {
tag: i64,
}
-def_object_ref!(
- /// The reference with its own docs.
- pub RsPoint3,
- RsPoint,
- RsPoint3Obj,
-);
+/// The reference with its own docs.
+#[derive(Clone, ObjectRef)]
+#[object_ref(parent = RsPoint)]
+pub struct RsPoint3 {
+ data: ObjectArc<RsPoint3Obj>,
+}
/// A subtype without fields, which still registers its reflection.
#[rs_object("testing.rust.RsMarker", final)]
@@ -76,7 +79,11 @@ pub struct RsMarkerObj {
base: RsPointObj,
}
-def_object_ref!(pub RsMarker, RsPoint, RsMarkerObj);
+#[derive(Clone, ObjectRef)]
+#[object_ref(parent = RsPoint)]
+pub struct RsMarker {
+ data: ObjectArc<RsMarkerObj>,
+}
/// Scalar fields, whose literal arguments to `new` infer as the field types.
#[rs_object("testing.rust.RsScalars", final)]
@@ -88,7 +95,10 @@ pub struct RsScalarsObj {
pub ratio: f32,
}
-def_object_ref!(pub RsScalars, ObjectRef, RsScalarsObj);
+#[derive(Clone, tvm_ffi::derive::ObjectRef)]
+pub struct RsScalars {
+ data: tvm_ffi::object::ObjectArc<RsScalarsObj>,
+}
// `testing.TestCxxClassBase` from libtvm_ffi_testing, bound as tvm-ffi-stubgen
// binds a complete C++ type that can have children: with a public `new`,
@@ -130,8 +140,11 @@ unsafe impl UnsafeInit for TestCxxClassBaseObj {
}
}
-// `def_object_ref!` takes any object type, not only an `rs_object` one.
-def_object_ref!(pub TestCxxClassBase, ObjectRef, TestCxxClassBaseObj);
+// A reference to any object type, not only an `rs_object` one.
+#[derive(Clone, ObjectRef)]
+pub struct TestCxxClassBase {
+ data: ObjectArc<TestCxxClassBaseObj>,
+}
/// A subtype of a C++ type.
#[rs_object("testing.rust.RsCxxChild", final, s_eq_hash_kind = "TreeNode")]
@@ -140,7 +153,11 @@ pub struct RsCxxChildObj {
pub name: tvm_ffi::String,
}
-def_object_ref!(pub RsCxxChild, TestCxxClassBase, RsCxxChildObj);
+#[derive(Clone, ObjectRef)]
+#[object_ref(parent = TestCxxClassBase)]
+pub struct RsCxxChild {
+ data: ObjectArc<RsCxxChildObj>,
+}
/// A type that defines its own `__ffi_init__`, over which C++ generates none.
#[rs_object("testing.rust.RsOwnInit", final, reflection =
RsOwnInitObj::reflect_methods)]
@@ -149,13 +166,21 @@ pub struct RsOwnInitObj {
pub v: i64,
}
-def_object_ref!(pub RsOwnInit, ObjectRef, RsOwnInitObj);
+#[derive(Clone, ObjectRef)]
+pub struct RsOwnInit {
+ data: ObjectArc<RsOwnInitObj>,
+}
impl RsOwnInitObj {
fn reflect_methods(def: &mut ObjectDef<Self>) {
def.def_static(
"__ffi_init__",
- |v: i64| -> Result<RsOwnInit> {
Ok(RsOwnInitObj::new(Object::new(), 2 * v).into()) },
+ |v: i64| -> Result<RsOwnInit> {
+ Ok(RsOwnInit::from_object(RsOwnInitObj::new(
+ Object::new(),
+ 2 * v,
+ )))
+ },
"Twice `v`.",
);
}
@@ -172,7 +197,10 @@ pub struct RsInitOffObj {
pub point: Option<RsPoint>,
}
-def_object_ref!(pub RsInitOff, ObjectRef, RsInitOffObj);
+#[derive(Clone, ObjectRef)]
+pub struct RsInitOff {
+ data: ObjectArc<RsInitOffObj>,
+}
/// An `init_off` reference, which all-zero bytes leave undefined: its
registration fails.
#[rs_object("testing.rust.RsInitOffReference", final)]
@@ -194,27 +222,40 @@ mod reexported {
pub v: i64,
}
- ::tvm_ffi::def_object_ref!(
- pub Reexported,
- ::tvm_ffi::object::ObjectRef,
- ReexportedObj,
- crate = ::tvm_ffi
- );
+ #[derive(Clone, ::tvm_ffi::derive::ObjectRef)]
+ #[object_ref(crate = ::tvm_ffi)]
+ pub struct Reexported {
+ data: ::tvm_ffi::object::ObjectArc<ReexportedObj>,
+ }
+
+ impl Reexported {
+ pub fn new(v: i64) -> Self {
+ Self::from_object(ReexportedObj::new(::tvm_ffi::Object::new(), v))
+ }
+ }
}
fn point(x: i64, label: &str) -> RsPoint {
- RsPointObj::new(Object::new(), x, label).into()
+ RsPoint::from_object(RsPointObj::new(Object::new(), x, label))
}
fn point3(x: i64, z: i64) -> RsPoint3 {
let tags = Array::from_vec(vec!["a".into(), "b".into()]);
- RsPoint3Obj::new(RsPointObj::new(Object::new(), x, "p3"), z, tags,
7).into()
+ RsPoint3::from_object(RsPoint3Obj::new(
+ RsPointObj::new(Object::new(), x, "p3"),
+ z,
+ tags,
+ 7,
+ ))
}
/// The dummy call keeps libtvm_ffi_testing, which registers
`testing.TestCxxClassBase`, linked.
fn cxx_child(v_i64: i64, v_i32: i32, name: &str) -> RsCxxChild {
assert_eq!(unsafe { tvm_ffi_sys::TVMFFITestingDummyTarget() }, 0);
- RsCxxChildObj::new(TestCxxClassBaseObj::new(v_i64, v_i32), name).into()
+ RsCxxChild::from_object(RsCxxChildObj::new(
+ TestCxxClassBaseObj::new(v_i64, v_i32),
+ name,
+ ))
}
fn type_info<N: ObjectCore>() -> &'static TVMFFITypeInfo {
@@ -352,7 +393,7 @@ fn test_constructors_deref_and_upcast() {
assert!(back.same_as(&p3));
assert!(RsPoint3::try_from(Any::from(point(1, ""))).is_err());
- let marker =
RsMarker::from(RsMarkerObj::new(RsPointObj::new(Object::new(), 4, "m")));
+ let marker =
RsMarker::from_object(RsMarkerObj::new(RsPointObj::new(Object::new(), 4, "m")));
assert_eq!(marker.x, 4);
assert!(RsPoint::from(&marker).same_as(&marker));
@@ -364,7 +405,8 @@ fn test_constructors_deref_and_upcast() {
#[test]
fn test_new_takes_literals_of_the_field_types() {
register_types();
- let scalars = RsScalars::from(RsScalarsObj::new(Object::new(),
5_000_000_000, 1, 2, 0.5));
+ let scalars =
+ RsScalars::from_object(RsScalarsObj::new(Object::new(), 5_000_000_000,
1, 2, 0.5));
assert_eq!(
(scalars.big, scalars.count, scalars.port, scalars.ratio),
(5_000_000_000, 1, 2, 0.5)
@@ -411,7 +453,7 @@ fn test_shallow_copy_and_finality_are_type_attributes() {
assert!(is_final(RsPoint3Obj::type_index()));
assert!(is_final(RsMarkerObj::type_index()));
- let original =
RsMarker::from(RsMarkerObj::new(RsPointObj::new(Object::new(), 3, "m")));
+ let original =
RsMarker::from_object(RsMarkerObj::new(RsPointObj::new(Object::new(), 3, "m")));
let shallow_copy =
Function::try_from(attr(RsMarkerObj::type_index(),
"__ffi_shallow_copy__")).unwrap();
let copy =
RsMarker::try_from(shallow_copy.call_tuple((original.clone(),)).unwrap()).unwrap();
@@ -430,7 +472,7 @@ fn test_subtype_of_a_cpp_type() {
assert_eq!((child.v_i64, child.v_i32, child.name.as_str()), (7, 8, "c"));
// The C++ fields are reflected under the subtype.
let v_i64 = FieldGetter::new(RsCxxChildObj::type_index(),
"v_i64").unwrap();
- assert_eq!(v_i64.get::<_, i64>(&*child.base).unwrap(), 7);
+ assert_eq!(v_i64.get::<_, i64>(&*child).unwrap(), 7);
assert_eq!(field_names::<RsCxxChildObj>(), ["name"]);
let base = TestCxxClassBase::from(child.clone());
@@ -448,7 +490,7 @@ fn test_subtype_of_a_cpp_type() {
#[test]
fn test_crate_path_names_a_reexported_tvm_ffi() {
register_types();
- let value =
reexported::Reexported::from(reexported::ReexportedObj::new(Object::new(), 5));
+ let value = reexported::Reexported::new(5);
assert_eq!(value.v, 5);
assert!(ObjectRef::from(&value).same_as(&value));
assert_eq!(field_names::<reexported::ReexportedObj>(), ["v"]);
diff --git a/tests/python/test_stubgen_rust.py
b/tests/python/test_stubgen_rust.py
index bbb5f043..d60e5708 100644
--- a/tests/python/test_stubgen_rust.py
+++ b/tests/python/test_stubgen_rust.py
@@ -276,7 +276,7 @@ def test_check_tokens_ignores_spelling() -> None:
"#[derive(tvm_ffi::derive::Object)]",
"pub struct AddObj { base: ExprObj, pub a: i64, pub b: i64 }",
"const _: () = assert!(::core::mem::size_of::<AddObj>() == 40);",
- "tvm_ffi::unsafe_impl_object_upcast!(Add => super::super::ir::Expr);",
+ "tvm_ffi::unsafe_impl_object_ref_upcast!(Add =>
super::super::ir::Expr);",
]
respelled = [
"use crate::ir::Expr;",
@@ -287,7 +287,7 @@ def test_check_tokens_ignores_spelling() -> None:
" pub b: i64,",
"}",
"const _: () = assert!(core::mem::size_of::<AddObj>() == 40);",
- "unsafe_impl_object_upcast!(Add => Expr);",
+ "unsafe_impl_object_ref_upcast!(Add => Expr);",
]
assert check_tokens(generated) == check_tokens(respelled)
for drift in [("a: i64", "a: i32"), ("40", "48"), ("ExprObj,",
"StmtObj,"), ("Expr)", "Stmt)")]:
@@ -380,13 +380,6 @@ pub struct Pair {{
base: ObjectArc<PairObj>,
}}
-impl Deref for Pair {{
- type Target = PairObj;
- fn deref(&self) -> &PairObj {{
- &self.base
- }}
-}}
-
impl PairObj {{
pub fn a(&self) -> Result<i64> {{
FieldGetter::new(Self::type_index(), "a")?.get(self)
@@ -425,7 +418,6 @@ def test_render_root_object() -> None:
text, imports = _render(info)
assert text == ROOT_EXPECTED
assert _uses(imports) == {
- "std::ops::Deref",
"tvm_ffi::Object",
"tvm_ffi::ObjectArc",
"tvm_ffi::ObjectCore",
@@ -466,7 +458,7 @@ def test_render_derived_object_same_module() -> None:
assert "#[type_final]\npub struct AddObj {\n base: ExprObj,\n}" in text
assert "impl Deref for AddObj {\n type Target = ExprObj;" in text
assert "pub fn a(&self) -> Result<Expr> {" in text
- assert text.endswith("tvm_ffi::unsafe_impl_object_upcast!(Add => BaseExpr,
Add => Expr);")
+ assert text.endswith("tvm_ffi::unsafe_impl_object_ref_upcast!(Add =>
BaseExpr, Add => Expr);")
# Same-module names are local items: nothing to `use`.
assert not any(path.startswith("demo") for path in _uses(imports))
@@ -476,7 +468,7 @@ def test_render_derived_object_cross_module() -> None:
info = _info("tirx.Add", parent="ir.Expr", ancestors=["ffi.Object",
"ir.Expr"])
text, imports = _render(info)
assert " base: ExprObj," in text
- assert text.endswith("tvm_ffi::unsafe_impl_object_upcast!(Add => Expr);")
+ assert text.endswith("tvm_ffi::unsafe_impl_object_ref_upcast!(Add =>
Expr);")
assert {"crate::ir::ExprObj", "crate::ir::Expr"} <= _uses(imports)
@@ -495,7 +487,7 @@ def test_render_object_under_builtin_parent() -> None:
assert "tvm_ffi::Object" in _uses(imports)
# No Deref, upcast or crate import for the stand-ins.
assert "impl Deref for ColorObj" not in text
- assert "unsafe_impl_object_upcast" not in text
+ assert "unsafe_impl_object_ref_upcast" not in text
assert not any("Enum" in path for path in _uses(imports))
# A child of `ffi.Object` embeds the crate's header.
text, imports = _render(_info("demo.Root"))
@@ -513,7 +505,6 @@ def test_render_object_under_builtin_parent() -> None:
BUILTIN_MIRRORS_EXPECTED = """\
-use std::ops::Deref;
use tvm_ffi::Object;
use tvm_ffi::ObjectArc;
@@ -609,13 +600,6 @@ pub struct IterVar {{
base: ObjectArc<IterVarObj>,
}}
-impl Deref for IterVar {{
- type Target = IterVarObj;
- fn deref(&self) -> &IterVarObj {{
- &self.base
- }}
-}}
-
impl Deref for IterVarObj {{
type Target = PrimExprConvertibleObj;
fn deref(&self) -> &PrimExprConvertibleObj {{
@@ -646,7 +630,7 @@ impl IterVarObj {{
}}
}}
-tvm_ffi::unsafe_impl_object_upcast!(IterVar => PrimExprConvertible);"""
+tvm_ffi::unsafe_impl_object_ref_upcast!(IterVar => PrimExprConvertible);"""
def test_render_iter_var_golden() -> None:
@@ -759,13 +743,6 @@ pub struct Expr {
base: ObjectArc<ExprObj>,
}
-impl Deref for Expr {
- type Target = ExprObj;
- fn deref(&self) -> &ExprObj {
- &self.base
- }
-}
-
impl ExprObj {
pub fn new(span: Option<Span>, ty: Type) -> Self {
let base = Object::new();
@@ -824,13 +801,6 @@ pub struct Add {
base: ObjectArc<AddObj>,
}
-impl Deref for Add {
- type Target = AddObj;
- fn deref(&self) -> &AddObj {
- &self.base
- }
-}
-
impl Deref for AddObj {
type Target = ExprObj;
fn deref(&self) -> &ExprObj {
@@ -858,7 +828,7 @@ impl Add {
}
}
-tvm_ffi::unsafe_impl_object_upcast!(Add => Expr, Add => PrimExpr);"""
+tvm_ffi::unsafe_impl_object_ref_upcast!(Add => Expr, Add => PrimExpr);"""
def test_render_complete_expr_golden() -> None:
@@ -868,7 +838,6 @@ def test_render_complete_expr_golden() -> None:
text, imports = _render(_expr(), imports)
assert text == EXPR_EXPECTED
assert _uses(imports) == {
- "std::ops::Deref",
"std::ptr::addr_of_mut",
"tvm_ffi::Object",
"tvm_ffi::ObjectArc",
@@ -1237,7 +1206,7 @@ def test_upcast_directive_adds_typed_views() -> None:
RUST.add_directive(imports, "upcast", "demo.Leaf -> Other", 2)
text, imports = _render(info, imports)
assert text.endswith(
- "tvm_ffi::unsafe_impl_object_upcast!(Leaf => Base, Leaf => LeafView,
Leaf => Other);"
+ "tvm_ffi::unsafe_impl_object_ref_upcast!(Leaf => Base, Leaf =>
LeafView, Leaf => Other);"
)
assert "crate::typed::LeafView" in _uses(imports)
assert "fn new(" not in text
@@ -1408,7 +1377,7 @@ def test_registry_complete_chain_is_mirrored() -> None:
"}"
) in dd
assert dd.endswith(
- "tvm_ffi::unsafe_impl_object_upcast!(TestCxxClassDerivedDerived =>
TestCxxClassBase, "
+ "tvm_ffi::unsafe_impl_object_ref_upcast!(TestCxxClassDerivedDerived =>
TestCxxClassBase, "
"TestCxxClassDerivedDerived => TestCxxClassDerived);"
)
assert {"tvm_ffi::String", "tvm_ffi::ShallowCopy"} <= _uses(imports)
@@ -1475,7 +1444,7 @@ def test_cli_init_generates_a_module_tree(tmp_path: Path,
monkeypatch: pytest.Mo
assert '#[type_key = "testing.TestCxxClassDerivedDerived"]' in text
assert " base: TestCxxClassDerivedObj,\n pub v_str: String," in text
assert (
- "tvm_ffi::unsafe_impl_object_upcast!(TestCxxClassDerivedDerived =>
TestCxxClassBase, "
+ "tvm_ffi::unsafe_impl_object_ref_upcast!(TestCxxClassDerivedDerived =>
TestCxxClassBase, "
"TestCxxClassDerivedDerived => TestCxxClassDerived);"
) in text
# Builtin ancestors are mirrored once, in the import section; enum
fixtures embed the last one.
@@ -1697,7 +1666,7 @@ def test_blocks_are_named_by_the_module_of_their_file(
text = nested.read_text(encoding="utf-8")
assert "use crate::TestCxxClassBase;\nuse crate::TestCxxClassBaseObj;\n"
in text
assert " base: TestCxxClassBaseObj," in text
- assert "tvm_ffi::unsafe_impl_object_upcast!(TestCxxClassDerived =>
TestCxxClassBase);" in text
+ assert "tvm_ffi::unsafe_impl_object_ref_upcast!(TestCxxClassDerived =>
TestCxxClassBase);" in text
assert "super::" not in text
# Moving the parent's block moves the path, not the type key.
lib.write_text("", encoding="utf-8")
@@ -1720,7 +1689,7 @@ def test_ty_map_parent_redirects_base_deref_and_upcast()
-> None:
text = "\n".join(block.lines[1:-1])
assert " base: ExprObj," in text
assert "impl Deref for AddObj {\n type Target = ExprObj;" in text
- assert text.endswith("tvm_ffi::unsafe_impl_object_upcast!(Add => Expr);")
+ assert text.endswith("tvm_ffi::unsafe_impl_object_ref_upcast!(Add =>
Expr);")
assert {"crate::ir::ExprObj", "crate::ir::Expr"} <= _uses(imports)
assert not any(path.startswith("super::") for path in _uses(imports))
@@ -1820,7 +1789,7 @@ def
test_parent_with_the_objects_own_name_is_spelled_in_full() -> None:
parent = "crate::demo::t::Frame"
assert f" base: {parent}Obj," in text
assert f" type Target = {parent}Obj;" in text
- assert text.endswith(f"tvm_ffi::unsafe_impl_object_upcast!(Frame =>
{parent});")
+ assert text.endswith(f"tvm_ffi::unsafe_impl_object_ref_upcast!(Frame =>
{parent});")
assert not any(path.startswith(f"{parent}") for path in _uses(imports))
@@ -1859,7 +1828,7 @@ def
test_ty_map_target_in_the_files_own_module_is_not_imported() -> None:
assert "crate::ir::Op" not in _uses(imports)
text, imports = render(_info("ir.Special", parent="ir.Op",
ancestors=["ffi.Object", "ir.Op"]))
assert " base: OpObj," in text
- assert text.endswith("tvm_ffi::unsafe_impl_object_upcast!(Special => Op);")
+ assert text.endswith("tvm_ffi::unsafe_impl_object_ref_upcast!(Special =>
Op);")
assert not any(path.startswith("crate::") for path in _uses(imports))
# Elsewhere the target is imported as written.
text, imports = render(_info("tirx.Call", (_field("op", "ir.Op"),)))