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 e9a4ab04 [Rust] Group Object derive attributes under #[object(...)];
std-shaped Array/List accessors (#962)
e9a4ab04 is described below
commit e9a4ab04944de2a8dc55d554dd7ceda76a76d8e3
Author: Tianqi Chen <[email protected]>
AuthorDate: Sun Nov 8 09:07:15 2026 -0500
[Rust] Group Object derive attributes under #[object(...)]; std-shaped
Array/List accessors (#962)
This PR makes two Rust API consistency changes.
**`#[derive(Object)]` options grouped under `#[object(...)]`.** The ten
flat `#[type_*]` helper attributes become one attribute, parsed like
`#[object_ref(...)]`:
```rust
#[object(type_key = "my_ext.Point", register, final, immutable, child_slots
= 4)]
#[object(type_key = "ffi.String", unsafe type_index =
TypeIndex::kTVMFFIStr)]
```
- `type_key` and `type_index` keep their names. The other options drop
the `type_` prefix: `final`, `immutable`, `register`, `rs_parent`,
`child_slots`, `child_slots_can_overflow`, `s_eq_hash_kind`,
`reflection`.
- `unsafe` is allowed only before `type_index`. An unknown option is an
error that lists the valid ones.
- The flat attributes are removed. `#[rs_object]`, the Rust stub
generator, the in-tree users and the `derive::Object` docs now use the
new form.
**`Array` and `List` accessors follow std shapes.** A method that can
genuinely fail returns `Result`: a type mismatch, an FFI call or growth.
Out-of-range is then an `IndexError` inside it. A method that can fail
only on its bounds follows std instead.
- `Array::get` returns `Option<T>`, since elements are checked when the
array is built.
- `Array::set`, `Array::insert` and `List::set` return `()` and panic on
an out-of-range index, as `Vec` does.
- `List::remove_range` takes `impl RangeBounds<usize>`, returns `()` and
panics on an out-of-range or inverted range, as `Vec::drain` does.
---
docs/guides/rust_lang_guide.rst | 2 +-
.../rust/src/generated/rust_stubgen/mod.rs | 4 +-
python/tvm_ffi/stub/rust_generator/codegen.py | 21 ++-
rust/tvm-ffi-macros/src/lib.rs | 17 +-
rust/tvm-ffi-macros/src/object_macros.rs | 209 ++++++++++++---------
rust/tvm-ffi-macros/src/rs_object.rs | 18 +-
rust/tvm-ffi-macros/src/utils.rs | 33 ----
rust/tvm-ffi/src/big_int/mod.rs | 4 +-
rust/tvm-ffi/src/collections/array.rs | 76 +++-----
rust/tvm-ffi/src/collections/list.rs | 51 +++--
rust/tvm-ffi/src/collections/map.rs | 3 +-
rust/tvm-ffi/src/collections/shape.rs | 3 +-
rust/tvm-ffi/src/collections/tensor.rs | 3 +-
rust/tvm-ffi/src/derive.rs | 36 +++-
rust/tvm-ffi/src/enum_ref.rs | 8 +-
rust/tvm-ffi/src/error.rs | 3 +-
rust/tvm-ffi/src/extra/module.rs | 3 +-
rust/tvm-ffi/src/extra/structural_eq_hash.rs | 3 +-
rust/tvm-ffi/src/extra/structural_mutate.rs | 2 +-
rust/tvm-ffi/src/extra/structural_visit.rs | 8 +-
rust/tvm-ffi/src/extra/visit_error_context.rs | 3 +-
rust/tvm-ffi/src/function.rs | 3 +-
rust/tvm-ffi/src/lib.rs | 2 +-
rust/tvm-ffi/src/object.rs | 2 +-
rust/tvm-ffi/src/reflection/access_path.rs | 6 +-
rust/tvm-ffi/src/reflection/enum_def.rs | 8 +-
rust/tvm-ffi/src/reflection/registry.rs | 2 +-
rust/tvm-ffi/src/reflection/unsafe_init.rs | 2 +-
rust/tvm-ffi/src/string.rs | 6 +-
rust/tvm-ffi/tests/test_array.rs | 8 +-
rust/tvm-ffi/tests/test_cast.rs | 14 +-
rust/tvm-ffi/tests/test_enum.rs | 4 +-
rust/tvm-ffi/tests/test_list.rs | 15 +-
rust/tvm-ffi/tests/test_match_any_ast_lookup.rs | 14 +-
rust/tvm-ffi/tests/test_object_reflection.rs | 37 ++--
rust/tvm-ffi/tests/test_object_registration.rs | 20 +-
rust/tvm-ffi/tests/test_rs_object.rs | 3 +-
rust/tvm-ffi/tests/test_structural_mutate.rs | 17 +-
tests/python/test_stubgen_rust.py | 29 ++-
39 files changed, 341 insertions(+), 361 deletions(-)
diff --git a/docs/guides/rust_lang_guide.rst b/docs/guides/rust_lang_guide.rst
index 1f4439a2..4f476985 100644
--- a/docs/guides/rust_lang_guide.rst
+++ b/docs/guides/rust_lang_guide.rst
@@ -443,7 +443,7 @@ take all-zero bytes as a value, as a scalar, an ``Option``
of a reference or an
``Optional<T>`` does; a reference or
a string does not, and fails the type's registration. The parent's object must
implement ``ShallowCopy`` and ``UnsafeInit``, as ``rs_object`` types do, and so
-does a binding with ``#[type_rs_parent]`` on its ``derive(Object)``, such as a
+does a binding with ``#[object(rs_parent)]`` on its ``derive(Object)``, such
as a
non-final type bound by ``tvm-ffi-stubgen``.
Converting Borrowed Values into ``Any``
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 b2cdae8a..ee27babc 100644
--- a/examples/rust_stubgen/rust/src/generated/rust_stubgen/mod.rs
+++ b/examples/rust_stubgen/rust/src/generated/rust_stubgen/mod.rs
@@ -74,9 +74,7 @@ impl TryFrom<i64> for PairKind {
/// Complete: reflected fields fill [24, 48) exactly (alignment padding [44,
48)).
#[repr(C)]
#[derive(tvm_ffi::derive::Object)]
-#[type_key = "rust_stubgen.IntPair"]
-#[type_final]
-#[type_immutable]
+#[object(type_key = "rust_stubgen.IntPair", final, immutable)]
pub struct IntPairObj {
base_: Object,
pub a: i64,
diff --git a/python/tvm_ffi/stub/rust_generator/codegen.py
b/python/tvm_ffi/stub/rust_generator/codegen.py
index d2de8b75..b8eb3f46 100644
--- a/python/tvm_ffi/stub/rust_generator/codegen.py
+++ b/python/tvm_ffi/stub/rust_generator/codegen.py
@@ -30,7 +30,7 @@ the verdict of :mod:`tvm_ffi.stub.layout`:
one ``from_fields``. A type that is not final
can be the parent of a type defined in Rust (``tvm_ffi::rs_object``): its
``<Leaf>Obj::new`` is public, and its object derives ``ShallowCopy`` and
- ``UnsafeInit`` (``#[type_rs_parent]``) when every ancestor's is generated
+ ``UnsafeInit`` (``rs_parent``) when every ancestor's is generated
too (a ``ty-map``'d one is hand-written and may not). ``<Leaf>Obj::new`` of
a final type is crate-private.
- *opaque*: the struct embeds only its parent, and each field is read through
the C ABI getter. Nothing allocates it.
@@ -577,16 +577,23 @@ class _ObjectRenderer:
lines.append(f"tvm_ffi::impl_object_ref_upcast!({prefix}{pairs});")
return lines
+ def _object_attr(self, verdict: Verdict) -> str:
+ """Render the ``#[object(...)]`` attribute of the object struct."""
+ options = [
+ f'type_key = "{self.type_key}"',
+ *(["final"] if self.info.is_final else []),
+ *(["immutable"] if self._is_immutable() else []),
+ *(["rs_parent"] if self._is_rs_parent(verdict) else []),
+ ]
+ return f"#[object({', '.join(options)})]"
+
def _struct_lines(self, verdict: Verdict, base: str) -> list[str]:
"""Render the object struct: every field when complete, the parent
alone when opaque."""
header = [
*self._allow_missing_docs(),
"#[repr(C)]",
"#[derive(tvm_ffi::derive::Object)]",
- f'#[type_key = "{self.type_key}"]',
- *(["#[type_final]"] if self.info.is_final else []),
- *(["#[type_immutable]"] if self._is_immutable() else []),
- *(["#[type_rs_parent]"] if self._is_rs_parent(verdict) else []),
+ self._object_attr(verdict),
f"pub struct {self.obj_struct} {{",
f" base_: {base},", # a reflected `base_` is rejected
(`rust_ident`)
]
@@ -787,7 +794,7 @@ class _ObjectRenderer:
return all(field.frozen for each in [*chain, info] for field in
each.fields)
def _is_rs_parent(self, verdict: Verdict) -> bool:
- """Whether the object derives ``ShallowCopy`` and ``UnsafeInit``
(``#[type_rs_parent]``).
+ """Whether the object derives ``ShallowCopy`` and ``UnsafeInit``
(``rs_parent``).
A subtype defined in Rust needs both of its parent's object. They are
derived
for a complete type that is not final and whose ancestors below
``ffi.Object``
@@ -925,7 +932,7 @@ def _builtin_mirror_lines(type_key: str, base: str) ->
list[str]:
"#[allow(dead_code)]",
"#[repr(C)]",
"#[derive(tvm_ffi::derive::Object)]",
- f'#[type_key = "{type_key}"]',
+ f'#[object(type_key = "{type_key}")]',
f"pub struct {builtin_mirror_name(type_key)} {{",
f" base_: {base},",
"}",
diff --git a/rust/tvm-ffi-macros/src/lib.rs b/rust/tvm-ffi-macros/src/lib.rs
index 4a8ab6f8..408fd2fd 100644
--- a/rust/tvm-ffi-macros/src/lib.rs
+++ b/rust/tvm-ffi-macros/src/lib.rs
@@ -45,22 +45,7 @@ pub fn match_any(input: TokenStream) -> TokenStream {
// Documented on the `tvm_ffi::derive::Object` re-export, whose examples run
// as tvm-ffi doctests.
#[proc_macro_error]
-#[proc_macro_derive(
- Object,
- attributes(
- type_key,
- type_index,
- type_final,
- type_immutable,
- type_rs_parent,
- type_register,
- type_child_slots,
- type_child_slots_can_overflow,
- type_s_eq_hash_kind,
- type_reflection,
- reflect
- )
-)]
+#[proc_macro_derive(Object, attributes(object, reflect))]
pub fn derive_object(input: TokenStream) -> TokenStream {
object_macros::derive_object(input)
}
diff --git a/rust/tvm-ffi-macros/src/object_macros.rs
b/rust/tvm-ffi-macros/src/object_macros.rs
index 7d8a08bb..58eef552 100644
--- a/rust/tvm-ffi-macros/src/object_macros.rs
+++ b/rust/tvm-ffi-macros/src/object_macros.rs
@@ -35,7 +35,7 @@ pub fn derive_object(input: proc_macro::TokenStream) ->
TokenStream {
/// registers reflection, naming the tvm-ffi crate `tvm_ffi_crate`.
///
/// `rs_object` registers the reflection of a type without `#[reflect]` fields
-/// or `#[type_s_eq_hash_kind]` too, with the creator of its `UnsafeInit`, as
+/// or `s_eq_hash_kind` too, with the creator of its `UnsafeInit`, as
/// `#[rs_object]` does.
pub(crate) fn expand_object(
derive_input: &DeriveInput,
@@ -43,63 +43,37 @@ pub(crate) fn expand_object(
rs_object: bool,
) -> TokenStream2 {
let struct_name = derive_input.ident.clone();
+ let options = object_options(derive_input);
- let type_key = get_attr(derive_input, "type_key")
- .map(attr_to_str)
- .expect("Expect #[type_key = \"<my_type_key>\"] attribute");
- let type_final = match get_attr(derive_input, "type_final") {
- Some(attr) if matches!(attr.parse_meta(), Ok(syn::Meta::Path(_))) =>
true,
- Some(_) => panic!("Expect #[type_final] attribute"),
- None => false,
- };
-
- let type_immutable = match get_attr(derive_input, "type_immutable") {
- Some(attr) if matches!(attr.parse_meta(), Ok(syn::Meta::Path(_))) =>
true,
- Some(_) => panic!("Expect #[type_immutable] attribute"),
- None => false,
- };
-
+ let type_key = options
+ .type_key
+ .clone()
+ .expect("Expect #[object(type_key = \"<my_type_key>\")]");
+ let type_final = options.type_final;
+ let type_immutable = options.immutable;
// Whether an `rs_object` can derive from this type, which needs its
// `ShallowCopy` and `UnsafeInit`.
- let type_rs_parent = match get_attr(derive_input, "type_rs_parent") {
- Some(attr) if matches!(attr.parse_meta(), Ok(syn::Meta::Path(_))) =>
true,
- Some(_) => panic!("Expect #[type_rs_parent] attribute"),
- None => false,
- };
-
+ let type_rs_parent = options.rs_parent;
// Whether this type is defined here, so that it registers its key, as C++
// `TVM_FFI_DECLARE_OBJECT_INFO` does. Without it, the type binds a key
// that its defining library, usually C++, has registered.
- let type_register = match get_attr(derive_input, "type_register") {
- Some(attr) if matches!(attr.parse_meta(), Ok(syn::Meta::Path(_))) =>
true,
- Some(_) => panic!("Expect #[type_register] attribute"),
- None => false,
- };
+ let type_register = options.register;
// Reserved child slots and whether children may exceed them, as C++
// `_type_child_slots` and `_type_child_slots_can_overflow` declare them.
- let child_slots = match get_attr(derive_input, "type_child_slots").map(|a|
a.parse_meta()) {
- Some(Ok(syn::Meta::NameValue(syn::MetaNameValue {
- lit: syn::Lit::Int(n),
- ..
- }))) => n
+ let child_slots = match &options.child_slots {
+ Some(n) => n
.base10_parse::<i32>()
- .expect("Expect #[type_child_slots = <non-negative integer>]"),
- Some(_) => panic!("Expect #[type_child_slots = <non-negative
integer>]"),
+ .expect("Expect `child_slots = <non-negative integer>`"),
None => 0,
};
- let child_slots_can_overflow =
- match get_attr(derive_input, "type_child_slots_can_overflow").map(|a|
a.parse_meta()) {
- Some(Ok(syn::Meta::NameValue(syn::MetaNameValue {
- lit: syn::Lit::Bool(b),
- ..
- }))) => b.value,
- Some(_) => panic!("Expect #[type_child_slots_can_overflow =
<bool>]"),
- None => true,
- };
+ let child_slots_can_overflow = options
+ .child_slots_can_overflow
+ .as_ref()
+ .is_none_or(|b| b.value);
assert!(
child_slots >= 0,
- "Expect #[type_child_slots = <non-negative integer>]"
+ "Expect `child_slots = <non-negative integer>`"
);
assert!(
!(type_final && child_slots > 0),
@@ -107,9 +81,8 @@ pub(crate) fn expand_object(
);
assert!(
type_register
- || (get_attr(derive_input, "type_child_slots").is_none()
- && get_attr(derive_input,
"type_child_slots_can_overflow").is_none()),
- "#[type_child_slots] and #[type_child_slots_can_overflow] need
#[type_register]"
+ || (options.child_slots.is_none() &&
options.child_slots_can_overflow.is_none()),
+ "`child_slots` and `child_slots_can_overflow` need `register`"
);
// Upcasts reinterpret a pointer to the object as one to its parent, which
// only `repr(C)` keeps at offset zero.
@@ -123,7 +96,7 @@ pub(crate) fn expand_object(
}
.expect("First field must be `<base_name>: <ObjectCoreType>`");
- let reflection = Reflection::parse(derive_input, rs_object);
+ let reflection = Reflection::parse(derive_input, &options, rs_object);
let writable = reflection
.iter()
.flat_map(|reflection| &reflection.reflected)
@@ -143,9 +116,8 @@ pub(crate) fn expand_object(
quote! {}
};
assert!(
- reflection.is_none() || (type_register && get_attr(derive_input,
"type_index").is_none()),
- "#[reflect], #[type_s_eq_hash_kind] and #[type_reflection] need
#[type_register] and no \
- #[type_index]"
+ reflection.is_none() || (type_register &&
options.type_index.is_none()),
+ "#[reflect], `s_eq_hash_kind` and `reflection` need `register` and no
`type_index`"
);
let register_reflection = match &reflection {
Some(reflection) => reflection.register_tokens(&struct_name,
&type_key, tvm_ffi_crate),
@@ -161,7 +133,7 @@ pub(crate) fn expand_object(
if !derive_input.generics.params.is_empty() {
abort!(
struct_name,
- "#[type_rs_parent] needs a struct without generic parameters"
+ "`rs_parent` needs a struct without generic parameters"
);
}
unsafe_init_tokens(&struct_name, &base_ty, &base_id, &field_ids,
tvm_ffi_crate)
@@ -174,22 +146,10 @@ pub(crate) fn expand_object(
// calls at load, or takes the index the key already has,
// as C++ `TVM_FFI_DECLARE_OBJECT_INFO` does; any other type binds the
// index its defining library registered.
- let type_index_tokens = match get_attr(derive_input, "type_index") {
- Some(attr) => {
- // A static index is trusted: the type must be the one the runtime
- // gives that index.
- let type_index_expr = attr
- .parse_args_with(|input: syn::parse::ParseStream| {
- input.parse::<syn::Token![unsafe]>()?;
- input.parse::<syn::Expr>()
- })
- .unwrap_or_else(|error| {
- abort!(
- attr,
- "expected #[type_index(unsafe TypeIndex::...)]: {}",
- error
- )
- });
+ let type_index_tokens = match &options.type_index {
+ // A static index is trusted: the type must be the one the runtime
+ // gives that index.
+ Some(type_index_expr) => {
quote! {
#[inline]
fn type_index() -> i32 {
@@ -255,7 +215,8 @@ pub(crate) fn expand_object(
if ret != 0 {
::core::panic!(
"Type key {} is not registered: load the
library that defines \
- it first, or add #[type_register] if this
crate defines it",
+ it first, or add #[object(register)] if
this crate defines \
+ it",
#type_key
);
}
@@ -291,11 +252,11 @@ pub(crate) fn expand_object(
// A type defined here registers when its library loads, as a C++ type does
// through `refl::ObjectDef` in a static init block; a binding stays lazy,
// and so does a type under a binding whose library is not loaded yet.
- let register_at_load = if type_register && get_attr(derive_input,
"type_index").is_none() {
+ let register_at_load = if type_register && options.type_index.is_none() {
if !derive_input.generics.params.is_empty() {
abort!(
struct_name,
- "#[type_register] needs a struct without generic parameters"
+ "`register` needs a struct without generic parameters"
);
}
// The key is in the text of the block, which identifies the block, so
@@ -402,12 +363,12 @@ pub(crate) fn expand_object(
}
}
-/// The reflection a `#[type_register]` type declares: its `#[reflect]` fields,
-/// its `#[type_s_eq_hash_kind]` and its `#[type_reflection]` function.
+/// The reflection a `register` type declares: its `#[reflect]` fields, its
+/// `s_eq_hash_kind` and its `reflection` function.
struct Reflection {
/// The structural kind; `None` inherits the parent's.
kind: Option<syn::Ident>,
- /// The function `#[type_reflection(path)]` names, which registers methods.
+ /// The function `reflection = path` names, which registers methods.
methods: Option<syn::Path>,
/// The reflected fields.
reflected: Vec<ReflectedField>,
@@ -501,15 +462,12 @@ fn doc_comment(attrs: &[syn::Attribute]) -> String {
impl Reflection {
/// The type's reflection, or `None` when it declares none and is not an
/// `rs_object`.
- fn parse(derive_input: &DeriveInput, rs_object: bool) -> Option<Self> {
- let kind = get_attr(derive_input, "type_s_eq_hash_kind").map(|attr| {
- let kind = attr_to_str(attr);
- syn::Ident::new(&kind.value(), kind.span())
- });
- let methods = get_attr(derive_input, "type_reflection").map(|attr| {
- attr.parse_args::<syn::Path>()
- .expect("Expect #[type_reflection(<path to fn(&mut
ObjectDef<Self>)>)]")
- });
+ fn parse(derive_input: &DeriveInput, options: &ObjectOptions, rs_object:
bool) -> Option<Self> {
+ let kind = options
+ .s_eq_hash_kind
+ .as_ref()
+ .map(|kind| syn::Ident::new(&kind.value(), kind.span()));
+ let methods = options.reflection.clone();
let syn::Data::Struct(s) = &derive_input.data else {
return None;
};
@@ -577,7 +535,7 @@ impl Reflection {
)?;
}
});
- // The `#[type_reflection]` function registers the methods after the
+ // The `reflection` function registers the methods after the
// fields, as a C++ `ObjectDef` chains `def` after `def_ro`.
let methods = self.methods.as_ref().map(|path| {
// The registration is a `static` initializer, where `Self` does
@@ -988,3 +946,86 @@ fn object_ref_options(derive_input: &DeriveInput) ->
ObjectRefOptions {
}
options
}
+
+/// The options of `#[object(...)]` on an `Object` derive.
+#[derive(Default)]
+struct ObjectOptions {
+ type_key: Option<syn::LitStr>,
+ type_index: Option<syn::Expr>,
+ type_final: bool,
+ immutable: bool,
+ register: bool,
+ rs_parent: bool,
+ child_slots: Option<syn::LitInt>,
+ child_slots_can_overflow: Option<syn::LitBool>,
+ s_eq_hash_kind: Option<syn::LitStr>,
+ reflection: Option<syn::Path>,
+}
+
+impl ObjectOptions {
+ /// Parses one `#[object(...)]` item: a flag, `name = value` or
+ /// `unsafe type_index = expr`.
+ fn parse_item(&mut self, input: syn::parse::ParseStream) ->
syn::Result<()> {
+ let is_unsafe =
input.parse::<Option<syn::Token![unsafe]>>()?.is_some();
+ // `final` is a keyword.
+ let name: syn::Ident = input.call(syn::Ident::parse_any)?;
+ if is_unsafe != (name == "type_index") {
+ let message = if is_unsafe {
+ "only `type_index` takes `unsafe`"
+ } else {
+ "expected `unsafe type_index = ...`: the type must be the one
the runtime gives \
+ that index"
+ };
+ return Err(syn::Error::new(name.span(), message));
+ }
+ match name.to_string().as_str() {
+ "final" => self.type_final = true,
+ "immutable" => self.immutable = true,
+ "register" => self.register = true,
+ "rs_parent" => self.rs_parent = true,
+ "type_key" => self.type_key = Some(parse_value(input)?),
+ "type_index" => self.type_index = Some(parse_value(input)?),
+ "child_slots" => self.child_slots = Some(parse_value(input)?),
+ "child_slots_can_overflow" => self.child_slots_can_overflow =
Some(parse_value(input)?),
+ "s_eq_hash_kind" => self.s_eq_hash_kind =
Some(parse_value(input)?),
+ "reflection" => self.reflection = Some(parse_value(input)?),
+ _ => {
+ return Err(syn::Error::new(
+ name.span(),
+ "unknown `object` option; expected `type_key = \"...\"`, \
+ `unsafe type_index = ...`, `final`, `immutable`,
`register`, `rs_parent`, \
+ `child_slots = <n>`, `child_slots_can_overflow = <bool>`,
\
+ `s_eq_hash_kind = \"<kind>\"` or `reflection = <path>`",
+ ))
+ }
+ }
+ Ok(())
+ }
+}
+
+/// The `= value` of a `name = value` option.
+fn parse_value<T: syn::parse::Parse>(input: syn::parse::ParseStream) ->
syn::Result<T> {
+ input.parse::<syn::Token![=]>()?;
+ input.parse()
+}
+
+fn object_options(derive_input: &DeriveInput) -> ObjectOptions {
+ let mut options = ObjectOptions::default();
+ for attr in derive_input
+ .attrs
+ .iter()
+ .filter(|a| a.path.is_ident("object"))
+ {
+ attr.parse_args_with(|input: syn::parse::ParseStream| {
+ while !input.is_empty() {
+ options.parse_item(input)?;
+ if !input.is_empty() {
+ input.parse::<syn::Token![,]>()?;
+ }
+ }
+ Ok(())
+ })
+ .unwrap_or_else(|error| abort!(error.span(), "{}", error));
+ }
+ options
+}
diff --git a/rust/tvm-ffi-macros/src/rs_object.rs
b/rust/tvm-ffi-macros/src/rs_object.rs
index 06cef3d9..da53a2e3 100644
--- a/rust/tvm-ffi-macros/src/rs_object.rs
+++ b/rust/tvm-ffi-macros/src/rs_object.rs
@@ -188,26 +188,26 @@ fn expand(args: &RsObjectArgs, item: &DeriveInput) ->
syn::Result<TokenStream2>
let obj = &item.ident;
let key = &args.type_key;
- // `derive(Object)` reads the type from these attributes; the struct
written out
+ // `derive(Object)` reads the type from these options; the struct written
out
// carries neither them nor `#[reflect]`, which no derive on it would take.
- let mut derive_attrs = vec![quote!(#[type_key = #key]),
quote!(#[type_register])];
+ let mut options = vec![quote!(type_key = #key), quote!(register)];
if args.type_final {
- derive_attrs.push(quote!(#[type_final]));
+ options.push(quote!(final));
}
if args.immutable {
- derive_attrs.push(quote!(#[type_immutable]));
+ options.push(quote!(immutable));
}
if let Some(kind) = &args.s_eq_hash_kind {
- derive_attrs.push(quote!(#[type_s_eq_hash_kind = #kind]));
+ options.push(quote!(s_eq_hash_kind = #kind));
}
if let Some(slots) = &args.child_slots {
- derive_attrs.push(quote!(#[type_child_slots = #slots]));
+ options.push(quote!(child_slots = #slots));
}
if let Some(overflow) = &args.child_slots_can_overflow {
- derive_attrs.push(quote!(#[type_child_slots_can_overflow =
#overflow]));
+ options.push(quote!(child_slots_can_overflow = #overflow));
}
if let Some(reflection) = &args.reflection {
- derive_attrs.push(quote!(#[type_reflection(#reflection)]));
+ options.push(quote!(reflection = #reflection));
}
let reflected_fields = fields.iter().map(|field| {
if field.attrs.iter().any(|a| a.path.is_ident("reflect")) {
@@ -218,7 +218,7 @@ fn expand(args: &RsObjectArgs, item: &DeriveInput) ->
syn::Result<TokenStream2>
});
let object_input: DeriveInput = syn::parse2(quote! {
#[repr(C)]
- #(#derive_attrs)*
+ #[object(#(#options),*)]
#vis struct #obj {
#base,
#(#reflected_fields,)*
diff --git a/rust/tvm-ffi-macros/src/utils.rs b/rust/tvm-ffi-macros/src/utils.rs
index ec1a5a6b..e6e3560a 100644
--- a/rust/tvm-ffi-macros/src/utils.rs
+++ b/rust/tvm-ffi-macros/src/utils.rs
@@ -26,36 +26,3 @@ use quote::quote;
pub(crate) fn get_tvm_ffi_crate() -> TokenStream {
quote!(::tvm_ffi)
}
-
-/// Get an attribute by name from a derive input
-///
-/// # Arguments
-/// * `derive_input` - The derive input to get the attribute from
-/// * `name` - The name of the attribute to get
-///
-/// # Returns
-/// * `Option<&syn::Attribute>` - The attribute if it exists
-pub(crate) fn get_attr<'a>(
- derive_input: &'a syn::DeriveInput,
- name: &str,
-) -> Option<&'a syn::Attribute> {
- derive_input.attrs.iter().find(|a| a.path.is_ident(name))
-}
-
-/// Convert an attribute to a string
-///
-/// # Arguments
-/// * `attr` - The attribute to convert
-///
-/// # Returns
-/// * `syn::LitStr` - The string value of the attribute
-pub(crate) fn attr_to_str(attr: &syn::Attribute) -> syn::LitStr {
- match attr.parse_meta() {
- Ok(syn::Meta::NameValue(syn::MetaNameValue {
- lit: syn::Lit::Str(s),
- ..
- })) => s,
- Ok(_m) => panic!("Expected a string literal, got"),
- Err(e) => panic!("{}", e),
- }
-}
diff --git a/rust/tvm-ffi/src/big_int/mod.rs b/rust/tvm-ffi/src/big_int/mod.rs
index 3edad11f..bd17e6f8 100644
--- a/rust/tvm-ffi/src/big_int/mod.rs
+++ b/rust/tvm-ffi/src/big_int/mod.rs
@@ -65,9 +65,7 @@ unsafe impl Sync for BigInt {}
// two's-complement words as trailing items. Mirrors C++ `detail::BigIntObj`.
#[repr(C)]
#[derive(Object)]
-#[type_key = "ffi.BigInt"]
-#[type_index(unsafe TypeIndex::kTVMFFIBigInt)]
-#[type_final]
+#[object(type_key = "ffi.BigInt", unsafe type_index =
TypeIndex::kTVMFFIBigInt, final)]
struct BigIntObj {
object: Object,
size: usize,
diff --git a/rust/tvm-ffi/src/collections/array.rs
b/rust/tvm-ffi/src/collections/array.rs
index c52808ea..8d28473b 100644
--- a/rust/tvm-ffi/src/collections/array.rs
+++ b/rust/tvm-ffi/src/collections/array.rs
@@ -20,7 +20,6 @@
use std::fmt::Debug;
use std::marker::PhantomData;
-use std::ops::Deref;
use crate::any::TryFromTemp;
use crate::collections::list::seq_cells;
@@ -35,8 +34,7 @@ use tvm_ffi_sys::{TVMFFIAny, TVMFFIObject};
/// The object behind an [`Array`], laid out as the C++ `ArrayObj`.
#[repr(C)]
#[derive(Object)]
-#[type_key = "ffi.Array"]
-#[type_index(unsafe TypeIndex::kTVMFFIArray)]
+#[object(type_key = "ffi.Array", unsafe type_index = TypeIndex::kTVMFFIArray)]
pub struct ArrayObj {
pub(crate) object: Object,
/// The element buffer, `capacity` `TVMFFIAny` cells.
@@ -93,8 +91,8 @@ impl Drop for ArrayObj {
///
/// let mut array = Array::from_vec(vec![1_i64, 2, 3]);
/// assert_eq!(array.len(), 3);
-/// assert_eq!(array.get(1)?, 2);
-/// assert!(array.get(3).is_err());
+/// assert_eq!(array.get(1), Some(2));
+/// assert_eq!(array.get(3), None);
///
/// let snapshot = array.clone();
/// array.push(4);
@@ -103,7 +101,6 @@ impl Drop for ArrayObj {
///
/// let doubled: Array<i64> = array.iter().map(|x| x * 2).collect();
/// assert_eq!(doubled.iter().copied().collect::<Vec<_>>(), vec![2, 4, 6, 8]);
-/// # Ok::<(), tvm_ffi::Error>(())
/// ```
#[repr(C)]
#[derive(Clone)]
@@ -305,17 +302,18 @@ impl<T: ContainerElement + Clone> Array<T> {
container.size += 1;
}
- /// Insert `value` at `index`, shifting the elements after it.
+ /// Insert `value` at `index`, shifting the elements after it, as
+ /// [`Vec::insert`].
///
- /// # Errors
+ /// # Panics
///
- /// Returns an `IndexError` if `index > len`.
+ /// Panics if `index > len`.
///
/// See also [`List::insert`](crate::List::insert).
- pub fn insert(&mut self, index: usize, value: T) -> Result<(),
crate::Error> {
+ pub fn insert(&mut self, index: usize, value: T) {
let size = self.len();
if index > size {
- crate::bail!(crate::error::INDEX_ERROR, "Array insert index out of
bound");
+ panic!("insertion index (is {index}) should be <= len (is
{size})");
}
let container = self.make_mut(size + 1);
// SAFETY: `make_mut` left room for one more cell; the tail moves up
by one.
@@ -325,7 +323,6 @@ impl<T: ContainerElement + Clone> Array<T> {
write_cell(cell, value);
}
container.size += 1;
- Ok(())
}
/// Remove and return the last element, or `None` if the array is empty,
@@ -368,17 +365,17 @@ impl<T: ContainerElement + Clone> Array<T> {
}
}
- /// Replace the element at `index`.
+ /// Replace the element at `index`, as `vec[index] = value`.
///
- /// # Errors
+ /// # Panics
///
- /// Returns an `IndexError` if `index >= len`.
+ /// Panics if `index >= len`.
///
/// See also [`List::set`](crate::List::set).
- pub fn set(&mut self, index: usize, value: T) -> Result<(), crate::Error> {
+ pub fn set(&mut self, index: usize, value: T) {
let size = self.len();
if index >= size {
- crate::bail!(crate::error::INDEX_ERROR, "Array set index out of
bound");
+ panic!("Index out of bounds: the len is {size} but the index is
{index}");
}
let container = self.make_mut(size);
// SAFETY: the cell at `index` is initialized; its old value is dropped
@@ -389,7 +386,6 @@ impl<T: ContainerElement + Clone> Array<T> {
write_cell(cell, value);
drop(old);
}
- Ok(())
}
/// Return the number of elements.
@@ -410,39 +406,17 @@ impl<T: ContainerElement + Clone> Array<T> {
self.len() == 0
}
- /// Return the element at `index`.
- ///
- /// # Arguments
- ///
- /// * `index` - The position of the element.
- ///
- /// # Returns
- ///
- /// The element at `index`.
- ///
- /// # Errors
- ///
- /// Returns an `IndexError` if `index` is out of bounds, or a `TypeError`
- /// if the element is not a `T`.
- pub fn get(&self, index: usize) -> Result<T, crate::Error> {
- if index >= self.len() {
- crate::bail!(crate::error::INDEX_ERROR, "Array get index out of
bound");
- }
- unsafe {
- let container = self.data.deref();
- let base_ptr = container.data as *const TVMFFIAny;
- let raw_any_ref = &*base_ptr.add(index);
-
- match T::container_try_cast_from_any_view(raw_any_ref) {
- Ok(val) => Ok(val),
- Err(_) => crate::bail!(
- crate::error::TYPE_ERROR,
- "Failed to cast element at {} to {}",
- index,
- T::container_type_str()
- ),
- }
- }
+ /// Return the element at `index`, or `None` if it is out of bounds, as
+ /// [`slice::get`]. `array[index]` borrows it instead, and panics when out
+ /// of bounds.
+ pub fn get(&self, index: usize) -> Option<T> {
+ let cell = self.cells().get(index)?;
+ // SAFETY: every cell of an `Array<T>` holds a `T`, checked when the
+ // array is built.
+ Some(unsafe {
+ debug_assert!(T::container_check_any_strict(cell));
+ T::container_copy_from_any_view_after_check(cell)
+ })
}
/// An array of `items` with room for `capacity` elements.
diff --git a/rust/tvm-ffi/src/collections/list.rs
b/rust/tvm-ffi/src/collections/list.rs
index 91241a68..1951145f 100644
--- a/rust/tvm-ffi/src/collections/list.rs
+++ b/rust/tvm-ffi/src/collections/list.rs
@@ -48,6 +48,7 @@
use std::cell::UnsafeCell;
use std::fmt::Debug;
use std::marker::PhantomData;
+use std::ops::{Bound, RangeBounds};
use std::ptr;
use crate::any::TryFromTemp;
@@ -64,8 +65,7 @@ use tvm_ffi_sys::{TVMFFIAny, TVMFFIObject};
/// runtime replaces when the list grows.
#[repr(C)]
#[derive(Object)]
-#[type_key = "ffi.List"]
-#[type_index(unsafe TypeIndex::kTVMFFIList)]
+#[object(type_key = "ffi.List", unsafe type_index = TypeIndex::kTVMFFIList)]
pub struct ListObj {
pub(crate) object: Object,
// `List` mutates these through `&self` (`push`, `set`, growth), so they
are
@@ -310,20 +310,24 @@ impl<T: ContainerElement> List<T> {
}
}
- /// Replaces the element at `index`.
+ /// Replaces the element at `index`, as `vec[index] = value`.
+ ///
+ /// # Panics
+ ///
+ /// Panics if `index >= len`.
#[inline]
- pub fn set(&self, index: usize, value: T) -> Result<()> {
+ pub fn set(&self, index: usize, value: T) {
let obj = self.obj();
unsafe {
- if index >= (*(*obj).size.get()) as usize {
- return index_error("set");
+ let size = (*(*obj).size.get()) as usize;
+ if index >= size {
+ panic!("Index out of bounds: the len is {size} but the index
is {index}");
}
let cell = ListObj::cell(obj, index);
let old = take_cell(cell);
write_cell(cell, value);
drop(old);
}
- Ok(())
}
/// Appends `value` to the end of the list.
@@ -427,16 +431,38 @@ impl<T: ContainerElement> List<T> {
unsafe { ListObj::truncate(self.obj(), len) }
}
- /// Removes the elements in `start..end`, shifting the ones after them.
- pub fn remove_range(&self, start: usize, end: usize) -> Result<()> {
+ /// Removes the elements in `range`, shifting the ones after them, as
+ /// [`Vec::drain`] does without returning them.
+ ///
+ /// # Panics
+ ///
+ /// Panics if `range` starts after it ends or ends past the list.
+ pub fn remove_range(&self, range: impl RangeBounds<usize>) {
let obj = self.obj();
unsafe {
let size = (*(*obj).size.get()) as usize;
- if start > end || end > size {
- return index_error("remove_range");
+ let start = match range.start_bound() {
+ Bound::Included(&start) => start,
+ Bound::Excluded(&start) => start
+ .checked_add(1)
+ .expect("attempted to index slice from after maximum
usize"),
+ Bound::Unbounded => 0,
+ };
+ let end = match range.end_bound() {
+ Bound::Included(&end) => end
+ .checked_add(1)
+ .expect("attempted to index slice up to maximum usize"),
+ Bound::Excluded(&end) => end,
+ Bound::Unbounded => size,
+ };
+ if start > end {
+ panic!("slice index starts at {start} but ends at {end}");
+ }
+ if end > size {
+ panic!("range end index {end} out of range for slice of length
{size}");
}
if start == end {
- return Ok(());
+ return;
}
// Rotate the removed cells to the back and drop them from there,
// so the list is consistent while each element is released.
@@ -444,7 +470,6 @@ impl<T: ContainerElement> List<T> {
.rotate_left(end - start);
ListObj::truncate(obj, size - (end - start));
}
- Ok(())
}
/// Inserts every item of `iter`, in order, at `index`, shifting the
diff --git a/rust/tvm-ffi/src/collections/map.rs
b/rust/tvm-ffi/src/collections/map.rs
index 5cec416b..511cf96d 100644
--- a/rust/tvm-ffi/src/collections/map.rs
+++ b/rust/tvm-ffi/src/collections/map.rs
@@ -96,8 +96,7 @@ fn element_from_any<T: ContainerElement>(value: Any) ->
Result<T> {
// that would shift every offset below.
#[repr(C)]
#[derive(Object)]
-#[type_key = "ffi.Map"]
-#[type_index(unsafe TypeIndex::kTVMFFIMap)]
+#[object(type_key = "ffi.Map", unsafe type_index = TypeIndex::kTVMFFIMap)]
pub struct MapObj {
pub(crate) object: Object,
/// Pointer to the (opaque) key/value storage region (`MapBaseObj::data_`).
diff --git a/rust/tvm-ffi/src/collections/shape.rs
b/rust/tvm-ffi/src/collections/shape.rs
index 6bbd5c43..f5a965c0 100644
--- a/rust/tvm-ffi/src/collections/shape.rs
+++ b/rust/tvm-ffi/src/collections/shape.rs
@@ -33,8 +33,7 @@ use tvm_ffi_sys::TVMFFITypeIndex as TypeIndex;
/// The object behind a [`Shape`].
#[repr(C)]
#[derive(Object)]
-#[type_key = "ffi.Shape"]
-#[type_index(unsafe TypeIndex::kTVMFFIShape)]
+#[object(type_key = "ffi.Shape", unsafe type_index = TypeIndex::kTVMFFIShape)]
pub struct ShapeObj {
object: Object,
data: TVMFFIShapeCell,
diff --git a/rust/tvm-ffi/src/collections/tensor.rs
b/rust/tvm-ffi/src/collections/tensor.rs
index df39460a..0cee1734 100644
--- a/rust/tvm-ffi/src/collections/tensor.rs
+++ b/rust/tvm-ffi/src/collections/tensor.rs
@@ -115,8 +115,7 @@ unsafe fn data_size(tensor: &DLTensor) -> usize {
/// The object behind a [`Tensor`]: an object header followed by a `DLTensor`.
#[repr(C)]
#[derive(Object)]
-#[type_key = "ffi.Tensor"]
-#[type_index(unsafe TypeIndex::kTVMFFITensor)]
+#[object(type_key = "ffi.Tensor", unsafe type_index =
TypeIndex::kTVMFFITensor)]
pub struct TensorObj {
object: Object,
dltensor: DLTensor,
diff --git a/rust/tvm-ffi/src/derive.rs b/rust/tvm-ffi/src/derive.rs
index 90f0b5ec..868514be 100644
--- a/rust/tvm-ffi/src/derive.rs
+++ b/rust/tvm-ffi/src/derive.rs
@@ -24,29 +24,51 @@
/// parent. Define a new type in Rust with
[`rs_object`](macro@crate::rs_object)
/// instead.
///
-/// `#[type_key = "..."]` names the type. `type_index()` takes the index the
+/// Without `register` or `type_index`, `type_index()` takes the index the
/// defining library registered for the key, and panics if that library is not
/// loaded yet, so a binding never registers a key with the wrong child slots.
/// Such a panic is final: `type_index()` of the type, and of every type under
/// it, keeps panicking after the library loads, so call it only once the
/// library is loaded. It also panics unless the registered key has the type's
/// parent and depth and, when the type table records a size, a size no
-/// smaller than the type's. `#[type_index(unsafe ...)]` gives the type a
-/// static index instead; the `unsafe` marks the caller's promise that the
-/// runtime gives that index to this type.
+/// smaller than the type's.
///
/// A binding that declares only part of its object must not be constructed:
/// keep its fields private.
///
-/// * `#[type_final]` - The type has no subtypes, as C++ `_type_final`.
-/// * `#[type_immutable]` - Implement
+/// # Options
+///
+/// `#[object(...)]` takes a comma-separated list, as in
+/// `#[object(type_key = "ffi.String", unsafe type_index =
TypeIndex::kTVMFFIStr)]`:
+///
+/// * `type_key = "..."` - The type key; required.
+/// * `unsafe type_index = expr` - A static index instead of the registered
+/// one; the `unsafe` marks the caller's promise that the runtime gives that
+/// index to this type.
+/// * `final` - The type has no subtypes, as C++ `_type_final`.
+/// * `immutable` - Implement
/// [`ImmutableObject`](crate::object::ImmutableObject), as for a C++ type
/// not registered as `_type_mutable`; every subtype must be immutable too.
-/// * `#[type_rs_parent]` - Implement `ShallowCopy` and
+/// * `rs_parent` - Implement `ShallowCopy` and
/// [`UnsafeInit`](crate::reflection::UnsafeInit), so an `rs_object` can
/// derive from the type. Each field other than the parent must be `Clone`
/// and implement [`AnyCompatible`](crate::AnyCompatible), be an
/// [`Any`](crate::Any) or have no drop glue.
+/// * `register` - The type is defined here: register its key under its
+/// parent when the library loads, as `rs_object` does.
+/// * `child_slots = N`, `child_slots_can_overflow = bool` - Type indices kept
+/// for subtypes, as C++ `_type_child_slots` and
+/// `_type_child_slots_can_overflow`; 0 and `true` by default. Needs
+/// `register`.
+/// * `s_eq_hash_kind = "TreeNode"` - The
+/// [`SEqHashKind`](crate::reflection::SEqHashKind) of structural equality;
+/// by default the parent's.
+/// * `reflection = path` - A `fn(&mut ObjectDef<Self>)` that registers the
+/// type's methods.
+///
+/// `s_eq_hash_kind`, `reflection` and field `#[reflect(...)]`, as
+/// [`rs_object`](macro@crate::rs_object) takes it, need `register` and no
+/// `type_index`.
pub use tvm_ffi_macros::Object;
/// Declare an object reference: the managed, reference-counted handle to an
diff --git a/rust/tvm-ffi/src/enum_ref.rs b/rust/tvm-ffi/src/enum_ref.rs
index 1d41a0ee..8f751493 100644
--- a/rust/tvm-ffi/src/enum_ref.rs
+++ b/rust/tvm-ffi/src/enum_ref.rs
@@ -33,8 +33,7 @@ use crate::{AnyView, Error, Result, String, VALUE_ERROR};
/// `base_: EnumObj`.
#[repr(C)]
#[derive(Object)]
-#[type_key = "ffi.Enum"]
-#[type_immutable]
+#[object(type_key = "ffi.Enum", immutable)]
pub struct EnumObj {
base_: Object,
// The registration order, which can differ between processes, so values
@@ -77,10 +76,7 @@ impl EnumObj {
///
/// #[repr(C)]
/// #[derive(Object)]
-/// #[type_key = "my_ext.Color"]
-/// #[type_register]
-/// #[type_final]
-/// #[type_immutable]
+/// #[object(type_key = "my_ext.Color", register, final, immutable)]
/// pub struct ColorObj {
/// base_: EnumObj,
/// }
diff --git a/rust/tvm-ffi/src/error.rs b/rust/tvm-ffi/src/error.rs
index c6b33f5e..57cae23e 100644
--- a/rust/tvm-ffi/src/error.rs
+++ b/rust/tvm-ffi/src/error.rs
@@ -31,8 +31,7 @@ use tvm_ffi_sys::{
/// The object behind an [`Error`]: its kind, message, backtrace, cause and
context.
#[repr(C)]
#[derive(Object)]
-#[type_key = "ffi.Error"]
-#[type_index(unsafe TVMFFITypeIndex::kTVMFFIError)]
+#[object(type_key = "ffi.Error", unsafe type_index =
TVMFFITypeIndex::kTVMFFIError)]
pub struct ErrorObj {
object: Object,
cell: TVMFFIErrorCell,
diff --git a/rust/tvm-ffi/src/extra/module.rs b/rust/tvm-ffi/src/extra/module.rs
index aff890dd..fbebcc88 100644
--- a/rust/tvm-ffi/src/extra/module.rs
+++ b/rust/tvm-ffi/src/extra/module.rs
@@ -32,8 +32,7 @@ use tvm_ffi_sys::TVMFFITypeIndex as TypeIndex;
/// The object behind a [`Module`].
#[repr(C)]
#[derive(Object)]
-#[type_key = "ffi.Module"]
-#[type_index(unsafe TypeIndex::kTVMFFIModule)]
+#[object(type_key = "ffi.Module", unsafe type_index =
TypeIndex::kTVMFFIModule)]
pub struct ModuleObj {
object: Object,
}
diff --git a/rust/tvm-ffi/src/extra/structural_eq_hash.rs
b/rust/tvm-ffi/src/extra/structural_eq_hash.rs
index cbf90b88..a60d1f8c 100644
--- a/rust/tvm-ffi/src/extra/structural_eq_hash.rs
+++ b/rust/tvm-ffi/src/extra/structural_eq_hash.rs
@@ -184,6 +184,5 @@ pub fn get_first_structural_mismatch_with<'l, 'r>(
crate::cached_global_func!("ffi.GetFirstStructuralMismatch")
.call_packed(&args)?
.try_into()?;
- pair.map(|pair| Ok((pair.get(0)?, pair.get(1)?)))
- .transpose()
+ Ok(pair.map(|pair| (pair[0].clone(), pair[1].clone())))
}
diff --git a/rust/tvm-ffi/src/extra/structural_mutate.rs
b/rust/tvm-ffi/src/extra/structural_mutate.rs
index 54913571..156b50bd 100644
--- a/rust/tvm-ffi/src/extra/structural_mutate.rs
+++ b/rust/tvm-ffi/src/extra/structural_mutate.rs
@@ -835,7 +835,7 @@ impl<D, Base: StructuralMutatorBase>
StructuralMutatorView<'_, D, Base> {
///
/// let root = Array::from_vec(vec![Array::from_vec(vec![1_i64, 2]),
Array::from_vec(vec![3_i64])]);
/// let result = Array::<Array<i64>>::try_from(structural_mutate(root,
Double)?)?;
-/// assert_eq!(result.get(0)?.iter().copied().collect::<Vec<_>>(), vec![2, 4]);
+/// assert_eq!(result[0].iter().copied().collect::<Vec<_>>(), vec![2, 4]);
/// # Ok::<(), tvm_ffi::Error>(())
/// ```
pub fn structural_mutate<R, H, M, D, O>(root: R, dispatcher: H) -> Result<Any>
diff --git a/rust/tvm-ffi/src/extra/structural_visit.rs
b/rust/tvm-ffi/src/extra/structural_visit.rs
index 74b70293..349ee77c 100644
--- a/rust/tvm-ffi/src/extra/structural_visit.rs
+++ b/rust/tvm-ffi/src/extra/structural_visit.rs
@@ -123,9 +123,11 @@ impl Default for StructuralVisitorObj {
/// Native `ffi.VisitInterrupt` object containing the interruption payload.
#[repr(C)]
#[derive(Object)]
-#[type_key = "ffi.VisitInterrupt"]
-#[type_index(unsafe TVMFFITypeIndex::kTVMFFIVisitInterrupt)]
-#[type_final]
+#[object(
+ type_key = "ffi.VisitInterrupt",
+ unsafe type_index = TVMFFITypeIndex::kTVMFFIVisitInterrupt,
+ final
+)]
pub struct VisitInterruptObj {
base_: Object,
/// Payload returned with the interrupt, or FFI `None` for no payload.
diff --git a/rust/tvm-ffi/src/extra/visit_error_context.rs
b/rust/tvm-ffi/src/extra/visit_error_context.rs
index f33a0c7b..965ca02b 100644
--- a/rust/tvm-ffi/src/extra/visit_error_context.rs
+++ b/rust/tvm-ffi/src/extra/visit_error_context.rs
@@ -33,8 +33,7 @@ use crate::tvm_ffi_sys::{TVMFFIAny, TVMFFIObject,
TVMFFITypeIndex};
/// visit unwound through, laid out as the C++ `VisitErrorContextObj`.
#[repr(C)]
#[derive(Object)]
-#[type_key = "ffi.VisitErrorContext"]
-#[type_final]
+#[object(type_key = "ffi.VisitErrorContext", final)]
pub(crate) struct VisitErrorContextObj {
base_: Object,
/// Visited nodes, innermost first; an entry may be `None`.
diff --git a/rust/tvm-ffi/src/function.rs b/rust/tvm-ffi/src/function.rs
index 4af45203..5f709ced 100644
--- a/rust/tvm-ffi/src/function.rs
+++ b/rust/tvm-ffi/src/function.rs
@@ -34,8 +34,7 @@ use tvm_ffi_sys::{
/// The object behind a [`Function`].
#[repr(C)]
#[derive(Object)]
-#[type_key = "ffi.Function"]
-#[type_index(unsafe TVMFFITypeIndex::kTVMFFIFunction)]
+#[object(type_key = "ffi.Function", unsafe type_index =
TVMFFITypeIndex::kTVMFFIFunction)]
pub struct FunctionObj {
object: Object,
cell: TVMFFIFunctionCell,
diff --git a/rust/tvm-ffi/src/lib.rs b/rust/tvm-ffi/src/lib.rs
index ed3fd393..8dd0421a 100644
--- a/rust/tvm-ffi/src/lib.rs
+++ b/rust/tvm-ffi/src/lib.rs
@@ -295,7 +295,7 @@ pub use tvm_ffi_macros::match_any;
/// field's type must take all-zero bytes as a value. An optional field is an
/// `Option` of an object reference or an [`Optional`] of any other value. The
parent's object must
/// implement `ShallowCopy` and `UnsafeInit`, as `rs_object` types and
-/// `#[type_rs_parent]` bindings, such as the non-final ones `tvm-ffi-stubgen`
+/// `#[object(rs_parent)]` bindings, such as the non-final ones
`tvm-ffi-stubgen`
/// generates, do.
///
/// # Registration
diff --git a/rust/tvm-ffi/src/object.rs b/rust/tvm-ffi/src/object.rs
index 70234aaf..a937e6f3 100644
--- a/rust/tvm-ffi/src/object.rs
+++ b/rust/tvm-ffi/src/object.rs
@@ -334,7 +334,7 @@ impl<T: ObjectCore> Clone for ObjectArc<T> {
/// `Send + Sync` when `T`'s fields are.
///
/// Implement it with the `immutable` option of [`rs_object`](crate::rs_object)
-/// or `#[type_immutable]` on [`derive(Object)`](crate::derive::Object), which
+/// or `#[object(immutable)]` on [`derive(Object)`](crate::derive::Object),
which
/// reject `writable` fields.
///
/// # Safety
diff --git a/rust/tvm-ffi/src/reflection/access_path.rs
b/rust/tvm-ffi/src/reflection/access_path.rs
index 3c65e44b..9eb2bcd5 100644
--- a/rust/tvm-ffi/src/reflection/access_path.rs
+++ b/rust/tvm-ffi/src/reflection/access_path.rs
@@ -48,8 +48,7 @@ pub enum AccessKind {
/// The object behind an [`AccessStep`], laid out as the C++ `AccessStepObj`.
#[repr(C)]
#[derive(Object)]
-#[type_key = "ffi.reflection.AccessStep"]
-#[type_final]
+#[object(type_key = "ffi.reflection.AccessStep", final)]
pub struct AccessStepObj {
object: Object,
/// What the step accesses.
@@ -68,8 +67,7 @@ pub struct AccessStep {
/// The object behind an [`AccessPath`], laid out as the C++ `AccessPathObj`.
#[repr(C)]
#[derive(Object)]
-#[type_key = "ffi.reflection.AccessPath"]
-#[type_final]
+#[object(type_key = "ffi.reflection.AccessPath", final)]
pub struct AccessPathObj {
object: Object,
/// The path without its last step; `None` at the root.
diff --git a/rust/tvm-ffi/src/reflection/enum_def.rs
b/rust/tvm-ffi/src/reflection/enum_def.rs
index 15996a1d..63278daf 100644
--- a/rust/tvm-ffi/src/reflection/enum_def.rs
+++ b/rust/tvm-ffi/src/reflection/enum_def.rs
@@ -57,10 +57,7 @@ use crate::{Any, AnyView, EnumRef, Error, List, Result,
String, TYPE_ERROR, VALU
///
/// #[repr(C)]
/// #[derive(Object)]
-/// #[type_key = "my_ext.Color"]
-/// #[type_register]
-/// #[type_final]
-/// #[type_immutable]
+/// #[object(type_key = "my_ext.Color", register, final, immutable)]
/// pub struct ColorObj {
/// base_: EnumObj,
/// }
@@ -294,8 +291,7 @@ const ENUM_STATE_ATTR: &str = "__ffi_enum__";
/// columns (`Dict<String, Dict<ObjectRef, Any>>`).
#[repr(C)]
#[derive(Object)]
-#[type_key = "ffi.EnumState"]
-#[type_final]
+#[object(type_key = "ffi.EnumState", final)]
pub(crate) struct EnumStateObj {
base_: Object,
entries: List<Any>,
diff --git a/rust/tvm-ffi/src/reflection/registry.rs
b/rust/tvm-ffi/src/reflection/registry.rs
index 36d61ed2..05595700 100644
--- a/rust/tvm-ffi/src/reflection/registry.rs
+++ b/rust/tvm-ffi/src/reflection/registry.rs
@@ -57,7 +57,7 @@ use crate::{Any, Error, Function, Result, INTERNAL_ERROR,
TYPE_ERROR, VALUE_ERRO
/// `__ffi_shallow_copy__` returns; fields that are references share their
/// objects, as `Clone` of an `Arc` does.
///
-/// [`rs_object`](crate::rs_object) and `#[type_rs_parent]` on
+/// [`rs_object`](crate::rs_object) and `#[object(rs_parent)]` on
/// [`derive(Object)`](crate::derive::Object) implement it by copying the
/// parent with its `ShallowCopy` and cloning every other field. An
/// `rs_object` needs its parent's object to implement it.
diff --git a/rust/tvm-ffi/src/reflection/unsafe_init.rs
b/rust/tvm-ffi/src/reflection/unsafe_init.rs
index a9b1c9e9..d11fea85 100644
--- a/rust/tvm-ffi/src/reflection/unsafe_init.rs
+++ b/rust/tvm-ffi/src/reflection/unsafe_init.rs
@@ -48,7 +48,7 @@ use crate::{Error, INTERNAL_ERROR};
/// [`unsafe_init`], which a JSON graph read back, `__ffi_init__` and
/// `__ffi_new__` then fill. `#[rs_object]` implements it for its object, so
the
/// parent's object must implement it too: [`Object`] does, and so does a
-/// binding with `#[type_rs_parent]` on its
[`derive(Object)`](crate::derive::Object).
+/// binding with `#[object(rs_parent)]` on its
[`derive(Object)`](crate::derive::Object).
///
/// `__ffi_init__` leaves an `init_off` field as the creator made it, so such a
/// field's type must take all-zero bytes as a value, as a scalar, `Option<T>`
diff --git a/rust/tvm-ffi/src/string.rs b/rust/tvm-ffi/src/string.rs
index 461d6e5c..2eba2150 100644
--- a/rust/tvm-ffi/src/string.rs
+++ b/rust/tvm-ffi/src/string.rs
@@ -54,8 +54,7 @@ unsafe impl Sync for Bytes {}
// BytesObj for heap-allocated bytes
#[repr(C)]
#[derive(Object)]
-#[type_key = "ffi.Bytes"]
-#[type_index(unsafe TypeIndex::kTVMFFIBytes)]
+#[object(type_key = "ffi.Bytes", unsafe type_index = TypeIndex::kTVMFFIBytes)]
pub(crate) struct BytesObj {
object: Object,
data: TVMFFIByteArray,
@@ -274,8 +273,7 @@ unsafe impl Sync for String {}
// StringObj for heap-allocated string
#[repr(C)]
#[derive(Object)]
-#[type_key = "ffi.String"]
-#[type_index(unsafe TypeIndex::kTVMFFIStr)]
+#[object(type_key = "ffi.String", unsafe type_index = TypeIndex::kTVMFFIStr)]
pub(crate) struct StringObj {
object: Object,
data: TVMFFIByteArray,
diff --git a/rust/tvm-ffi/tests/test_array.rs b/rust/tvm-ffi/tests/test_array.rs
index 7780ec83..9c003e33 100644
--- a/rust/tvm-ffi/tests/test_array.rs
+++ b/rust/tvm-ffi/tests/test_array.rs
@@ -181,21 +181,19 @@ fn test_array_copy_on_write() {
let mut array = Array::from_vec(vec![s("a"), s("b"), s("c")]);
let shared = array.clone();
- array.set(0, s("x")).unwrap();
+ array.set(0, s("x"));
assert_ne!(ptr(&array), ptr(&shared));
assert_eq!(values(&shared), ["a", "b", "c"]);
let unique = ptr(&array);
- array.set(1, s("y")).unwrap();
+ array.set(1, s("y"));
array.truncate(2);
assert_eq!(array.pop().unwrap().as_str(), "y");
assert_eq!(ptr(&array), unique);
array.push(s("z"));
- array.insert(0, s("w")).unwrap();
+ array.insert(0, s("w"));
assert_eq!(values(&array), ["w", "x", "z"]);
- assert!(array.set(3, s("q")).is_err());
- assert!(array.insert(4, s("q")).is_err());
assert!(Array::<String>::new().pop().is_none());
}
diff --git a/rust/tvm-ffi/tests/test_cast.rs b/rust/tvm-ffi/tests/test_cast.rs
index a1399c59..ce6ab003 100644
--- a/rust/tvm-ffi/tests/test_cast.rs
+++ b/rust/tvm-ffi/tests/test_cast.rs
@@ -28,9 +28,7 @@ use tvm_ffi::*;
// must have repr(C) for the object header to stay in the same position
#[repr(C)]
#[derive(Object)]
-#[type_key = "testing.rust.CastBase"]
-#[type_register]
-#[type_immutable]
+#[object(type_key = "testing.rust.CastBase", register, immutable)]
struct TestBaseObj {
base_: Object,
value: i64,
@@ -52,10 +50,7 @@ struct TestBase {
#[repr(C)]
#[derive(Object)]
-#[type_key = "testing.rust.CastDerived"]
-#[type_register]
-#[type_final]
-#[type_immutable]
+#[object(type_key = "testing.rust.CastDerived", register, final, immutable)]
struct TestDerivedObj {
base_: TestBaseObj,
extra: i64,
@@ -127,7 +122,7 @@ fn test_is_instance_of() {
// Bindings of the C++ testing.TestObjectBase and testing.TestObjectDerived.
#[repr(C)]
#[derive(Object)]
-#[type_key = "testing.TestObjectBase"]
+#[object(type_key = "testing.TestObjectBase")]
struct CxxTestBaseObj {
base_: Object,
v_i64: i64,
@@ -137,8 +132,7 @@ struct CxxTestBaseObj {
#[repr(C)]
#[derive(Object)]
-#[type_key = "testing.TestObjectDerived"]
-#[type_final]
+#[object(type_key = "testing.TestObjectDerived", final)]
struct CxxTestDerivedObj {
base_: CxxTestBaseObj,
v_map: Map<Any, Any>,
diff --git a/rust/tvm-ffi/tests/test_enum.rs b/rust/tvm-ffi/tests/test_enum.rs
index 7aef0428..8c9baba9 100644
--- a/rust/tvm-ffi/tests/test_enum.rs
+++ b/rust/tvm-ffi/tests/test_enum.rs
@@ -27,9 +27,7 @@ use tvm_ffi::{EnumRef, ObjectArc, ObjectRefCore, Result,
VALUE_ERROR};
#[repr(C)]
#[derive(Object)]
-#[type_key = "testing.TestEnumVariant"]
-#[type_final]
-#[type_immutable]
+#[object(type_key = "testing.TestEnumVariant", final, immutable)]
struct TestEnumVariantObj {
base_: EnumObj,
}
diff --git a/rust/tvm-ffi/tests/test_list.rs b/rust/tvm-ffi/tests/test_list.rs
index 30f4e334..6a7a87a7 100644
--- a/rust/tvm-ffi/tests/test_list.rs
+++ b/rust/tvm-ffi/tests/test_list.rs
@@ -78,9 +78,8 @@ fn test_list_mutation() {
assert!(list.capacity() >= 100);
assert_eq!(list.get(99).unwrap(), 99);
- list.set(0, -1).unwrap();
+ list.set(0, -1);
assert_eq!(list.get(0).unwrap(), -1);
- assert!(list.set(100, 0).is_err());
list.insert(1, 7).unwrap();
assert_eq!(list.get(1).unwrap(), 7);
@@ -147,7 +146,7 @@ fn test_list_releases_elements() {
list.insert(0, array.clone()).unwrap();
assert_eq!(probe.debug_strong_count().unwrap(), base + 3);
- list.set(0, Array::from_vec(vec![2i64])).unwrap();
+ list.set(0, Array::from_vec(vec![2i64]));
assert_eq!(probe.debug_strong_count().unwrap(), base + 2);
drop(list.remove(1).unwrap());
assert_eq!(probe.debug_strong_count().unwrap(), base + 1);
@@ -158,7 +157,7 @@ fn test_list_releases_elements() {
list.extend([array.clone(), array.clone(), array.clone()])
.unwrap();
assert_eq!(probe.debug_strong_count().unwrap(), base + 3);
- list.remove_range(0, 2).unwrap();
+ list.remove_range(0..2);
assert_eq!(probe.debug_strong_count().unwrap(), base + 1);
list.reverse();
list.truncate(0);
@@ -268,12 +267,10 @@ fn test_list_bulk_edits() {
list.extend(std::iter::empty()).unwrap();
assert!(list.insert_many(list.len() + 1, [0i64]).is_err());
- list.remove_range(2, 4).unwrap();
+ list.remove_range(2..4);
assert_eq!(list.len(), len);
assert_eq!(list.get(2).unwrap(), 2);
- list.remove_range(1, 1).unwrap();
- assert!(list.remove_range(3, 2).is_err());
- assert!(list.remove_range(0, list.len() + 1).is_err());
+ list.remove_range(1..1);
list.truncate(3);
assert_eq!(
@@ -390,7 +387,7 @@ fn test_list_mismatched_element_is_reported() {
// Another handle to the same object puts a `String` where an `i64` is
expected.
let ints: List<i64> = [1i64, 2, 3].into_iter().collect();
let as_any: List<Any> = Any::from(ints.clone()).try_into().unwrap();
- as_any.set(1, Any::from(String::from("x"))).unwrap();
+ as_any.set(1, Any::from(String::from("x")));
// `get` and the iterator report that element instead of truncating.
assert!(ints.get(1).is_err());
diff --git a/rust/tvm-ffi/tests/test_match_any_ast_lookup.rs
b/rust/tvm-ffi/tests/test_match_any_ast_lookup.rs
index a06bee7d..e94e2869 100644
--- a/rust/tvm-ffi/tests/test_match_any_ast_lookup.rs
+++ b/rust/tvm-ffi/tests/test_match_any_ast_lookup.rs
@@ -25,10 +25,12 @@ use tvm_ffi::{
#[repr(C)]
#[derive(Object)]
-#[type_key = "testing.match_any.Expr"]
-#[type_register]
-#[type_child_slots = 20]
-#[type_child_slots_can_overflow = false]
+#[object(
+ type_key = "testing.match_any.Expr",
+ register,
+ child_slots = 20,
+ child_slots_can_overflow = false
+)]
struct ExprObj {
base_: ObjectBase,
}
@@ -44,9 +46,7 @@ macro_rules! define_expr_leaves {
$(
#[repr(C)]
#[derive(Object)]
- #[type_key = $type_key]
- #[type_register]
- #[type_final]
+ #[object(type_key = $type_key, register, final)]
struct $object {
base_: ExprObj,
}
diff --git a/rust/tvm-ffi/tests/test_object_reflection.rs
b/rust/tvm-ffi/tests/test_object_reflection.rs
index 669a35a6..7d0f54cf 100644
--- a/rust/tvm-ffi/tests/test_object_reflection.rs
+++ b/rust/tvm-ffi/tests/test_object_reflection.rs
@@ -25,16 +25,18 @@ use tvm_ffi_sys::{
TVMFFIMethodInfo, TVMFFISEqHashKind, TVMFFITypeInfo,
};
-// Rust-defined types whose `#[reflect]` fields and `#[type_reflection]`
+// Rust-defined types whose `#[reflect]` fields and `reflection`
// methods reach the type table, so that the C++ structural engines, `repr`,
// deep copy and other languages see them.
#[repr(C)]
#[derive(Object)]
-#[type_key = "testing.rust.ReflectedPoint"]
-#[type_register]
-#[type_s_eq_hash_kind = "TreeNode"]
-#[type_reflection(PointObj::reflect_methods)]
+#[object(
+ type_key = "testing.rust.ReflectedPoint",
+ register,
+ s_eq_hash_kind = "TreeNode",
+ reflection = PointObj::reflect_methods
+)]
struct PointObj {
base_: Object,
/// The x coordinate.
@@ -81,9 +83,7 @@ struct Point {
#[repr(C)]
#[derive(Object)]
-#[type_key = "testing.rust.ReflectedPoint3"]
-#[type_register]
-#[type_final]
+#[object(type_key = "testing.rust.ReflectedPoint3", register, final)]
struct Point3Obj {
base_: PointObj,
#[reflect]
@@ -297,10 +297,12 @@ fn test_methods_are_called_through_the_type_table() {
#[repr(C)]
#[derive(Object)]
-#[type_key = "testing.rust.ReflectedTally"]
-#[type_register]
-#[type_final]
-#[type_reflection(Self::reflect_methods)]
+#[object(
+ type_key = "testing.rust.ReflectedTally",
+ register,
+ final,
+ reflection = Self::reflect_methods
+)]
struct TallyObj {
base_: Object,
count: i64,
@@ -477,9 +479,7 @@ fn test_shallow_copy_and_finality_are_type_attributes() {
#[repr(C)]
#[derive(Object)]
-#[type_key = "testing.rust.Unreflected"]
-#[type_register]
-#[type_final]
+#[object(type_key = "testing.rust.Unreflected", register, final)]
struct CounterObj {
base_: Object,
count: i64,
@@ -505,8 +505,7 @@ fn test_types_without_reflect_register_no_reflection() {
mod no_imports {
#[repr(C)]
#[derive(tvm_ffi::derive::Object)]
- #[type_key = "testing.rust.ReflectedNoImports"]
- #[type_register]
+ #[object(type_key = "testing.rust.ReflectedNoImports", register)]
pub(super) struct LeafObj {
base_: super::PointObj,
#[reflect]
@@ -538,9 +537,7 @@ fn test_derives_need_nothing_in_scope() {
#[repr(C)]
#[derive(Object)]
-#[type_key = "testing.rust.DuplicateMethod"]
-#[type_register]
-#[type_reflection(Self::reflect_methods)]
+#[object(type_key = "testing.rust.DuplicateMethod", register, reflection =
Self::reflect_methods)]
struct DuplicateMethodObj {
base_: Object,
}
diff --git a/rust/tvm-ffi/tests/test_object_registration.rs
b/rust/tvm-ffi/tests/test_object_registration.rs
index 3966b457..a18dd0b4 100644
--- a/rust/tvm-ffi/tests/test_object_registration.rs
+++ b/rust/tvm-ffi/tests/test_object_registration.rs
@@ -22,15 +22,17 @@ use tvm_ffi::*;
use tvm_ffi_sys::{TVMFFIByteArray, TVMFFIGetTypeInfo, TVMFFITypeKeyToIndex};
// Type keys that nothing else registers: deriving `Object` with
-// `#[type_register]` registers them when the test binary loads, under their
+// `#[object(register)]` registers them when the test binary loads, under their
// parents, as C++ `TVM_FFI_DECLARE_OBJECT_INFO` does.
#[repr(C)]
#[derive(Object)]
-#[type_key = "testing.rust.RegisteredBase"]
-#[type_register]
-#[type_child_slots = 2]
-#[type_child_slots_can_overflow = false]
+#[object(
+ type_key = "testing.rust.RegisteredBase",
+ register,
+ child_slots = 2,
+ child_slots_can_overflow = false
+)]
struct RegisteredBaseObj {
base_: Object,
value: i64,
@@ -44,9 +46,7 @@ struct RegisteredBase {
#[repr(C)]
#[derive(Object)]
-#[type_key = "testing.rust.RegisteredLeaf"]
-#[type_register]
-#[type_final]
+#[object(type_key = "testing.rust.RegisteredLeaf", register, final)]
struct RegisteredLeafObj {
base_: RegisteredBaseObj,
extra: i64,
@@ -111,10 +111,10 @@ fn test_registered_object_types_round_trip_through_any() {
}
// A type that binds a key its defining library registers, without
-// `#[type_register]`, does not register it.
+// `#[object(register)]`, does not register it.
#[repr(C)]
#[derive(Object)]
-#[type_key = "testing.rust.NeverRegistered"]
+#[object(type_key = "testing.rust.NeverRegistered")]
struct UnregisteredBindingObj {
base_: Object,
}
diff --git a/rust/tvm-ffi/tests/test_rs_object.rs
b/rust/tvm-ffi/tests/test_rs_object.rs
index e648198e..9f68e23e 100644
--- a/rust/tvm-ffi/tests/test_rs_object.rs
+++ b/rust/tvm-ffi/tests/test_rs_object.rs
@@ -108,8 +108,7 @@ pub struct RsScalars {
#[repr(C)]
#[derive(Object)]
-#[type_key = "testing.TestCxxClassBase"]
-#[type_rs_parent]
+#[object(type_key = "testing.TestCxxClassBase", rs_parent)]
pub struct TestCxxClassBaseObj {
base_: Object,
pub v_i64: i64,
diff --git a/rust/tvm-ffi/tests/test_structural_mutate.rs
b/rust/tvm-ffi/tests/test_structural_mutate.rs
index 36b0e6c2..61b7ea95 100644
--- a/rust/tvm-ffi/tests/test_structural_mutate.rs
+++ b/rust/tvm-ffi/tests/test_structural_mutate.rs
@@ -189,9 +189,11 @@ impl RecursiveEntryMutator {
#[repr(C)]
#[derive(tvm_ffi::derive::Object)]
-#[type_key = "testing.rust.MutationRemapNode"]
-#[type_register]
-#[type_s_eq_hash_kind = "DAGNode"]
+#[object(
+ type_key = "testing.rust.MutationRemapNode",
+ register,
+ s_eq_hash_kind = "DAGNode"
+)]
struct RemapNodeObj {
base_: Object,
#[reflect]
@@ -206,8 +208,7 @@ struct RemapNode {
#[repr(C)]
#[derive(tvm_ffi::derive::Object)]
-#[type_key = "testing.rust.MutationFieldNode"]
-#[type_register]
+#[object(type_key = "testing.rust.MutationFieldNode", register)]
struct FieldNodeObj {
base_: Object,
a: Array<i64>,
@@ -704,7 +705,7 @@ fn owned_entry_mode_is_forwarded_by_generated_callbacks() {
assert_eq!(owner.debug_strong_count(), Some(count));
let result = change.value_or_unchanged(owner);
if let Some(alias) = alias {
- assert_eq!(Array::<i64>::try_from(alias)?.get(0)?, 1);
+ assert_eq!(Array::<i64>::try_from(alias)?[0], 1);
}
Ok(UnchangedOr::changed(result))
})
@@ -917,7 +918,7 @@ fn
consuming_default_descent_transfers_between_contexts_without_node_borrows() {
structural_mutate(true, &mut inner)?;
let result = inner.result;
if let Some(alias) = alias {
- assert_eq!(alias.get(0)?, 1);
+ assert_eq!(alias[0], 1);
}
result.try_into()
}
@@ -1118,7 +1119,7 @@ fn
callback_errors_preserve_message_and_add_object_context() {
);
#[repr(C)]
#[derive(tvm_ffi::derive::Object)]
- #[type_key = "ffi.VisitErrorContext"]
+ #[object(type_key = "ffi.VisitErrorContext")]
struct VisitErrorContextObj {
base_: Object,
}
diff --git a/tests/python/test_stubgen_rust.py
b/tests/python/test_stubgen_rust.py
index 1406698e..c42c1da6 100644
--- a/tests/python/test_stubgen_rust.py
+++ b/tests/python/test_stubgen_rust.py
@@ -377,7 +377,7 @@ pub struct Pair {{
{NO_METADATA}
#[repr(C)]
#[derive(tvm_ffi::derive::Object)]
-#[type_key = "demo.Pair"]
+#[object(type_key = "demo.Pair")]
pub struct PairObj {{
base_: Object,
}}
@@ -445,14 +445,14 @@ def test_render_root_object() -> None:
def test_render_object_without_fields_has_no_impl() -> None:
text, imports = _render(_info("demo.Marker", is_final=True))
- assert "#[type_final]" in text
+ assert '#[object(type_key = "demo.Marker", final)]' in text
assert "impl MarkerObj" not in text
assert "FieldGetter" not in _uses(imports)
def test_render_thread_safe_only_when_not_mutable() -> None:
"""A class C++ declares not ``_type_mutable`` is ``ImmutableObject``;
others are not."""
- marker = "#[type_immutable]"
+ marker = ", immutable)]"
assert marker in _render(_info("demo.Marker", is_final=True,
is_mutable=False))[0]
assert marker not in _render(_info("demo.Marker", is_final=True,
is_mutable=True))[0]
assert marker not in _render(_info("demo.Marker", is_final=True))[0]
@@ -468,7 +468,7 @@ def test_render_derived_object_same_module() -> None:
is_final=True,
)
text, imports = _render(info)
- assert "#[type_final]\npub struct AddObj {\n base_: ExprObj,\n}" in text
+ assert ", 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::impl_object_ref_upcast!(Add => BaseExpr,
Add => Expr);")
@@ -525,7 +525,7 @@ use tvm_ffi::enum_ref::EnumObj;
#[allow(dead_code)]
#[repr(C)]
#[derive(tvm_ffi::derive::Object)]
-#[type_key = "ffi.IntEnum"]
+#[object(type_key = "ffi.IntEnum")]
pub struct FfiIntEnumObj {
base_: EnumObj,
}
@@ -535,7 +535,7 @@ pub struct FfiIntEnumObj {
#[allow(dead_code)]
#[repr(C)]
#[derive(tvm_ffi::derive::Object)]
-#[type_key = "ffi.StrEnum"]
+#[object(type_key = "ffi.StrEnum")]
pub struct FfiStrEnumObj {
base_: EnumObj,
}"""
@@ -601,8 +601,7 @@ impl TryFrom<i64> for IterVarType {{
{NO_METADATA}
#[repr(C)]
#[derive(tvm_ffi::derive::Object)]
-#[type_key = "tirx.IterVar"]
-#[type_final]
+#[object(type_key = "tirx.IterVar", final)]
pub struct IterVarObj {{
base_: PrimExprConvertibleObj,
}}
@@ -758,8 +757,7 @@ pub struct Expr {
/// Complete: reflected fields fill [24, 40) exactly.
#[repr(C)]
#[derive(tvm_ffi::derive::Object)]
-#[type_key = "ir.Expr"]
-#[type_rs_parent]
+#[object(type_key = "ir.Expr", rs_parent)]
pub struct ExprObj {
base_: Object,
pub span: Option<Span>,
@@ -798,8 +796,7 @@ pub struct Add {
/// Complete: reflected fields fill [40, 56) exactly.
#[repr(C)]
#[derive(tvm_ffi::derive::Object)]
-#[type_key = "tirx.Add"]
-#[type_final]
+#[object(type_key = "tirx.Add", final)]
pub struct AddObj {
base_: ExprObj,
pub a: PrimExpr,
@@ -1029,7 +1026,7 @@ def test_final_type_has_no_unsafe_init() -> None:
"""No Rust type derives from a final type, which needs no `UnsafeInit`."""
info = _info("demo.Leaf", (_field("a", "int", 24, 8),), total_size=32,
is_final=True)
text, _ = _render(info)
- assert "#[type_rs_parent]" not in text
+ assert ", rs_parent)]" not in text
def test_complete_list_field_mirrors() -> None:
@@ -1495,7 +1492,7 @@ def test_registry_complete_chain_is_mirrored() -> None:
" TestCxxClassDerivedDerivedObj::new(v_i64, v_i32, v_f64,
v_f32, v_str.into(), v_bool);\n"
) in dd
# Not final, under generated ancestors: a Rust subtype can copy its
parent's object.
- assert "#[type_rs_parent]\npub struct TestCxxClassDerivedDerivedObj {" in
dd
+ assert ", rs_parent)]\npub struct TestCxxClassDerivedDerivedObj {" in dd
assert dd.endswith(
"tvm_ffi::impl_object_ref_upcast!(TestCxxClassDerivedDerived =>
TestCxxClassBase, "
"TestCxxClassDerivedDerived => TestCxxClassDerived);"
@@ -1563,7 +1560,7 @@ def test_cli_init_generates_a_module_tree(tmp_path: Path,
monkeypatch: pytest.Mo
assert (
"use tvm_ffi::reflection::FieldGetter;" in text
) # the opaque fixtures read through getters
- assert '#[type_key = "testing.TestCxxClassDerivedDerived"]' in text
+ assert '#[object(type_key = "testing.TestCxxClassDerivedDerived"' in text
# Without a directive for the narrow `v_i32`, the chain is opaque.
assert (
"pub struct TestCxxClassDerivedDerivedObj {\n base_:
TestCxxClassDerivedObj,\n}" in text
@@ -1888,7 +1885,7 @@ def
test_stage_3_checks_dependencies_across_the_run(tmp_path: Path) -> None:
assert "use crate::hand::TestCxxClassBaseObj;" in text
# Not final: public `new`; a hand-written parent may lack `ShallowCopy`,
so the type has none.
assert "impl TestCxxClassDerivedObj {\n pub fn new(" in text
- assert "#[type_rs_parent]" not in text
+ assert ", rs_parent)]" not in text
# ---------------------------------------------------------------------------