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 3598d6f8 [Rust] Optional Send/Sync, is::<T>(), type_key(), prelude,
weak_count as std (#964)
3598d6f8 is described below
commit 3598d6f84d8604dcf641ddc235340c19a0f74843
Author: Tianqi Chen <[email protected]>
AuthorDate: Sun Nov 8 11:18:33 2026 -0500
[Rust] Optional Send/Sync, is::<T>(), type_key(), prelude, weak_count as
std (#964)
- `Optional<T>` is `Send`/`Sync` when `T` is, like `Array<T>` and
`Map<K, V>`.
- `is::<T>()` on `Any`, `AnyView`, `MutationView` and object references:
the strict type test of `downcast_ref`, as std `Any::is`, covering every
`AnyCompatible` type including scalars.
- `type_key()` on `Any`, `AnyView` and object references, as C++
`GetTypeKey`.
- `ObjectArc::weak_count` matches `Arc::weak_count`: the weak reference
the strong references share is not counted.
- `object::unsafe_::inc_ref` is crate-private like `dec_ref`;
`ObjectDerivesFrom` and `marker`, named only by macro expansions, are
`doc(hidden)`.
- `Display for Error` prints `Kind: message`, as C++ `what()`; `Debug`
keeps the traceback, without its leading blank line.
- `format_err!(kind, fmt, args...)` makes an `Error` with the source
location `bail!` records, without returning; `bail!` returns it.
- `Error::new(kind, message)`; the backtrace, cause and extra context
are set with `with_backtrace`, `with_cause` and `with_extra_context`,
copy-on-write like `with_appended_backtrace`. They replace
`new_with_cause_and_extra_context`.
- `ErrorKind` is `Copy` and `Hash`. Callers use `Error::from_panic`
directly; the hidden `function_internal::panic_to_error` is removed.
- `String` and `Bytes` replace the blanket `From`/`PartialEq` impls over
`AsRef<str>`/`AsRef<[u8]>` with concrete ones, as std `String` and
`Vec<u8>` have. This lets `String` implement `AsRef<str>` and
`Borrow<str>`, and `Bytes` `AsRef<[u8]>` and `Borrow<[u8]>`. Comparisons
work in both directions, and both convert into std `String`/`Vec<u8>`.
- `Debug` for `String` and `Bytes` prints as `str` and `[u8]` do;
`Bytes` no longer implements `Display`.
- `NDAllocator` is `Send + Sync`: a tensor frees its data through the
allocator when the last reference drops, which may be on any thread.
- `FieldGetter::of::<N>(name)` resolves a reflected field against `N`,
so reads take `&N` without a per-object subtype check;
`cached_field_getter!(N, "f")` caches one per call site, as
`cached_global_func!` does. The Rust stub generator emits it for field
accessors, and its enum conversions use the two-argument `Error::new`.
- `TypeAttrDef::attr`/`native_fn_attr` are renamed
`set_attr`/`set_native_fn_attr`, as `EnumDef::set_attr`.
- `#[rs_object(type_key = "...", ...)]` names the type key, as
`#[object(type_key = ...)]` does, and takes `crate = path`; a positional
key is a compile error that shows the named form.
- `into_typed_fn!(f, Fn(..) -> R)` accepts only `Fn` in the trait
position; any other name is a compile error.
- `Function::get_global` reports a missing function as `ValueError`, as
C++ `Function::GetGlobalRequired`.
- `AsPackedCallable`, the bound of `Function::from_typed` and
`register_global_typed`, is re-exported and documented under
`tvm_ffi::function`, with the accepted parameter types.
- `MutationView::with_inplace_mode` is renamed `narrow_inplace_mode`: it
only restricts the mode, never upgrading it.
---
docs/guides/rust_lang_guide.rst | 16 +--
.../rust/src/generated/rust_stubgen/mod.rs | 2 +-
python/tvm_ffi/stub/rust_generator/codegen.py | 79 ++++++-----
python/tvm_ffi/stub/rust_generator/consts.py | 2 +
rust/tvm-ffi-macros/src/rs_object.rs | 34 +++--
rust/tvm-ffi/src/any.rs | 53 ++++++-
rust/tvm-ffi/src/big_int/int_ops.rs | 6 +-
rust/tvm-ffi/src/big_int/mod.rs | 15 +-
rust/tvm-ffi/src/collections/map.rs | 2 +-
rust/tvm-ffi/src/collections/tensor.rs | 7 +-
rust/tvm-ffi/src/derive.rs | 6 +-
rust/tvm-ffi/src/enum_ref.rs | 1 -
rust/tvm-ffi/src/error.rs | 126 ++++++++++-------
rust/tvm-ffi/src/extra/module.rs | 18 +--
rust/tvm-ffi/src/extra/structural_common.rs | 3 +-
rust/tvm-ffi/src/extra/structural_eq_hash.rs | 2 +-
rust/tvm-ffi/src/extra/structural_mutate.rs | 28 ++--
rust/tvm-ffi/src/extra/structural_visit.rs | 1 -
rust/tvm-ffi/src/function.rs | 16 ++-
rust/tvm-ffi/src/function_internal.rs | 16 +--
rust/tvm-ffi/src/lib.rs | 6 +-
rust/tvm-ffi/src/macros.rs | 110 ++++++++++++---
rust/tvm-ffi/src/object.rs | 47 ++++++-
rust/tvm-ffi/src/optional.rs | 5 +
rust/tvm-ffi/src/reflection/accessor.rs | 146 ++++++++++++-------
rust/tvm-ffi/src/reflection/enum_def.rs | 4 -
rust/tvm-ffi/src/reflection/native_function.rs | 13 +-
rust/tvm-ffi/src/reflection/registry.rs | 21 ++-
rust/tvm-ffi/src/reflection/unsafe_init.rs | 2 +-
rust/tvm-ffi/src/rvalue_ref.rs | 1 -
rust/tvm-ffi/src/static_init.rs | 7 +-
rust/tvm-ffi/src/string.rs | 156 +++++++++++++++------
rust/tvm-ffi/tests/test_device.rs | 2 +-
rust/tvm-ffi/tests/test_error.rs | 17 +--
rust/tvm-ffi/tests/test_function_metadata.rs | 2 +-
rust/tvm-ffi/tests/test_object.rs | 14 +-
rust/tvm-ffi/tests/test_object_ref_any.rs | 2 +-
rust/tvm-ffi/tests/test_object_reflection.rs | 16 ++-
rust/tvm-ffi/tests/test_rs_object.rs | 22 +--
rust/tvm-ffi/tests/test_static_init.rs | 2 +-
rust/tvm-ffi/tests/test_string.rs | 5 +-
rust/tvm-ffi/tests/test_structural_mutate.rs | 39 +++---
rust/tvm-ffi/tests/test_structural_visit.rs | 2 +-
rust/tvm-ffi/tests/test_structural_visit_base.rs | 6 +-
tests/python/test_stubgen_rust.py | 69 +++------
45 files changed, 715 insertions(+), 434 deletions(-)
diff --git a/docs/guides/rust_lang_guide.rst b/docs/guides/rust_lang_guide.rst
index 3fa3a679..8f378f57 100644
--- a/docs/guides/rust_lang_guide.rst
+++ b/docs/guides/rust_lang_guide.rst
@@ -319,7 +319,7 @@ loads, as a C++ type does:
use tvm_ffi::reflection::ObjectDef;
use tvm_ffi::{rs_object, Object, ObjectArc, Result};
- #[rs_object("my.Point", s_eq_hash_kind = "TreeNode", reflection =
PointObj::reflect_methods)]
+ #[rs_object(type_key = "my.Point", s_eq_hash_kind = "TreeNode", reflection
= PointObj::reflect_methods)]
pub struct PointObj {
base_: Object,
/// The x coordinate.
@@ -378,7 +378,7 @@ names the reference to the parent's object:
use tvm_ffi::derive::ObjectRef;
use tvm_ffi::{rs_object, Object, ObjectArc};
- #[rs_object("my.Point", s_eq_hash_kind = "TreeNode")]
+ #[rs_object(type_key = "my.Point", s_eq_hash_kind = "TreeNode")]
pub struct PointObj {
base_: Object,
pub x: i64,
@@ -391,7 +391,7 @@ names the reference to the parent's object:
data: ObjectArc<PointObj>,
}
- #[rs_object("my.Point3", final)]
+ #[rs_object(type_key = "my.Point3", final)]
pub struct Point3Obj {
base_: PointObj,
pub z: i64,
@@ -502,11 +502,7 @@ TVM FFI uses standard Rust ``Result`` types:
// Custom errors
if value < 0 {
- return Err(Error::new(
- VALUE_ERROR,
- "Value must be non-negative",
- ""
- ));
+ return Err(Error::new(VALUE_ERROR, "Value must be non-negative"));
}
Ok(())
@@ -1024,7 +1020,7 @@ An enclosing Pattern region wins, and the prior region is
restored even on error
or panic. State remains available after the recursive call.
Typed ``MutationView<'_, T>`` inputs work with the same recursive methods.
-Use ``value.with_inplace_mode(InplaceMode::Disallow)`` to restrict permission.
+Use ``value.narrow_inplace_mode(InplaceMode::Disallow)`` to restrict
permission.
Borrow the value in place through ``value.downcast_ref::<Node>()``, as with
``AnyView``, or other payloads through ``value.as_any_view()``; finish using
those
borrows before consuming the capability. A typed object capability also
dereferences to its shared node.
@@ -1084,7 +1080,7 @@ while retaining typed scalar replacements:
Ok((value + 1).into())
}
fn mutate_any(&mut self, value: MutationView<'_>, mutator: &mut
StructuralMutatorView<'_, Self>) -> Result<UnchangedOr<Any>> {
- mutator.default_mutate(self,
value.with_inplace_mode(InplaceMode::Disallow))
+ mutator.default_mutate(self,
value.narrow_inplace_mode(InplaceMode::Disallow))
}
}
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 ee27babc..2183fd4e 100644
--- a/examples/rust_stubgen/rust/src/generated/rust_stubgen/mod.rs
+++ b/examples/rust_stubgen/rust/src/generated/rust_stubgen/mod.rs
@@ -64,7 +64,7 @@ impl TryFrom<i64> for PairKind {
type Error = Error;
fn try_from(value: i64) -> Result<Self> {
i32::try_from(value).map(Self).map_err(|_| {
- Error::new(VALUE_ERROR, &format!("PairKind value {value} does not
fit i32"), "")
+ Error::new(VALUE_ERROR, &format!("PairKind value {value} does not
fit i32"))
})
}
}
diff --git a/python/tvm_ffi/stub/rust_generator/codegen.py
b/python/tvm_ffi/stub/rust_generator/codegen.py
index a7f9acd9..d7128168 100644
--- a/python/tvm_ffi/stub/rust_generator/codegen.py
+++ b/python/tvm_ffi/stub/rust_generator/codegen.py
@@ -89,6 +89,42 @@ if TYPE_CHECKING:
from .directives import EnumSpec
+def _read_lines(head: str, macro: str, args: list[str], read: str) ->
list[str]:
+ """``head + macro!(args) + read`` in an accessor body, wrapped as rustfmt
does at its depth.
+
+ The accessor sits in an ``impl``, so its lines gain one more indent.
Arguments
+ wider than rustfmt's ``fn_call_width`` take one line each; otherwise the
read
+ moves to its own line. A ``let`` keeps a column for its ``;``, and first
tries
+ its value on the next line.
+ """
+ width = C_RUST.RUST_MAX_WIDTH - 4
+ indent = head[: len(head) - len(head.lstrip())]
+ joined = ", ".join(args)
+ call = f"{macro}!({joined})"
+ if len(joined) > C_RUST.RUST_FN_CALL_WIDTH:
+ *rest, last = args
+ return [
+ f"{head}{macro}!(",
+ *[f"{indent} {arg}," for arg in rest],
+ f"{indent} {last}",
+ f"{indent})",
+ f"{indent}{read}",
+ ]
+ let, eq, _ = head.partition(" = ")
+ line = f"{head}{call}{read}"
+ chained = [f"{head}{call}", f"{indent} {read}"]
+ if not eq:
+ return [line] if len(line) <= width else chained
+ moved = f"{indent} {call}{read}"
+ if len(line) < width - 1:
+ return [line]
+ if len(moved) < width - 1:
+ return [f"{let} =", moved]
+ if len(f"{head}{call}") < width:
+ return chained
+ return [f"{let} =", f"{indent} {call}", f"{indent} {read}"]
+
+
def _call_lines(open_: str, items: list[str], close: str) -> list[str]:
"""``open_ + items + close`` on one line, or wrapped the way rustfmt does
when it would overflow.
@@ -421,8 +457,8 @@ class _ObjectRenderer:
target = f"{self.type_key}.{field.name}"
name = rust_ident(field.name)
vis = self._visibility(target)
- static = self._getter_static_lines(field.name)
- getter = "GETTER.clone()?"
+ cached = self.imports.record("tvm_ffi::cached_field_getter")
+ args = [self.obj_struct, f'"{field.name}"']
allow = _allow_non_snake_case([name])
enum = directives.enums.get(target)
@@ -430,8 +466,7 @@ class _ObjectRenderer:
return [
*allow,
f"{vis} fn {name}(&self) -> Result<{enum.name}> {{",
- *static,
- f" let raw: i64 = {getter}.get(self)?;",
+ *_read_lines(" let raw: i64 = ", cached, args,
".get(self)?;"),
f" {enum.name}::try_from(raw)",
"}",
]
@@ -447,8 +482,7 @@ class _ObjectRenderer:
return [
*allow,
f"{vis} fn {name}(&self) -> Result<{any_type}> {{",
- *static,
- f" {getter}.get_any(self)",
+ *_read_lines(" ", cached, args, ".get_any(self)"),
"}",
]
if target in directives.nullable and not
rust_type.startswith("Option<"):
@@ -456,35 +490,10 @@ class _ObjectRenderer:
return [
*allow,
f"{vis} fn {name}(&self) -> Result<{rust_type}> {{",
- *static,
- f" {getter}.get(self)",
+ *_read_lines(" ", cached, args, ".get(self)"),
"}",
]
- def _getter_static_lines(self, field_name: str) -> list[str]:
- """Declare the accessor's ``static GETTER``, which resolves the field
on first use only.
-
- A fn-local ``static`` cannot name ``Self``, so the owner is spelled
out.
- Wrapped as rustfmt does at the accessor's depth (``impl``, then
``fn``).
- """
- lazy =
self.imports.record("tvm_ffi::extra::cold_init_lazy_lock::ColdInitLazyLock")
- static = f" static GETTER: {lazy}<Result<FieldGetter>> ="
- owner, key = f"{self.obj_struct}::type_index()", f'"{field_name}"'
- call = f"FieldGetter::new({owner}, {key})"
- one_line = f"{static} {lazy}::new(|| {call});"
- moved = f" {lazy}::new(|| {call});"
- if len(one_line) <= C_RUST.RUST_MAX_WIDTH:
- lines = [one_line]
- elif len(moved) <= C_RUST.RUST_MAX_WIDTH:
- lines = [static, moved]
- else:
- lines = [
- f"{static} {lazy}::new(|| {{",
- *_call_lines(" FieldGetter::new(", [owner, key],
")"),
- " });",
- ]
- return [line[4:] for line in lines]
-
def _enum_lines(self, spec: EnumSpec) -> list[str]:
"""Render the type an ``enum`` directive declares: an open integer
newtype, or a closed enum."""
error = self.imports.record("tvm_ffi::Error")
@@ -494,7 +503,7 @@ class _ObjectRenderer:
if spec.closed:
message = f'&format!("{spec.name} has no value {{value}}")'
rejected = _call_lines(
- f" _ => Err({error}::new(", [value_error, message,
'""'], ")),"
+ f" _ => Err({error}::new(", [value_error, message],
")),"
)
return [
*allow,
@@ -537,7 +546,7 @@ class _ObjectRenderer:
f" fn try_from(value: i64) -> {result}<Self> {{",
f" {spec.repr}::try_from(value).map(Self).map_err(|_| {{",
f' {error}::new({value_error}, &format!("{spec.name}
value {{value}} does not fit '
- f'{spec.repr}"), "")',
+ f'{spec.repr}"))',
" })",
" }",
"}",
@@ -829,8 +838,6 @@ class _ObjectRenderer:
is_enum = C_RUST.RUST_ENUM_TYPE_KEY in self.info.ancestors
has_accessors = bool(fields) and not verdict.is_complete
if has_accessors:
- self.imports.record("tvm_ffi::ObjectCore") # `<Obj>::type_index()`
- self.imports.record("tvm_ffi::reflection::FieldGetter")
self.imports.record("tvm_ffi::Result")
# The reference comes first, so a `///` written above the block
documents it.
diff --git a/python/tvm_ffi/stub/rust_generator/consts.py
b/python/tvm_ffi/stub/rust_generator/consts.py
index 4c341890..2f66c5d1 100644
--- a/python/tvm_ffi/stub/rust_generator/consts.py
+++ b/python/tvm_ffi/stub/rust_generator/consts.py
@@ -183,3 +183,5 @@ RUST_GENERATED_MEMBERS = frozenset({"base_", "data_"})
#: ``rustfmt``'s default ``max_width``; a wider allocator signature wraps one
parameter per line.
RUST_MAX_WIDTH = 100
+#: ``rustfmt``'s default ``fn_call_width``; wider call arguments take one line
each.
+RUST_FN_CALL_WIDTH = 60
diff --git a/rust/tvm-ffi-macros/src/rs_object.rs
b/rust/tvm-ffi-macros/src/rs_object.rs
index 26f32f19..8b02d4c0 100644
--- a/rust/tvm-ffi-macros/src/rs_object.rs
+++ b/rust/tvm-ffi-macros/src/rs_object.rs
@@ -34,7 +34,7 @@ pub(crate) fn rs_object(args: TokenStream, item: TokenStream)
-> TokenStream {
.into()
}
-/// `#[rs_object("type.key", final, ...)]`.
+/// `#[rs_object(type_key = "type.key", final, ...)]`.
struct RsObjectArgs {
type_key: LitStr,
type_final: bool,
@@ -48,14 +48,15 @@ struct RsObjectArgs {
impl Parse for RsObjectArgs {
fn parse(input: ParseStream) -> syn::Result<Self> {
- let type_key = input.parse::<LitStr>().map_err(|error| {
- syn::Error::new(
- error.span(),
- "expected the type key first, as in
`#[rs_object(\"my.Point\")]`",
- )
- })?;
+ if input.peek(LitStr) {
+ return Err(syn::Error::new(
+ input.span(),
+ "name the type key, as in `#[rs_object(type_key =
\"my.Point\")]`",
+ ));
+ }
+ let mut type_key = None;
let mut args = RsObjectArgs {
- type_key,
+ type_key: LitStr::new("", input.span()),
type_final: false,
immutable: false,
s_eq_hash_kind: None,
@@ -66,9 +67,11 @@ impl Parse for RsObjectArgs {
};
let mut seen = Vec::new();
while !input.is_empty() {
- input.parse::<Token![,]>()?;
- if input.is_empty() {
- break;
+ if !seen.is_empty() {
+ input.parse::<Token![,]>()?;
+ if input.is_empty() {
+ break;
+ }
}
// `final` and `crate` are keywords.
let name = input.call(syn::Ident::parse_any)?;
@@ -80,6 +83,7 @@ impl Parse for RsObjectArgs {
input.parse::<Token![=]>()?;
}
match key.as_str() {
+ "type_key" => type_key = Some(input.parse()?),
"final" => args.type_final = true,
"immutable" => args.immutable = true,
"s_eq_hash_kind" => args.s_eq_hash_kind = Some(input.parse()?),
@@ -90,7 +94,7 @@ impl Parse for RsObjectArgs {
_ => {
return Err(syn::Error::new(
name.span(),
- "expected `final`, `immutable`, `s_eq_hash_kind =
\"<kind>\"`, `child_slots = <n>`, \
+ "expected `type_key = \"<key>\"`, `final`,
`immutable`, `s_eq_hash_kind = \"<kind>\"`, `child_slots = <n>`, \
`child_slots_can_overflow = <bool>`, `reflection =
<path>` or \
`crate = <path>`",
));
@@ -98,6 +102,12 @@ impl Parse for RsObjectArgs {
}
seen.push(key);
}
+ args.type_key = type_key.ok_or_else(|| {
+ syn::Error::new(
+ input.span(),
+ "missing the type key, as in `#[rs_object(type_key =
\"my.Point\")]`",
+ )
+ })?;
if args.type_final
&& (args.child_slots.is_some() ||
args.child_slots_can_overflow.is_some())
{
diff --git a/rust/tvm-ffi/src/any.rs b/rust/tvm-ffi/src/any.rs
index 54f6498f..f660644f 100644
--- a/rust/tvm-ffi/src/any.rs
+++ b/rust/tvm-ffi/src/any.rs
@@ -137,6 +137,13 @@ impl<'a> AnyView<'a> {
self.data.type_index
}
+ /// The registered type key of the viewed value, such as `"int"` or
+ /// `"ffi.Array"`. See also [`Any::type_key`].
+ #[inline]
+ pub fn type_key(&self) -> &'static str {
+ object::registered_type_key(self.data.type_index)
+ }
+
/// Whether the value is None.
#[inline(always)]
pub fn is_none(&self) -> bool {
@@ -189,6 +196,15 @@ impl<'a> AnyView<'a> {
unsafe { T::downcast_ref_from_any(&self.data) }
}
+ /// Whether the viewed value is a `T` as is, by the strict check of
+ /// [`downcast_ref`](Self::downcast_ref), without conversion.
+ /// See also [`Any::is`].
+ #[inline(always)]
+ pub fn is<T: AnyCompatible>(&self) -> bool {
+ // SAFETY: the view holds a valid cell.
+ unsafe { T::check_any_strict(&self.data) }
+ }
+
/// The strong reference count of the held object, for tests and debugging;
/// `None` if the value is not an object.
#[doc(hidden)]
@@ -289,6 +305,19 @@ impl Any {
self.data.type_index
}
+ /// The registered type key of the stored value, such as `"int"` or
+ /// `"ffi.Array"`, as C++ `Any::GetTypeKey`.
+ ///
+ /// ```
+ /// use tvm_ffi::{Any, Array};
+ /// assert_eq!(Any::from(1_i64).type_key(), "int");
+ /// assert_eq!(Any::from(Array::from_vec(vec![1_i64])).type_key(),
"ffi.Array");
+ /// ```
+ #[inline]
+ pub fn type_key(&self) -> &'static str {
+ object::registered_type_key(self.data.type_index)
+ }
+
/// Whether the value is None.
#[inline(always)]
pub fn is_none(&self) -> bool {
@@ -328,6 +357,24 @@ impl Any {
unsafe { T::downcast_ref_from_any(&self.data) }
}
+ /// Whether the stored value is a `T` as is, by the strict check of
+ /// [`downcast_ref`](Self::downcast_ref), without conversion, as std
+ /// `Any::is`. It covers every [`AnyCompatible`] type, such as `bool`.
+ ///
+ /// ```
+ /// use tvm_ffi::{Any, Array};
+ /// let value = Any::from(Array::from_vec(vec![1_i64, 2]));
+ /// assert!(value.is::<Array<i64>>());
+ /// assert!(!value.is::<Array<f64>>());
+ /// assert!(Any::from(true).is::<bool>());
+ /// assert!(!Any::from(1_i64).is::<f64>());
+ /// ```
+ #[inline(always)]
+ pub fn is<T: AnyCompatible>(&self) -> bool {
+ // SAFETY: the owned cell is valid.
+ unsafe { T::check_any_strict(&self.data) }
+ }
+
/// Return a pointer to the raw ABI representation, for a runtime call that
/// writes an owned result into it as an out-parameter.
///
@@ -540,7 +587,7 @@ impl<'a, T: AnyCompatible> TryFrom<AnyView<'a>> for
TryFromTemp<T> {
T::get_mismatch_type_info(&value.data),
T::type_str()
);
- crate::error::Error::new(crate::error::TYPE_ERROR,
&msg, "")
+ crate::error::Error::new(crate::error::TYPE_ERROR,
&msg)
})
.map(TryFromTemp::new)
}
@@ -574,7 +621,7 @@ impl<T: AnyCompatible> TryFrom<Any> for TryFromTemp<T> {
T::get_mismatch_type_info(&value.data),
T::type_str()
);
- crate::error::Error::new(crate::error::TYPE_ERROR,
&msg, "")
+ crate::error::Error::new(crate::error::TYPE_ERROR,
&msg)
})
.map(TryFromTemp::new)
}
@@ -629,7 +676,7 @@ impl<T: AnyCompatible> ArgTryFromAnyView for T {
T::get_mismatch_type_info(&value.data),
T::type_str()
);
- crate::error::Error::new(crate::error::TYPE_ERROR, &msg,
"")
+ crate::error::Error::new(crate::error::TYPE_ERROR, &msg)
})
}
}
diff --git a/rust/tvm-ffi/src/big_int/int_ops.rs
b/rust/tvm-ffi/src/big_int/int_ops.rs
index 9ba0f290..4573f328 100644
--- a/rust/tvm-ffi/src/big_int/int_ops.rs
+++ b/rust/tvm-ffi/src/big_int/int_ops.rs
@@ -77,15 +77,15 @@ impl WordsBuf {
}
pub(super) fn zero_division() -> Error {
- Error::new(ZERO_DIVISION_ERROR, "Division by zero", "")
+ Error::new(ZERO_DIVISION_ERROR, "Division by zero")
}
fn negative_shift() -> Error {
- Error::new(VALUE_ERROR, "Negative BigInt shift count", "")
+ Error::new(VALUE_ERROR, "Negative BigInt shift count")
}
fn overflow(message: &str) -> Error {
- Error::new(OVERFLOW_ERROR, message, "")
+ Error::new(OVERFLOW_ERROR, message)
}
/// Whether a nonempty signed-word view represents a negative integer.
diff --git a/rust/tvm-ffi/src/big_int/mod.rs b/rust/tvm-ffi/src/big_int/mod.rs
index bd17e6f8..65d83695 100644
--- a/rust/tvm-ffi/src/big_int/mod.rs
+++ b/rust/tvm-ffi/src/big_int/mod.rs
@@ -387,11 +387,7 @@ macro_rules! impl_try_from_big_int {
fn try_from(value: &BigInt) -> Result<Self> {
value.$to().ok_or_else(|| {
- Error::new(
- OVERFLOW_ERROR,
- &format!("{} does not fit in {}", value,
stringify!($t)),
- "",
- )
+ Error::new(OVERFLOW_ERROR, &format!("{} does not fit in
{}", value, stringify!($t)))
})
}
}
@@ -413,13 +409,12 @@ impl TryFrom<f64> for BigInt {
return Ok(Self::from_i64(value as i64));
}
if value.is_nan() {
- return Err(Error::new(VALUE_ERROR, "Cannot convert NaN to BigInt",
""));
+ return Err(Error::new(VALUE_ERROR, "Cannot convert NaN to
BigInt"));
}
if value.is_infinite() {
return Err(Error::new(
OVERFLOW_ERROR,
"Cannot convert infinity to BigInt",
- "",
));
}
// |value| >= 2^63 is an integer: shift the 53-bit significand to its
binary exponent.
@@ -445,11 +440,7 @@ impl FromStr for BigInt {
_ => (false, text),
};
if digits.is_empty() || !digits.bytes().all(|byte|
byte.is_ascii_digit()) {
- return Err(Error::new(
- VALUE_ERROR,
- "Invalid decimal string for BigInt",
- "",
- ));
+ return Err(Error::new(VALUE_ERROR, "Invalid decimal string for
BigInt"));
}
// Accumulate 19-digit chunks: magnitude = magnitude * 10^len + chunk.
let mut magnitude = Vec::new();
diff --git a/rust/tvm-ffi/src/collections/map.rs
b/rust/tvm-ffi/src/collections/map.rs
index 04232e1c..adac8de6 100644
--- a/rust/tvm-ffi/src/collections/map.rs
+++ b/rust/tvm-ffi/src/collections/map.rs
@@ -78,7 +78,7 @@ fn element_from_any<T: ContainerElement>(value: Any) ->
Result<T> {
T::container_get_mismatch_type_info(value.as_raw_ffi_any()),
T::container_type_str()
);
- Error::new(crate::error::TYPE_ERROR, &message, "")
+ Error::new(crate::error::TYPE_ERROR, &message)
})
}
}
diff --git a/rust/tvm-ffi/src/collections/tensor.rs
b/rust/tvm-ffi/src/collections/tensor.rs
index 24ec0200..f9bfe62b 100644
--- a/rust/tvm-ffi/src/collections/tensor.rs
+++ b/rust/tvm-ffi/src/collections/tensor.rs
@@ -41,7 +41,10 @@ use tvm_ffi_sys::{TVMFFIEnvTensorAlloc, TVMFFIObjectHandle};
/// (sub-byte types are packed), and `free_data` must release
/// exactly what `alloc_data` returned. `free_data` may be called for a tensor
/// whose `data` is null, when `alloc_data` panicked, and must then do nothing.
-pub unsafe trait NDAllocator: 'static {
+///
+/// `Send + Sync`: the tensor frees its data through the allocator when the
+/// last reference drops, which may be on any thread.
+pub unsafe trait NDAllocator: Send + Sync + 'static {
/// The minimum alignment of the allocated data, in bytes.
const MIN_ALIGN: usize;
/// Allocate the data of a tensor.
@@ -646,7 +649,7 @@ impl<'a> TensorView<'a> {
"{context}Cannot convert from type `{}` to `DLTensor*`",
crate::object::type_key_of_index(value.type_index)
);
- crate::error::Error::new(crate::error::TYPE_ERROR, &message, "")
+ crate::error::Error::new(crate::error::TYPE_ERROR, &message)
}
}
diff --git a/rust/tvm-ffi/src/derive.rs b/rust/tvm-ffi/src/derive.rs
index 868514be..fb2ec075 100644
--- a/rust/tvm-ffi/src/derive.rs
+++ b/rust/tvm-ffi/src/derive.rs
@@ -112,7 +112,7 @@ pub use tvm_ffi_macros::Object;
/// use tvm_ffi::derive::ObjectRef;
/// use tvm_ffi::{rs_object, Object, ObjectArc};
///
-/// #[rs_object("my_ext.MyPair")]
+/// #[rs_object(type_key = "my_ext.MyPair")]
/// pub struct MyPairObj {
/// base_: Object,
/// pub first: i64,
@@ -124,7 +124,7 @@ pub use tvm_ffi_macros::Object;
/// data: ObjectArc<MyPairObj>,
/// }
///
-/// #[rs_object("my_ext.MyTriple", final)]
+/// #[rs_object(type_key = "my_ext.MyTriple", final)]
/// pub struct MyTripleObj {
/// base_: MyPairObj,
/// pub third: i64,
@@ -155,7 +155,7 @@ pub use tvm_ffi_macros::Object;
/// use tvm_ffi::derive::ObjectRef;
/// use tvm_ffi::{rs_object, Array, Object, ObjectArc};
///
-/// #[rs_object("my_ext.MyPair")]
+/// #[rs_object(type_key = "my_ext.MyPair")]
/// pub struct MyPairObj {
/// base_: Object,
/// pub first: i64,
diff --git a/rust/tvm-ffi/src/enum_ref.rs b/rust/tvm-ffi/src/enum_ref.rs
index 8f751493..29f5c6bf 100644
--- a/rust/tvm-ffi/src/enum_ref.rs
+++ b/rust/tvm-ffi/src/enum_ref.rs
@@ -122,7 +122,6 @@ pub trait EnumRef: ObjectRefCore {
"Enum `{}` has no value named `{name}`",
Self::ContainerType::TYPE_KEY
),
- "",
));
};
any_into(value)
diff --git a/rust/tvm-ffi/src/error.rs b/rust/tvm-ffi/src/error.rs
index 57cae23e..af996786 100644
--- a/rust/tvm-ffi/src/error.rs
+++ b/rust/tvm-ffi/src/error.rs
@@ -22,7 +22,9 @@ use crate::derive::{Object, ObjectRef};
use crate::object::{self, Object, ObjectArc, ObjectRefCore};
use crate::type_traits::AnyCompatible;
use std::ffi::c_void;
-use tvm_ffi_sys::TVMFFIBacktraceUpdateMode::kTVMFFIBacktraceUpdateModeAppend;
+use tvm_ffi_sys::TVMFFIBacktraceUpdateMode::{
+ kTVMFFIBacktraceUpdateModeAppend, kTVMFFIBacktraceUpdateModeReplace,
+};
use tvm_ffi_sys::{
TVMFFIAny, TVMFFIByteArray, TVMFFIErrorCell,
TVMFFIErrorCreateWithCauseAndExtraContext,
TVMFFIErrorMoveFromRaised, TVMFFIErrorSetRaised, TVMFFIObjectHandle,
TVMFFITypeIndex,
@@ -82,7 +84,7 @@ pub type Result<T, E = Error> = std::result::Result<T, E>;
///
/// Use the constants such as [`VALUE_ERROR`] and [`TYPE_ERROR`], or
/// [`ErrorKind::new("MyError")`](ErrorKind::new) for a custom kind.
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ErrorKind<'a>(&'a str);
impl<'a> ErrorKind<'a> {
@@ -91,7 +93,7 @@ impl<'a> ErrorKind<'a> {
/// ```
/// use tvm_ffi::{Error, ErrorKind};
/// const MY_ERROR: ErrorKind = ErrorKind::new("MyError");
- /// let error = Error::new(MY_ERROR, "bad input", "");
+ /// let error = Error::new(MY_ERROR, "bad input");
/// assert_eq!(error.kind().as_str(), "MyError");
/// ```
pub const fn new(name: &'a str) -> Self {
@@ -155,7 +157,7 @@ impl Error {
.map(|s| s.as_str())
})
.unwrap_or("unknown payload");
- Self::new(INTERNAL_ERROR, &format!("panicked: {message}"), "")
+ Self::new(INTERNAL_ERROR, &format!("panicked: {message}"))
}
/// The `TypeError` for a reference that
[`downcast`](crate::ObjectRefCore::downcast)
@@ -176,55 +178,57 @@ impl Error {
B::get_mismatch_type_info(&cell),
B::type_str()
);
- Self::new(TYPE_ERROR, &message, "")
+ Self::new(TYPE_ERROR, &message)
}
- /// Create an error.
+ /// Create an error with an empty backtrace.
///
- /// Prefer [`bail!`](crate::bail), which also records the source location.
+ /// Prefer [`format_err!`](crate::format_err) or [`bail!`](crate::bail),
+ /// which also record the source location. Add the other parts with
+ /// [`with_backtrace`](Self::with_backtrace),
+ /// [`with_cause`](Self::with_cause) and
+ /// [`with_extra_context`](Self::with_extra_context).
///
/// # Arguments
///
/// * `kind` - The kind of the error, such as [`VALUE_ERROR`].
/// * `message` - The message of the error.
- /// * `backtrace` - The backtrace of the error, most recent call first;
may be empty.
///
/// # Returns
///
/// The error.
- pub fn new(kind: ErrorKind<'_>, message: &str, backtrace: &str) -> Self {
- Self::create(kind, message, backtrace, None, std::ptr::null_mut())
+ pub fn new(kind: ErrorKind<'_>, message: &str) -> Self {
+ Self::create(kind, message, "", None, std::ptr::null_mut())
}
- /// Create an error retaining its cause and any application-specific
context.
- ///
- /// # Arguments
- ///
- /// * `kind` - The kind of the error.
- /// * `message` - The message of the error.
- /// * `backtrace` - The backtrace of the error, most recent call first;
may be empty.
- /// * `cause_chain` - The error that caused this one, if any.
- /// * `extra_context` - An object of application-specific context, if any;
- /// `Send + Sync`, since the error is.
- ///
- /// # Returns
+ /// Set `cause` as the error that caused this one, the
+ /// [`cause_chain`](Self::cause_chain). The error is updated in place when
+ /// it is not shared, and copied otherwise.
///
- /// The error.
- pub fn new_with_cause_and_extra_context<T: ObjectRefCore + Send + Sync>(
- kind: ErrorKind<'_>,
- message: &str,
- backtrace: &str,
- cause_chain: Option<&Error>,
- extra_context: Option<&T>,
- ) -> Self {
- let extra_context = extra_context.map_or(std::ptr::null_mut(),
|context| {
- ObjectArc::as_ptr(T::data(context)) as TVMFFIObjectHandle
- });
- Self::create(kind, message, backtrace, cause_chain, extra_context)
+ /// ```
+ /// use tvm_ffi::{Error, RUNTIME_ERROR, VALUE_ERROR};
+ /// let cause = Error::new(VALUE_ERROR, "bad input");
+ /// let error = Error::new(RUNTIME_ERROR, "load
failed").with_cause(&cause);
+ /// assert_eq!(error.cause_chain().unwrap().message(), "bad input");
+ /// ```
+ #[inline]
+ pub fn with_cause(mut self, cause: &Error) -> Self {
+ *self.cause_chain_mut() = Some(cause.clone());
+ self
}
- /// As [`Self::new_with_cause_and_extra_context`], with `extra_context` a
- /// borrowed object handle or null.
+ /// Set `context` as the [`extra_context`](Self::extra_context), an object
+ /// of application-specific context; `Send + Sync`, since the error is.
+ /// The error is updated in place when it is not shared, and copied
+ /// otherwise.
+ #[inline]
+ pub fn with_extra_context<T: ObjectRefCore + Send + Sync>(mut self,
context: &T) -> Self {
+ *self.extra_context_mut() =
Some(object::into_root_object_ref(context.clone()));
+ self
+ }
+
+ /// Create an error from its parts, with `extra_context` a borrowed object
+ /// handle or null.
fn create(
kind: ErrorKind<'_>,
message: &str,
@@ -352,6 +356,14 @@ impl Error {
Some(unsafe { &*(handle as *const _ as *const object::ObjectRef) })
}
+ /// The `cause_chain` slot, to update in place.
+ fn cause_chain_mut(&mut self) -> &mut Option<Error> {
+ let error = self.make_mut();
+ // SAFETY: this handle is the only reference, and the slot owns a
nullable
+ // error handle, which is exactly an `Option<Error>`.
+ unsafe { &mut
*std::ptr::addr_of_mut!((*error).cell.cause_chain).cast::<Option<Error>>() }
+ }
+
/// The `extra_context` slot, to update in place.
pub(crate) fn extra_context_mut(&mut self) -> &mut
Option<object::ObjectRef> {
let error = self.make_mut();
@@ -371,15 +383,31 @@ impl Error {
/// The traceback of the error.
pub fn traceback_most_recent_call_last(&self) -> String {
let backtrace = self.backtrace();
- let backtrace_lines = backtrace.split('\n');
let mut traceback = String::new();
- for line in backtrace_lines.rev() {
+ for line in backtrace.split('\n').rev().filter(|line|
!line.is_empty()) {
traceback.push_str(line);
traceback.push('\n');
}
traceback
}
+ /// Replace the backtrace, most recent call first. The error is updated in
+ /// place when it is not shared, and copied otherwise.
+ #[inline]
+ pub fn with_backtrace(mut self, backtrace: &str) -> Self {
+ let error = self.make_mut();
+ // SAFETY: `make_mut` made this handle the only reference to `error`.
+ unsafe {
+ let backtrace_data = TVMFFIByteArray::from_str(backtrace);
+ ((*error).cell.update_backtrace)(
+ error.cast::<c_void>(),
+ &backtrace_data,
+ kTVMFFIBacktraceUpdateModeReplace as i32,
+ );
+ }
+ self
+ }
+
/// Append the backtrace to the error.
///
/// [`attach_context!`](crate::attach_context) appends the current source
@@ -423,27 +451,29 @@ impl Error {
}
}
+/// Formats the kind and the message, such as `ValueError: bad x`, as C++
+/// `what()`; [`Debug`](std::fmt::Debug) adds the traceback.
impl std::fmt::Display for Error {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}: {}", self.kind().as_str(), self.message())
+ }
+}
+
+/// Formats the traceback, most recent call last, then the kind and the
+/// message, as Python prints an exception; `unwrap` and `expect` print it.
+impl std::fmt::Debug for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.backtrace().is_empty() {
- return write!(f, "{}: {}", self.kind().as_str(), self.message());
+ return std::fmt::Display::fmt(self, f);
}
write!(
f,
- "Traceback (most recent call last):\n{}{}: {}",
+ "Traceback (most recent call last):\n{}{self}",
self.traceback_most_recent_call_last(),
- self.kind().as_str(),
- self.message()
)
}
}
-impl std::fmt::Debug for Error {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- std::fmt::Display::fmt(self, f)
- }
-}
-
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.cause_chain().map(|cause| cause as _)
diff --git a/rust/tvm-ffi/src/extra/module.rs b/rust/tvm-ffi/src/extra/module.rs
index adee30d0..acb681bb 100644
--- a/rust/tvm-ffi/src/extra/module.rs
+++ b/rust/tvm-ffi/src/extra/module.rs
@@ -58,7 +58,7 @@ impl Module {
/// # Errors
/// If the library cannot be loaded.
pub fn load_from_file<Str: AsRef<str>>(file_name: Str) -> Result<Module> {
- let file_name = crate::string::String::from(file_name);
+ let file_name = crate::string::String::from(file_name.as_ref());
crate::cached_global_func!("ffi.ModuleLoadFromFile")
.call_tuple((file_name,))?
.try_into()
@@ -79,7 +79,7 @@ impl Module {
/// Returns an error if the module has no function `name`, as Python's
/// `Module.get_function` raises.
pub fn get_function<Str: AsRef<str>>(&self, name: Str) -> Result<Function>
{
- let name = crate::string::String::from(name);
+ let name = crate::string::String::from(name.as_ref());
crate::cached_global_func!("ffi.ModuleGetFunction")
.call_tuple((self, name, false))?
.try_into()
@@ -88,7 +88,7 @@ impl Module {
/// As [`get_function`](Self::get_function), but also searching the modules
/// this one imports, depth first in import order.
pub fn get_function_with_imports<Str: AsRef<str>>(&self, name: Str) ->
Result<Function> {
- let name = crate::string::String::from(name);
+ let name = crate::string::String::from(name.as_ref());
crate::cached_global_func!("ffi.ModuleGetFunction")
.call_tuple((self, name, true))?
.try_into()
@@ -104,7 +104,7 @@ impl Module {
/// # Returns
/// Whether the function exists.
pub fn implements_function<Str: AsRef<str>>(&self, name: Str) ->
Result<bool> {
- let name = String::from(name);
+ let name = String::from(name.as_ref());
crate::cached_global_func!("ffi.ModuleImplementsFunction")
.call_tuple((self, name, false))?
.try_into()
@@ -113,7 +113,7 @@ impl Module {
/// As [`implements_function`](Self::implements_function), but also
searching the modules
/// this one imports, depth first in import order.
pub fn implements_function_with_imports<Str: AsRef<str>>(&self, name: Str)
-> Result<bool> {
- let name = String::from(name);
+ let name = String::from(name.as_ref());
crate::cached_global_func!("ffi.ModuleImplementsFunction")
.call_tuple((self, name, true))?
.try_into()
@@ -133,7 +133,7 @@ impl Module {
/// # Returns
/// The metadata as a JSON string, or `None` if the function has none.
pub fn get_function_metadata<Str: AsRef<str>>(&self, name: Str) ->
Result<Option<String>> {
- let name = String::from(name);
+ let name = String::from(name.as_ref());
crate::cached_global_func!("ffi.ModuleGetFunctionMetadata")
.call_tuple((self, name, false))?
.try_into()
@@ -145,7 +145,7 @@ impl Module {
&self,
name: Str,
) -> Result<Option<String>> {
- let name = String::from(name);
+ let name = String::from(name.as_ref());
crate::cached_global_func!("ffi.ModuleGetFunctionMetadata")
.call_tuple((self, name, true))?
.try_into()
@@ -164,7 +164,7 @@ impl Module {
/// # Returns
/// The doc string, or `None` if the function has none.
pub fn get_function_doc<Str: AsRef<str>>(&self, name: Str) ->
Result<Option<String>> {
- let name = String::from(name);
+ let name = String::from(name.as_ref());
crate::cached_global_func!("ffi.ModuleGetFunctionDoc")
.call_tuple((self, name, false))?
.try_into()
@@ -176,7 +176,7 @@ impl Module {
&self,
name: Str,
) -> Result<Option<String>> {
- let name = String::from(name);
+ let name = String::from(name.as_ref());
crate::cached_global_func!("ffi.ModuleGetFunctionDoc")
.call_tuple((self, name, true))?
.try_into()
diff --git a/rust/tvm-ffi/src/extra/structural_common.rs
b/rust/tvm-ffi/src/extra/structural_common.rs
index 7bbeba04..94b8cd86 100644
--- a/rust/tvm-ffi/src/extra/structural_common.rs
+++ b/rust/tvm-ffi/src/extra/structural_common.rs
@@ -39,7 +39,7 @@ use std::ops::ControlFlow;
#[cold]
#[inline(never)]
pub(super) fn runtime_error(message: &str) -> Error {
- Error::new(RUNTIME_ERROR, message, "")
+ Error::new(RUNTIME_ERROR, message)
}
#[cold]
@@ -131,7 +131,6 @@ pub(crate) fn reject_error_value(value: Any) -> Result<Any>
{
Err(Error::new(
TYPE_ERROR,
"a structural handler returned an Error as a value; return it as Err
to fail",
- "",
))
}
diff --git a/rust/tvm-ffi/src/extra/structural_eq_hash.rs
b/rust/tvm-ffi/src/extra/structural_eq_hash.rs
index a60d1f8c..8a028d3e 100644
--- a/rust/tvm-ffi/src/extra/structural_eq_hash.rs
+++ b/rust/tvm-ffi/src/extra/structural_eq_hash.rs
@@ -60,7 +60,7 @@ pub struct StructuralOptions {
/// use tvm_ffi::derive::ObjectRef;
/// use tvm_ffi::{rs_object, structural_equal, Array, Object, ObjectArc};
///
-/// #[rs_object("my_ext.MyPair", final, s_eq_hash_kind = "TreeNode")]
+/// #[rs_object(type_key = "my_ext.MyPair", final, s_eq_hash_kind =
"TreeNode")]
/// pub struct MyPairObj {
/// base_: Object,
/// pub first: i64,
diff --git a/rust/tvm-ffi/src/extra/structural_mutate.rs
b/rust/tvm-ffi/src/extra/structural_mutate.rs
index dc80da21..09b696d6 100644
--- a/rust/tvm-ffi/src/extra/structural_mutate.rs
+++ b/rust/tvm-ffi/src/extra/structural_mutate.rs
@@ -395,7 +395,7 @@ impl<'a, T> MutationView<'a, T> {
///
/// The view with the restricted mode.
#[inline(always)]
- pub fn with_inplace_mode(mut self, requested: InplaceMode) -> Self {
+ pub fn narrow_inplace_mode(mut self, requested: InplaceMode) -> Self {
if requested == InplaceMode::Disallow {
self.mode = InplaceMode::Disallow;
}
@@ -436,6 +436,12 @@ impl<'a, T> MutationView<'a, T> {
unsafe { U::downcast_ref_from_any(&self.value) }
}
+ /// Whether the value is a `U`, as [`Any::is`].
+ #[inline(always)]
+ pub fn is<U: crate::type_traits::AnyCompatible>(&self) -> bool {
+ self.as_any_view().is::<U>()
+ }
+
/// Narrow the matched type without acquiring an owning reference.
///
/// The check is the strict one of `match_any!`: only a value that fits
`U` as
@@ -527,7 +533,7 @@ impl<T: object::ObjectRefCore> MutationView<'_, T> {
/// ```
/// # use tvm_ffi::*;
/// # use tvm_ffi::StructuralMutatorView;
- /// # #[rs_object("my_ext.MyPair", final)]
+ /// # #[rs_object(type_key = "my_ext.MyPair", final)]
/// # pub struct MyPairObj { base_: Object, pub first: Array<i64>, pub
second: Array<i64> }
/// # #[derive(Clone, tvm_ffi::derive::ObjectRef)]
/// # pub struct MyPair { data: ObjectArc<MyPairObj> }
@@ -1726,24 +1732,16 @@ static SHALLOW_COPY_COLUMN:
ColdInitLazyLock<TypeAttrColumn> = ColdInitLazyLock:
#[cold]
#[inline(never)]
fn missing_shallow_copy_error(type_index: i32, attribute: &str) -> Error {
- Error::new(
- TYPE_ERROR,
- &format!(
+ Error::new(TYPE_ERROR, &format!(
"type `{}` cannot use reflected structural mutation because it
does not define `{attribute}`",
crate::object::type_key_of_index(type_index)
- ),
- "",
- )
+ ))
}
#[cold]
#[inline(never)]
fn invalid_shallow_copy_hook_error(attribute: &str) -> Error {
- Error::new(
- TYPE_ERROR,
- &format!("{attribute} must be an ffi.Function"),
- "",
- )
+ Error::new(TYPE_ERROR, &format!("{attribute} must be an ffi.Function"))
}
#[cold]
@@ -1752,7 +1750,6 @@ fn invalid_shallow_copy_result_error() -> Error {
Error::new(
TYPE_ERROR,
"shallow copy callback must return a distinct object with the same
type as its input",
- "",
)
}
@@ -1762,7 +1759,6 @@ fn missing_field_setter_error(name: &str) -> Error {
Error::new(
TYPE_ERROR,
&format!("cannot structurally mutate field `{name}` because it does
not define a setter"),
- "",
)
}
@@ -1780,7 +1776,6 @@ fn invalid_mutation_hook_error() -> Error {
Error::new(
TYPE_ERROR,
"__s_mutate__ must be an opaque function pointer or ffi.Function",
- "",
)
}
@@ -1790,7 +1785,6 @@ fn var_remap_key_error() -> Error {
Error::new(
TYPE_ERROR,
"variable-remap keys must be object-backed values",
- "",
)
}
diff --git a/rust/tvm-ffi/src/extra/structural_visit.rs
b/rust/tvm-ffi/src/extra/structural_visit.rs
index 349ee77c..767b35c3 100644
--- a/rust/tvm-ffi/src/extra/structural_visit.rs
+++ b/rust/tvm-ffi/src/extra/structural_visit.rs
@@ -1057,7 +1057,6 @@ fn invalid_visit_hook_result_error() -> Error {
Error::new(
TYPE_ERROR,
"structural visit hook must return None or ffi.VisitInterrupt",
- "",
)
}
diff --git a/rust/tvm-ffi/src/function.rs b/rust/tvm-ffi/src/function.rs
index 064a42a2..47eeb329 100644
--- a/rust/tvm-ffi/src/function.rs
+++ b/rust/tvm-ffi/src/function.rs
@@ -19,12 +19,11 @@
//! [`Function`], a callable shared with C++ and Python, and the global
function
//! registry.
-pub use crate::function_internal::{PackedCallArg, PackedCallArgs};
+pub use crate::function_internal::{AsPackedCallable, PackedCallArg,
PackedCallArgs};
use crate::any::{Any, AnyView};
use crate::derive::{Object, ObjectRef};
use crate::error::{Error, Result};
-use crate::function_internal::AsPackedCallable;
use crate::object::{Object, ObjectArc, ObjectCore};
use crate::type_traits::AnyCompatible;
use tvm_ffi_sys::{
@@ -122,7 +121,7 @@ impl<F: Fn(&[AnyView]) -> Result<Any> + Send + Sync +
'static> CallbackFunctionO
(this.callback)(packed_args)
})) {
Ok(ret_value) => ret_value,
- Err(payload) =>
Err(crate::function_internal::panic_to_error(payload)),
+ Err(payload) => Err(Error::from_panic(payload)),
};
match ret_value {
Ok(value) => {
@@ -227,8 +226,9 @@ impl Function {
///
/// # Errors
///
- /// Returns an error if no function is registered as `name`. Probe with
- /// `.ok()` where a missing function is expected.
+ /// Returns a `ValueError` if no function is registered as `name`, as C++
+ /// `Function::GetGlobalRequired`. Probe with `.ok()` where a missing
+ /// function is expected.
///
/// ```
/// use tvm_ffi::Function;
@@ -246,7 +246,7 @@ impl Function {
let mut result: TVMFFIObjectHandle = ::std::ptr::null_mut();
crate::check_safe_call!(TVMFFIFunctionGetGlobal(&name_arg, &mut
result))?;
if result.is_null() {
- crate::bail!(crate::error::RUNTIME_ERROR, "Function {} not
found", name);
+ crate::bail!(crate::error::VALUE_ERROR, "Function {} not
found", name);
}
Ok(Self {
data: ObjectArc::<FunctionObj>::from_raw(result as *mut
FunctionObj),
@@ -385,7 +385,9 @@ impl Function {
///
/// # Arguments
///
- /// * `func` - The typed function, of signature `Fn(T0, T1, ...) ->
Result<O>`.
+ /// * `func` - The typed function, an [`AsPackedCallable`] such as
+ /// `Fn(T0, T1, ...) -> Result<O>`; each parameter is an `AnyCompatible`
+ /// type, a `TensorView` or an `RValueRef<T>`.
///
/// # Returns
///
diff --git a/rust/tvm-ffi/src/function_internal.rs
b/rust/tvm-ffi/src/function_internal.rs
index 885a0a21..05ed2eb6 100644
--- a/rust/tvm-ffi/src/function_internal.rs
+++ b/rust/tvm-ffi/src/function_internal.rs
@@ -28,13 +28,6 @@ use tvm_ffi_sys::{TVMFFIAny, TVMFFIByteArray,
TVMFFIStringFromByteArray};
//------------------------------------------------------------------------
// PackedCallable
//------------------------------------------------------------------------
-/// The error to raise for a panic caught at an FFI boundary, where unwinding
-/// into the caller would abort the process.
-#[doc(hidden)]
-pub fn panic_to_error(payload: Box<dyn std::any::Any + Send>) -> Error {
- Error::from_panic(payload)
-}
-
/// The arguments of a packed call from C.
///
/// # Safety
@@ -52,8 +45,15 @@ pub unsafe fn packed_args<'a>(args: *const TVMFFIAny,
num_args: i32) -> &'a [Any
std::slice::from_raw_parts(args as *const AnyView, num_args as usize)
}
+/// A closure `Fn(T0, ..., Tn) -> Result<O>` with 0 to 8 parameters, which
+/// [`Function::from_typed`](crate::Function::from_typed) and
+/// [`Function::register_global_typed`](crate::Function::register_global_typed)
+/// wrap. Each parameter is any [`AnyCompatible`] type, a
+/// [`TensorView`](crate::TensorView), valid during the call, or an
+/// [`RValueRef<T>`](crate::rvalue_ref::RValueRef); `O` converts into
+/// [`Any`].
pub trait AsPackedCallable<I, O> {
- // Call the function in packed convention
+ /// Call the function with packed arguments.
fn call_packed(&self, packed_args: &[AnyView]) -> Result<Any>;
/// The type schema of the function, a JSON description of its signature.
diff --git a/rust/tvm-ffi/src/lib.rs b/rust/tvm-ffi/src/lib.rs
index 41229d66..79de4f47 100644
--- a/rust/tvm-ffi/src/lib.rs
+++ b/rust/tvm-ffi/src/lib.rs
@@ -243,7 +243,7 @@ pub use tvm_ffi_macros::match_any;
/// Define an object type in Rust that every language can use.
///
-/// `#[rs_object("type.Key", options...)]` registers the struct as an object
+/// `#[rs_object(type_key = "type.Key", options...)]` registers the struct as
an object
/// type with the reflection of every field, so that structural equality and
/// hashing, `repr`, deep copy and Python see the fields. The first field,
/// `base_`, is the parent type's object, embedded at the start of the struct:
@@ -265,7 +265,7 @@ pub use tvm_ffi_macros::match_any;
///
/// # Arguments
///
-/// * `"type.Key"` - The type key.
+/// * `type_key = "type.Key"` - The type key, required.
/// * `final` - The type has no subtypes.
/// * `immutable` - Shared objects of the type are never mutated, so its
/// references cross threads when its fields can
@@ -322,7 +322,7 @@ pub use tvm_ffi_macros::match_any;
/// use tvm_ffi::derive::ObjectRef;
/// use tvm_ffi::{rs_object, Object, ObjectArc};
///
-/// #[rs_object("my_ext.MyPair", final)]
+/// #[rs_object(type_key = "my_ext.MyPair", final)]
/// pub struct MyPairObj {
/// base_: Object,
/// pub first: i64,
diff --git a/rust/tvm-ffi/src/macros.rs b/rust/tvm-ffi/src/macros.rs
index 31ff2d91..f10cf237 100644
--- a/rust/tvm-ffi/src/macros.rs
+++ b/rust/tvm-ffi/src/macros.rs
@@ -76,6 +76,51 @@ macro_rules! cached_global_func {
}};
}
+/// Resolve a reflected field of the object type `N` once, and keep its getter
+/// for the process: a `&'static FieldGetter<N>`.
+///
+/// Each call site has its own cache, which later calls read without a lock.
+/// `N` must be a concrete type, not `Self` or a generic parameter. It panics,
+/// naming the field, if `N` has no such field. See also
+/// [`FieldGetter::of`](crate::reflection::FieldGetter::of), which returns an
+/// error instead.
+///
+/// # Examples
+///
+/// ```
+/// use tvm_ffi::{cached_field_getter, rs_object, Object, ObjectArc};
+///
+/// #[rs_object(type_key = "my_ext.Point")]
+/// pub struct PointObj {
+/// base_: Object,
+/// x: i64,
+/// }
+///
+/// let point = ObjectArc::new(PointObj { base_: Object::new(), x: 3 });
+/// let x: i64 = cached_field_getter!(PointObj, "x").get(&*point)?;
+/// assert_eq!(x, 3);
+/// # Ok::<(), tvm_ffi::Error>(())
+/// ```
+#[macro_export]
+macro_rules! cached_field_getter {
+ ($owner:ty, $name:literal) => {{
+ static GETTER: $crate::extra::cold_init_lazy_lock::ColdInitLazyLock<
+ $crate::reflection::FieldGetter<$owner>,
+ > = $crate::extra::cold_init_lazy_lock::ColdInitLazyLock::new(|| {
+
$crate::reflection::FieldGetter::of::<$owner>($name).unwrap_or_else(|_| {
+ ::core::panic!(::core::concat!(
+ "`",
+ ::core::stringify!($owner),
+ "` has no reflected field `",
+ $name,
+ "`"
+ ))
+ })
+ });
+ &*GETTER
+ }};
+}
+
/// Turn the return code of a safe call into a `Result`, taking the raised
/// error when it is nonzero: `check_safe_call!(function(args))?`.
#[doc(hidden)]
@@ -92,6 +137,33 @@ macro_rules! check_safe_call {
}};
}
+/// Make an [`Error`](crate::Error) of a kind with a formatted message,
+/// recording the source location in the backtrace, as [`bail!`](crate::bail)
+/// does without returning.
+///
+/// ```
+/// use tvm_ffi::{format_err, Result, KEY_ERROR};
+///
+/// fn lookup(names: &[&str], name: &str) -> Result<usize> {
+/// names.iter().position(|n| *n == name).ok_or_else(||
format_err!(KEY_ERROR, "no {name}"))
+/// }
+///
+/// assert_eq!(lookup(&["a"], "b").unwrap_err().to_string(), "KeyError: no b");
+/// ```
+#[macro_export]
+macro_rules! format_err {
+ ($error_kind:expr, $fmt:expr $(, $args:expr)* $(,)?) => {{
+ let context = ::std::format!(
+ " File \"{}\", line {}, in {}\n",
+ ::core::file!(),
+ ::core::line!(),
+ $crate::function_name!()
+ );
+ $crate::error::Error::new($error_kind, &::std::format!($fmt $(,
$args)*))
+ .with_backtrace(&context)
+ }};
+}
+
/// Return an [`Error`](crate::Error) of a kind with a formatted message from
/// the current function.
///
@@ -127,13 +199,7 @@ macro_rules! check_safe_call {
#[macro_export]
macro_rules! bail {
($error_kind:expr, $fmt:expr $(, $args:expr)* $(,)?) => {{
- let context = ::std::format!(
- " File \"{}\", line {}, in {}\n",
- ::core::file!(),
- ::core::line!(),
- $crate::function_name!()
- );
- return
::core::result::Result::Err($crate::error::Error::new($error_kind,
&::std::format!($fmt $(, $args)*), &context));
+ return ::core::result::Result::Err($crate::format_err!($error_kind,
$fmt $(, $args)*));
}};
}
@@ -396,7 +462,7 @@ macro_rules! impl_object_ref_upcast {
/// use tvm_ffi::{Object, ObjectArc, ObjectRefCore};
/// use tvm_ffi::object::ObjectRefAnyCompatible;
///
-/// #[rs_object("my_ext.MyPair", final)]
+/// #[rs_object(type_key = "my_ext.MyPair", final)]
/// pub struct MyPairObj {
/// base_: Object,
/// pub first: i64,
@@ -714,7 +780,7 @@ macro_rules! tvm_ffi_dll_export_typed_func {
$crate::function_internal::call_packed_callable($func,
packed_args)
})) {
::core::result::Result::Ok(ret_value) => ret_value,
- ::core::result::Result::Err(payload) =>
::core::result::Result::Err($crate::function_internal::panic_to_error(payload)),
+ ::core::result::Result::Err(payload) =>
::core::result::Result::Err($crate::error::Error::from_panic(payload)),
};
match ret_value {
::core::result::Result::Ok(value) => {
@@ -785,7 +851,7 @@ macro_rules! __tvm_ffi_dll_export_metadata {
$crate::function_internal::exported_metadata(&$func)
})) {
::core::result::Result::Ok(metadata) => metadata,
- ::core::result::Result::Err(payload) =>
::core::result::Result::Err($crate::function_internal::panic_to_error(payload)),
+ ::core::result::Result::Err(payload) =>
::core::result::Result::Err($crate::error::Error::from_panic(payload)),
};
$crate::function_internal::write_exported_str(metadata, result)
}
@@ -850,7 +916,7 @@ macro_rules! __tvm_ffi_dll_export_doc {
#[macro_export]
macro_rules! into_typed_fn {
// Case for 0 arguments
- ($f:expr, $trait:ident() -> $ret_ty:ty) => {{
+ ($f:expr, Fn() -> $ret_ty:ty) => {{
let _f = $f;
move || -> $ret_ty {
::core::convert::TryInto::try_into(_f.call_tuple(())?)
@@ -858,7 +924,7 @@ macro_rules! into_typed_fn {
}
}};
// Case for 1 argument
- ($f:expr, $trait:ident($t0:ty) -> $ret_ty:ty) => {{
+ ($f:expr, Fn($t0:ty) -> $ret_ty:ty) => {{
let _f = $f;
move |a0: $t0| -> $ret_ty {
::core::convert::TryInto::try_into(_f.call_tuple((a0,))?)
@@ -866,7 +932,7 @@ macro_rules! into_typed_fn {
}
}};
// Case for 2 arguments
- ($f:expr, $trait:ident($t0:ty, $t1:ty) -> $ret_ty:ty) => {{
+ ($f:expr, Fn($t0:ty, $t1:ty) -> $ret_ty:ty) => {{
let _f = $f;
move |a0: $t0, a1: $t1| -> $ret_ty {
::core::convert::TryInto::try_into(_f.call_tuple((a0, a1))?)
@@ -874,7 +940,7 @@ macro_rules! into_typed_fn {
}
}};
// Case for 3 arguments
- ($f:expr, $trait:ident($t0:ty, $t1:ty, $t2:ty) -> $ret_ty:ty) => {{
+ ($f:expr, Fn($t0:ty, $t1:ty, $t2:ty) -> $ret_ty:ty) => {{
let _f = $f;
move |a0: $t0, a1: $t1, a2: $t2| -> $ret_ty {
::core::convert::TryInto::try_into(_f.call_tuple((a0, a1, a2))?)
@@ -882,7 +948,7 @@ macro_rules! into_typed_fn {
}
}};
// Case for 4 arguments
- ($f:expr, $trait:ident($t0:ty, $t1:ty, $t2:ty, $t3:ty) -> $ret_ty:ty) => {{
+ ($f:expr, Fn($t0:ty, $t1:ty, $t2:ty, $t3:ty) -> $ret_ty:ty) => {{
let _f = $f;
move |a0: $t0, a1: $t1, a2: $t2, a3: $t3| -> $ret_ty {
::core::convert::TryInto::try_into(_f.call_tuple((a0, a1, a2,
a3))?)
@@ -890,7 +956,7 @@ macro_rules! into_typed_fn {
}
}};
// Case for 5 arguments
- ($f:expr, $trait:ident($t0:ty, $t1:ty, $t2:ty, $t3:ty, $t4:ty) ->
$ret_ty:ty) => {{
+ ($f:expr, Fn($t0:ty, $t1:ty, $t2:ty, $t3:ty, $t4:ty) -> $ret_ty:ty) => {{
let _f = $f;
move |a0: $t0, a1: $t1, a2: $t2, a3: $t3, a4: $t4| -> $ret_ty {
::core::convert::TryInto::try_into(_f.call_tuple((a0, a1, a2, a3,
a4))?)
@@ -898,7 +964,7 @@ macro_rules! into_typed_fn {
}
}};
// Case for 6 arguments
- ($f:expr, $trait:ident($t0:ty, $t1:ty, $t2:ty, $t3:ty, $t4:ty, $t5:ty) ->
$ret_ty:ty) => {{
+ ($f:expr, Fn($t0:ty, $t1:ty, $t2:ty, $t3:ty, $t4:ty, $t5:ty) ->
$ret_ty:ty) => {{
let _f = $f;
move |a0: $t0, a1: $t1, a2: $t2, a3: $t3, a4: $t4, a5: $t5| -> $ret_ty
{
::core::convert::TryInto::try_into(_f.call_tuple((a0, a1, a2, a3,
a4, a5))?)
@@ -906,7 +972,7 @@ macro_rules! into_typed_fn {
}
}};
// Case for 7 arguments
- ($f:expr, $trait:ident($t0:ty, $t1:ty, $t2:ty, $t3:ty, $t4:ty, $t5:ty,
$t6:ty)
+ ($f:expr, Fn($t0:ty, $t1:ty, $t2:ty, $t3:ty, $t4:ty, $t5:ty, $t6:ty)
-> $ret_ty:ty) => {{
let _f = $f;
move |a0: $t0, a1: $t1, a2: $t2, a3: $t3, a4: $t4, a5: $t5, a6: $t6|
-> $ret_ty {
@@ -915,7 +981,7 @@ macro_rules! into_typed_fn {
}
}};
// Case for 8 arguments
- ($f:expr, $trait:ident($t0:ty, $t1:ty, $t2:ty, $t3:ty, $t4:ty, $t5:ty,
$t6:ty, $t7:ty)
+ ($f:expr, Fn($t0:ty, $t1:ty, $t2:ty, $t3:ty, $t4:ty, $t5:ty, $t6:ty,
$t7:ty)
-> $ret_ty:ty) => {{
let _f = $f;
move |a0: $t0, a1: $t1, a2: $t2, a3: $t3, a4: $t4, a5: $t5, a6: $t6,
a7: $t7| -> $ret_ty {
@@ -923,4 +989,10 @@ macro_rules! into_typed_fn {
.map_err(::core::convert::Into::into)
}
}};
+ ($f:expr, $($signature:tt)*) => {
+ ::core::compile_error!(::core::concat!(
+ "expected `Fn(A, ...) -> R` with 0 to 8 arguments, ",
+ "as in `into_typed_fn!(func, Fn(i32) -> Result<i32>)`"
+ ))
+ };
}
diff --git a/rust/tvm-ffi/src/object.rs b/rust/tvm-ffi/src/object.rs
index 10c52b3e..4e18c8d3 100644
--- a/rust/tvm-ffi/src/object.rs
+++ b/rust/tvm-ffi/src/object.rs
@@ -291,13 +291,16 @@ impl<T: ObjectCore> ObjectArc<T> {
unsafe { unsafe_::is_unique(this.ptr.as_ptr().cast::<TVMFFIObject>()) }
}
- /// The number of weak references, as `Arc::weak_count`.
+ /// The number of weak references, as `Arc::weak_count`: the weak
+ /// reference that the strong references hold together is not counted.
///
/// # Arguments
/// * `this` - The handle.
#[inline]
pub fn weak_count(this: &Self) -> usize {
- unsafe { unsafe_::weak_count(this.ptr.as_ptr().cast::<TVMFFIObject>())
}
+ // While a strong reference lives, the raw count includes the one weak
+ // reference that all strong references share.
+ unsafe { unsafe_::weak_count(this.ptr.as_ptr().cast::<TVMFFIObject>())
- 1 }
}
}
@@ -433,10 +436,12 @@ pub unsafe trait ObjectCore: Sized + 'static {
/// # Safety
///
/// The blanket implementations are the only sound ones: do not implement it.
+#[doc(hidden)]
#[diagnostic::on_unimplemented(message = "`{Self}` does not derive from
`{A}`")]
pub unsafe trait ObjectDerivesFrom<A, Depth> {}
/// Inferred depths for [`ObjectDerivesFrom`].
+#[doc(hidden)]
pub mod marker {
/// `A` is the object type itself.
pub enum Itself {}
@@ -547,6 +552,31 @@ pub unsafe trait ObjectRefCore: Sized + Clone +
AnyCompatible {
== ObjectArc::as_ptr(Other::data(other)).cast::<()>()
}
+ /// The registered type key of the object's runtime type, such as
+ /// `"ffi.Array"`, as C++ `ObjectRef::GetTypeKey`.
+ #[inline]
+ fn type_key(&self) -> &'static str {
+ // SAFETY: ObjectRefCore guarantees a live object with a registered
+ // type index.
+ let type_index =
+ unsafe {
(*ObjectArc::as_ptr(Self::data(self)).cast::<TVMFFIObject>()).type_index };
+ registered_type_key(type_index)
+ }
+
+ /// Whether the reference is a `T` as is, such as a subtype or a container
+ /// whose elements are checked too, by the strict check of
+ /// [`downcast`](Self::downcast), without a reference count change.
+ /// See also [`crate::Any::is`].
+ #[inline(always)]
+ fn is<T: AnyCompatible>(&self) -> bool {
+ let mut any_data = TVMFFIAny::new();
+ // SAFETY: the view of `self` is a valid cell while `self` is borrowed.
+ unsafe {
+ Self::copy_to_any_view(self, &mut any_data);
+ T::check_any_strict(&any_data)
+ }
+ }
+
/// Borrow the underlying object as `T` when it is one.
///
/// `T` is an object type such as `MyPairObj`, or a managed reference such
@@ -631,7 +661,7 @@ pub unsafe trait ObjectRefCore: Sized + Clone +
AnyCompatible {
/// use tvm_ffi::{rs_object, Any, AnyView, Object, ObjectArc};
/// use tvm_ffi::object::ObjectRefAnyCompatible;
///
-/// #[rs_object("my_ext.MyPair", final)]
+/// #[rs_object(type_key = "my_ext.MyPair", final)]
/// pub struct MyPairObj {
/// base_: Object,
/// pub first: i64,
@@ -960,6 +990,15 @@ pub(crate) fn into_root_object_ref<S:
ObjectRefCore>(value: S) -> ObjectRef {
}
}
+/// The type key of `type_index`, the type index of a value, which the runtime
+/// always registers.
+#[inline]
+pub(crate) fn registered_type_key(type_index: i32) -> &'static str {
+ // SAFETY: a value's type index is registered, and type information lives
+ // as long as the process; a type key is UTF-8 text.
+ unsafe { (*TVMFFIGetTypeInfo(type_index)).type_key.as_str() }
+}
+
/// The registered type key of `type_index`, or `<type_index N>` when it has
none.
pub(crate) fn type_key_of_index(type_index: i32) -> String {
match crate::reflection::type_key(type_index) {
@@ -1019,7 +1058,7 @@ pub mod unsafe_ {
/// Increase the strong reference count of `handle`.
#[inline]
- pub unsafe fn inc_ref(handle: *mut TVMFFIObject) {
+ pub(crate) unsafe fn inc_ref(handle: *mut TVMFFIObject) {
let obj = &*handle;
obj.combined_ref_count.fetch_add(1, Ordering::Relaxed);
}
diff --git a/rust/tvm-ffi/src/optional.rs b/rust/tvm-ffi/src/optional.rs
index d17208df..3a895661 100644
--- a/rust/tvm-ffi/src/optional.rs
+++ b/rust/tvm-ffi/src/optional.rs
@@ -93,6 +93,11 @@ pub struct Optional<T: OptionalCompatible> {
_marker: PhantomData<T>,
}
+// SAFETY: the cell holds only a `T` or `None`, so sending or sharing an
+// `Optional` is as safe as sending or sharing its value.
+unsafe impl<T: OptionalCompatible + Send + Sync> Send for Optional<T> {}
+unsafe impl<T: OptionalCompatible + Send + Sync> Sync for Optional<T> {}
+
// Must stay 16 bytes / `TVMFFIAny`-aligned to overlay a C++ `ffi::Optional<T>`
// field in place, independent of `T`.
const _: () = assert!(
diff --git a/rust/tvm-ffi/src/reflection/accessor.rs
b/rust/tvm-ffi/src/reflection/accessor.rs
index 8b99d7f7..0f008790 100644
--- a/rust/tvm-ffi/src/reflection/accessor.rs
+++ b/rust/tvm-ffi/src/reflection/accessor.rs
@@ -20,6 +20,7 @@
//! Reads of registered types: methods, fields and type attributes.
use std::ffi::c_void;
+use std::marker::PhantomData;
use std::ptr::NonNull;
use std::sync::{PoisonError, RwLock};
@@ -75,7 +76,6 @@ impl TypeAttrColumn {
Error::new(
crate::INTERNAL_ERROR,
&format!("type attribute column `{name}` was not created"),
- "",
)
})
}
@@ -286,14 +286,29 @@ pub fn get_method_by_index(type_index: i32, method_name:
&str) -> Result<Functio
}
}
/// Resolves a reflected field once, then uses its registered C ABI getter.
-#[derive(Clone, Copy)]
-pub struct FieldGetter {
+///
+/// [`FieldGetter::new`] resolves a field by runtime type index into a
+/// `FieldGetter`, which checks the type of each object it reads.
[`FieldGetter::of`] resolves a field of the
+/// object type `N` into a `FieldGetter<N>`, which reads only `&N`, so the
+/// type system replaces that check;
[`cached_field_getter!`](crate::cached_field_getter)
+/// keeps one for the process.
+pub struct FieldGetter<N = ()> {
owner_type_index: i32,
owner_type_depth: i32,
field_offset: i64,
getter: TVMFFIFieldGetter,
+ owner: PhantomData<fn(&N)>,
+}
+
+impl<N> Clone for FieldGetter<N> {
+ #[inline(always)]
+ fn clone(&self) -> Self {
+ *self
+ }
}
+impl<N> Copy for FieldGetter<N> {}
+
impl FieldGetter {
/// Resolve a reflected field declared by `type_index` or one of its bases.
///
@@ -302,40 +317,16 @@ impl FieldGetter {
/// Returns a `TypeError` if `type_index` is not registered or the type has
/// no reflected field `field_name` with a getter.
pub fn new(type_index: i32, field_name: &str) -> Result<Self> {
- // The C API aborts the process on an unregistered index.
- if !is_registered(type_index) {
- return Err(Error::new(
- TYPE_ERROR,
- &format!("Cannot find type info for type_index={type_index}"),
- "",
- ));
- }
- let type_info = unsafe { TVMFFIGetTypeInfo(type_index) };
+ resolve(type_index, field_name)
+ }
- let field = unsafe { find_field(type_info, field_name) }.ok_or_else(||
{
- let type_key = unsafe { (*type_info).type_key.as_str() };
- Error::new(
- TYPE_ERROR,
- &format!("Cannot find reflected field `{field_name}` in type
`{type_key}`"),
- "",
- )
- })?;
- let field = unsafe { field.as_ref() };
- let getter = field.getter.ok_or_else(|| {
- // SAFETY: a field name is UTF-8 text.
- let name = unsafe { field.name.as_str() };
- Error::new(
- TYPE_ERROR,
- &format!("Reflected field `{name}` has no getter"),
- "",
- )
- })?;
- Ok(Self {
- owner_type_index: type_index,
- owner_type_depth: unsafe { (*type_info).type_depth },
- field_offset: field.offset,
- getter,
- })
+ /// Resolve a reflected field declared by `N` or one of its bases.
+ ///
+ /// # Errors
+ ///
+ /// As [`FieldGetter::new`] for `N::type_index()`.
+ pub fn of<N: ObjectCore>(field_name: &str) -> Result<FieldGetter<N>> {
+ resolve(N::type_index(), field_name)
}
/// Read the field as an owning [`Any`].
@@ -357,13 +348,48 @@ impl FieldGetter {
&format!(
"Cannot read a field of type_index={} from object
type_index={dynamic_type_index}",
self.owner_type_index
- ),
- "",
- ));
+ )));
}
+ // SAFETY: `object` has the resolved type or a subtype, which embeds
it.
+ unsafe { self.read(object) }
+ }
+
+ /// Read and convert the field to `T`.
+ pub fn get<N, T>(&self, object: &N) -> Result<T>
+ where
+ N: ObjectCore,
+ T: TryFrom<Any, Error = Error>,
+ {
+ T::try_from(self.get_any(object)?)
+ }
+}
+
+impl<N: ObjectCore> FieldGetter<N> {
+ /// Read the field as an owning [`Any`].
+ #[inline(always)]
+ pub fn get_any(&self, object: &N) -> Result<Any> {
+ // SAFETY: the field was resolved for `N`, whose layout `object` has.
+ unsafe { self.read(object) }
+ }
+
+ /// Read and convert the field to `T`.
+ #[inline(always)]
+ pub fn get<T>(&self, object: &N) -> Result<T>
+ where
+ T: TryFrom<Any, Error = Error>,
+ {
+ T::try_from(self.get_any(object)?)
+ }
+}
+impl<N> FieldGetter<N> {
+ /// # Safety
+ ///
+ /// `object` has the layout of the type the field was resolved for.
+ #[inline(always)]
+ unsafe fn read<M>(&self, object: &M) -> Result<Any> {
let field_address = unsafe {
- object_pointer
+ std::ptr::from_ref(object)
.cast::<u8>()
.offset(self.field_offset as isize)
.cast_mut()
@@ -375,15 +401,41 @@ impl FieldGetter {
}
Ok(result)
}
+}
- /// Read and convert the field to `T`.
- pub fn get<N, T>(&self, object: &N) -> Result<T>
- where
- N: ObjectCore,
- T: TryFrom<Any, Error = Error>,
- {
- T::try_from(self.get_any(object)?)
+fn resolve<N>(type_index: i32, field_name: &str) -> Result<FieldGetter<N>> {
+ // The C API aborts the process on an unregistered index.
+ if !is_registered(type_index) {
+ return Err(Error::new(
+ TYPE_ERROR,
+ &format!("Cannot find type info for type_index={type_index}"),
+ ));
}
+ let type_info = unsafe { TVMFFIGetTypeInfo(type_index) };
+
+ let field = unsafe { find_field(type_info, field_name) }.ok_or_else(|| {
+ let type_key = unsafe { (*type_info).type_key.as_str() };
+ Error::new(
+ TYPE_ERROR,
+ &format!("Cannot find reflected field `{field_name}` in type
`{type_key}`"),
+ )
+ })?;
+ let field = unsafe { field.as_ref() };
+ let getter = field.getter.ok_or_else(|| {
+ // SAFETY: a field name is UTF-8 text.
+ let name = unsafe { field.name.as_str() };
+ Error::new(
+ TYPE_ERROR,
+ &format!("Reflected field `{name}` has no getter"),
+ )
+ })?;
+ Ok(FieldGetter {
+ owner_type_index: type_index,
+ owner_type_depth: unsafe { (*type_info).type_depth },
+ field_offset: field.offset,
+ getter,
+ owner: PhantomData,
+ })
}
unsafe fn find_field(
diff --git a/rust/tvm-ffi/src/reflection/enum_def.rs
b/rust/tvm-ffi/src/reflection/enum_def.rs
index 63278daf..3c2e25b7 100644
--- a/rust/tvm-ffi/src/reflection/enum_def.rs
+++ b/rust/tvm-ffi/src/reflection/enum_def.rs
@@ -117,14 +117,12 @@ impl<E: EnumRef> EnumDef<E> {
return Err(Error::new(
VALUE_ERROR,
&format!("a value name of `{type_key}` must be non-empty"),
- "",
));
}
if !is_instance_of::<EnumObj>(type_index) || is_int_enum(type_index) {
return Err(Error::new(
TYPE_ERROR,
&format!("`{type_key}` is not an enum type whose values Rust
can register"),
- "",
));
}
// The assert above sees only the Rust mirror, which may be opaque: a
@@ -142,7 +140,6 @@ impl<E: EnumRef> EnumDef<E> {
"`{type_key}` objects have fields beyond `ffi.Enum`, so
Rust cannot \
register its values"
),
- "",
));
}
let _registering = lock_type_registration();
@@ -156,7 +153,6 @@ impl<E: EnumRef> EnumDef<E> {
return Err(Error::new(
VALUE_ERROR,
&format!("Enum `{type_key}` already has a value named
`{name}`"),
- "",
));
}
let object = ObjectArc::into_raw(ObjectArc::new(EnumObj::new(index,
name)));
diff --git a/rust/tvm-ffi/src/reflection/native_function.rs
b/rust/tvm-ffi/src/reflection/native_function.rs
index 06f42b02..8f6d19aa 100644
--- a/rust/tvm-ffi/src/reflection/native_function.rs
+++ b/rust/tvm-ffi/src/reflection/native_function.rs
@@ -42,7 +42,7 @@ use crate::{Any, AnyView, Error, Function, Result,
TYPE_ERROR};
/// it. Reading a hook with [`from_any`](Self::from_any) is `unsafe`, the
/// caller vouching for the signature, and then calling it is safe. Building a
/// hook from a Rust function with `into()` is safe; storing it, with
-/// [`TypeAttrDef::native_fn_attr`](super::TypeAttrDef::native_fn_attr) or
+///
[`TypeAttrDef::set_native_fn_attr`](super::TypeAttrDef::set_native_fn_attr) or
/// [`EnumDef::set_native_fn_attr`](super::EnumDef::set_native_fn_attr), or
/// converting it to [`Any`] for another registry with
/// [`into_any`](Self::into_any), is `unsafe`, the caller vouching that the
@@ -54,7 +54,7 @@ use crate::{Any, AnyView, Error, Function, Result,
TYPE_ERROR};
/// use tvm_ffi::reflection::{NativeFunction, TypeAttrColumn, TypeAttrDef};
/// use tvm_ffi::{rs_object, Object, ObjectArc, ObjectCore, Result};
///
-/// #[rs_object("my_ext.MyPair")]
+/// #[rs_object(type_key = "my_ext.MyPair")]
/// pub struct MyPairObj {
/// base_: Object,
/// first: i64,
@@ -67,7 +67,7 @@ use crate::{Any, AnyView, Error, Function, Result,
TYPE_ERROR};
///
/// let hook: NativeFunction<(&MyPairObj,), i64> = sum.into();
/// // SAFETY: `my_ext.sum` holds `i64(const MyPairObj*)` hooks.
-/// unsafe { TypeAttrDef::<MyPairObj>::new().native_fn_attr("my_ext.sum",
hook)? };
+/// unsafe { TypeAttrDef::<MyPairObj>::new().set_native_fn_attr("my_ext.sum",
hook)? };
///
/// let stored =
TypeAttrColumn::lookup("my_ext.sum").unwrap().get(MyPairObj::type_index()).unwrap();
/// // SAFETY: `my_ext.sum` holds `i64(const MyPairObj*)` hooks.
@@ -119,8 +119,7 @@ impl<Args: NativeArgs, R: TryFrom<Any>>
NativeFunction<Args, R> {
if result.type_index() == TVMFFITypeIndex::kTVMFFIError as i32 {
return Err(result.try_into()?);
}
- R::try_from(result)
- .map_err(|_| Error::new(TYPE_ERROR, "Packed hook result type
mismatch", ""))
+ R::try_from(result).map_err(|_| Error::new(TYPE_ERROR, "Packed hook
result type mismatch"))
}
}
@@ -169,7 +168,7 @@ impl<Args, R> NativeFunction<Args, R> {
"Cannot convert from type `{}` to `NativeFunction`",
type_key_of_index(raw.type_index)
);
- return Err(Error::new(TYPE_ERROR, &message, ""));
+ return Err(Error::new(TYPE_ERROR, &message));
}
Ok(Self {
data: value,
@@ -332,7 +331,7 @@ mod sealed {
// argument for the duration of the call, or null.
$(let Some($raw) = (unsafe { $raw.as_ref() }) else {
let message = "A native hook argument is null";
- return into_raw::<R>(Err(Error::new(TYPE_ERROR,
message, "")));
+ return into_raw::<R>(Err(Error::new(TYPE_ERROR,
message)));
};)*
// A panic aborts here rather than unwind into the
caller.
into_raw(function($($raw),*))
diff --git a/rust/tvm-ffi/src/reflection/registry.rs
b/rust/tvm-ffi/src/reflection/registry.rs
index 05595700..5f893ea2 100644
--- a/rust/tvm-ffi/src/reflection/registry.rs
+++ b/rust/tvm-ffi/src/reflection/registry.rs
@@ -174,7 +174,7 @@ impl BitOr for FieldFlags {
/// use tvm_ffi::reflection::{self, ObjectDef};
/// use tvm_ffi::{rs_object, Object, ObjectArc, Result};
///
-/// #[rs_object("my_ext.MyPair", final, reflection =
MyPairObj::reflect_methods)]
+/// #[rs_object(type_key = "my_ext.MyPair", final, reflection =
MyPairObj::reflect_methods)]
/// pub struct MyPairObj {
/// base_: Object,
/// pub first: i64,
@@ -378,7 +378,6 @@ impl<N: ObjectCore> ObjectDef<N> {
self.error = Some(Error::new(
VALUE_ERROR,
&format!("duplicate method `{name}` of `{}`", N::TYPE_KEY),
- "",
));
return self;
}
@@ -417,7 +416,6 @@ impl<N: ObjectCore> ObjectDef<N> {
self.error = Some(Error::new(
error.kind(),
&format!("registering method `{name}`: {}", error.message()),
- "",
));
}
self
@@ -487,7 +485,7 @@ pub unsafe fn register_object<N: ObjectCore + ShallowCopy>(
}
/// Registers type attributes of the object type `T`, each as soon as
-/// [`attr`](Self::attr) names it, for every language to read with
+/// [`set_attr`](Self::set_attr) names it, for every language to read with
/// [`TypeAttrColumn`](crate::reflection::TypeAttrColumn).
///
/// As in C++, attributes register during static initialization, such as in a
@@ -502,12 +500,12 @@ pub unsafe fn register_object<N: ObjectCore +
ShallowCopy>(
/// use tvm_ffi::reflection::TypeAttrColumn;
/// use tvm_ffi::{rs_object, Object, ObjectCore};
///
-/// #[rs_object("my_ext.MyPair")]
+/// #[rs_object(type_key = "my_ext.MyPair")]
/// pub struct MyPairObj {
/// base_: Object,
/// }
///
-/// TypeAttrDef::<MyPairObj>::new().attr("my_ext.arity", 2_i64)?;
+/// TypeAttrDef::<MyPairObj>::new().set_attr("my_ext.arity", 2_i64)?;
///
/// let column = TypeAttrColumn::lookup("my_ext.arity").unwrap();
/// let arity: i64 = column.get(MyPairObj::type_index()).unwrap().try_into()?;
@@ -541,8 +539,8 @@ impl<T: ObjectCore> TypeAttrDef<T> {
///
/// Returns an error if `T` already has the attribute `name`, or a
/// `TypeError` if `value` is an opaque pointer: register a native hook
- /// with [`native_fn_attr`](Self::native_fn_attr).
- pub fn attr(self, name: &str, value: impl Into<Any> + Send + Sync) ->
Result<Self> {
+ /// with [`set_native_fn_attr`](Self::set_native_fn_attr).
+ pub fn set_attr(self, name: &str, value: impl Into<Any> + Send + Sync) ->
Result<Self> {
let value = value.into();
ensure_not_opaque_ptr(name, &value)?;
self.register(name, value)
@@ -567,7 +565,7 @@ impl<T: ObjectCore> TypeAttrDef<T> {
///
/// Every caller of the column `name` calls its hooks with exactly `Args`
/// and reads `R`: a native caller calls the function pointer unchecked.
- pub unsafe fn native_fn_attr<Args, R>(
+ pub unsafe fn set_native_fn_attr<Args, R>(
self,
name: &str,
f: NativeFunction<Args, R>,
@@ -618,7 +616,6 @@ fn shallow_copy_any<N: ObjectCore + ShallowCopy>(object:
&ObjectRef) -> Result<A
N::TYPE_KEY,
object_type_key(object)
),
- "",
));
};
let copy = ObjectArc::new(node.shallow_copy());
@@ -666,7 +663,7 @@ unsafe extern "C" fn field_getter<F: AnyCompatible>(
Ok(()) => 0,
Err(_) => {
let message = format!("panic while reading a field of type {}",
F::type_str());
- Error::set_raised(&Error::new(INTERNAL_ERROR, &message, ""));
+ Error::set_raised(&Error::new(INTERNAL_ERROR, &message));
-1
}
}
@@ -697,7 +694,6 @@ unsafe extern "C" fn field_setter<F: AnyCompatible>(
F::type_str(),
F::get_mismatch_type_info(&*value)
),
- "",
)),
}
}));
@@ -707,7 +703,6 @@ unsafe extern "C" fn field_setter<F: AnyCompatible>(
Err(_) => Error::new(
INTERNAL_ERROR,
&format!("panic while writing a field of type {}", F::type_str()),
- "",
),
};
Error::set_raised(&error);
diff --git a/rust/tvm-ffi/src/reflection/unsafe_init.rs
b/rust/tvm-ffi/src/reflection/unsafe_init.rs
index d11fea85..28a0410b 100644
--- a/rust/tvm-ffi/src/reflection/unsafe_init.rs
+++ b/rust/tvm-ffi/src/reflection/unsafe_init.rs
@@ -209,7 +209,7 @@ pub unsafe extern "C" fn unsafe_init_creator<T: UnsafeInit>(
Ok(()) => 0,
Err(_) => {
let message = format!("panic while creating an object of `{}`",
T::TYPE_KEY);
- Error::set_raised(&Error::new(INTERNAL_ERROR, &message, ""));
+ Error::set_raised(&Error::new(INTERNAL_ERROR, &message));
-1
}
}
diff --git a/rust/tvm-ffi/src/rvalue_ref.rs b/rust/tvm-ffi/src/rvalue_ref.rs
index d63cc456..858a7d94 100644
--- a/rust/tvm-ffi/src/rvalue_ref.rs
+++ b/rust/tvm-ffi/src/rvalue_ref.rs
@@ -196,6 +196,5 @@ where
"{prefix}Cannot convert from `{source}` to `RValueRef<{}>`",
T::type_str()
),
- "",
)
}
diff --git a/rust/tvm-ffi/src/static_init.rs b/rust/tvm-ffi/src/static_init.rs
index 35ea6be1..b137df8b 100644
--- a/rust/tvm-ffi/src/static_init.rs
+++ b/rust/tvm-ffi/src/static_init.rs
@@ -28,7 +28,6 @@ use std::ffi::c_void;
use std::panic::catch_unwind;
use std::sync::{Once, OnceLock};
-use crate::function_internal::panic_to_error;
use crate::{Any, Error, Function, Result, INTERNAL_ERROR};
#[doc(hidden)]
@@ -234,7 +233,7 @@ impl StaticInitBlock {
(None, Ok(())) => Ok(()),
(None, Err(text)) => {
let message = format!("The static init block at {} failed:
{text}", self.place());
- Err(Error::new(INTERNAL_ERROR, &message, ""))
+ Err(Error::new(INTERNAL_ERROR, &message))
}
}
}
@@ -246,7 +245,7 @@ impl StaticInitBlock {
fn run_once(&self) -> Result<()> {
let marker = self.marker();
if Function::get_global(&marker).is_err() {
- catch_unwind(self.body).unwrap_or_else(|payload|
Err(panic_to_error(payload)))?;
+ catch_unwind(self.body).unwrap_or_else(|payload|
Err(Error::from_panic(payload)))?;
Function::register_global(&marker, Function::from_packed(|_|
Ok(Any::default())))?;
}
Ok(())
@@ -310,7 +309,7 @@ pub fn run_static_init_blocks() -> Result<()> {
error.message()
));
}
- Err(Error::new(failures[0].1.kind(), &message, ""))
+ Err(Error::new(failures[0].1.kind(), &message))
}
/// The constructor of every [`static_init_block!`](crate::static_init_block),
which the loader
diff --git a/rust/tvm-ffi/src/string.rs b/rust/tvm-ffi/src/string.rs
index 2eba2150..bf5929ef 100644
--- a/rust/tvm-ffi/src/string.rs
+++ b/rust/tvm-ffi/src/string.rs
@@ -114,14 +114,9 @@ unsafe impl ObjectCoreWithExtraItems for BytesObj {
}
}
-impl<T> From<T> for Bytes
-where
- T: AsRef<[u8]>,
-{
- #[inline]
- /// Copy the bytes of `src`.
- fn from(src: T) -> Self {
- let value: &[u8] = src.as_ref();
+impl From<&[u8]> for Bytes {
+ /// Copy the bytes of `value`.
+ fn from(value: &[u8]) -> Self {
// to be compatible with normal c++
const MAX_SMALL_BYTES_LEN: usize = 7;
unsafe {
@@ -224,17 +219,64 @@ impl Hash for Bytes {
}
}
-impl Display for Bytes {
+/// Formats as `[u8]` does, such as `[97, 98]`.
+impl Debug for Bytes {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- Debug::fmt(&self.as_slice(), f)
+ Debug::fmt(self.as_slice(), f)
}
}
-impl Debug for Bytes {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- f.debug_struct("ffi.Bytes")
- .field("data", &self.as_slice())
- .finish()
+macro_rules! impl_bytes_from {
+ ($([$($g:tt)*] $t:ty),* $(,)?) => {
+ $(impl<$($g)*> From<$t> for Bytes {
+ #[inline]
+ fn from(value: $t) -> Self {
+ Self::from(&value[..])
+ }
+ })*
+ };
+}
+
+impl_bytes_from!([] &mut [u8], [] Vec<u8>, [] &Vec<u8>, [] Box<[u8]>, [const
N: usize] [u8; N], [const N: usize] &[u8; N]);
+
+macro_rules! impl_bytes_eq {
+ ($([$($g:tt)*] $t:ty),* $(,)?) => {
+ $(impl<$($g)*> PartialEq<$t> for Bytes {
+ #[inline]
+ fn eq(&self, other: &$t) -> bool {
+ self.as_slice() == &other[..]
+ }
+ }
+
+ impl<$($g)*> PartialEq<Bytes> for $t {
+ #[inline]
+ fn eq(&self, other: &Bytes) -> bool {
+ &self[..] == other.as_slice()
+ }
+ })*
+ };
+}
+
+impl_bytes_eq!([] [u8], ['a] &'a [u8], [] Vec<u8>, [const N: usize] [u8; N],
['a, const N: usize] &'a [u8; N]);
+
+impl AsRef<[u8]> for Bytes {
+ #[inline]
+ fn as_ref(&self) -> &[u8] {
+ self.as_slice()
+ }
+}
+
+impl std::borrow::Borrow<[u8]> for Bytes {
+ #[inline]
+ fn borrow(&self) -> &[u8] {
+ self.as_slice()
+ }
+}
+
+impl From<Bytes> for Vec<u8> {
+ #[inline]
+ fn from(value: Bytes) -> Self {
+ value.as_slice().to_vec()
}
}
@@ -331,15 +373,10 @@ impl String {
}
}
-impl<T> From<T> for String
-where
- T: AsRef<str>,
-{
- #[inline]
- /// Copy the string `src`.
- fn from(src: T) -> Self {
+impl From<&str> for String {
+ /// Copy the string `value`.
+ fn from(value: &str) -> Self {
unsafe {
- let value: &str = src.as_ref();
let bytes = value.as_bytes();
const MAX_SMALL_BYTES_LEN: usize = 7;
if bytes.len() <= MAX_SMALL_BYTES_LEN {
@@ -423,29 +460,69 @@ impl PartialEq for String {
}
}
-// Allows `my_string == "hello"`
-impl<T> PartialEq<T> for String
-where
- T: AsRef<str>,
-{
+macro_rules! impl_string_from {
+ ($([$($g:tt)*] $t:ty),* $(,)?) => {
+ $(impl<$($g)*> From<$t> for String {
+ #[inline]
+ fn from(value: $t) -> Self {
+ Self::from(&value[..])
+ }
+ })*
+ };
+}
+
+impl_string_from!([] &mut str, [] std::string::String, []
&std::string::String, [] Box<str>, ['a] std::borrow::Cow<'a, str>);
+
+macro_rules! impl_string_eq {
+ ($([$($g:tt)*] $t:ty),* $(,)?) => {
+ $(impl<$($g)*> PartialEq<$t> for String {
+ #[inline]
+ fn eq(&self, other: &$t) -> bool {
+ self.as_str() == &other[..]
+ }
+ }
+
+ impl<$($g)*> PartialEq<String> for $t {
+ #[inline]
+ fn eq(&self, other: &String) -> bool {
+ &self[..] == other.as_str()
+ }
+ })*
+ };
+}
+
+impl_string_eq!([] str, ['a] &'a str, [] std::string::String, ['a]
std::borrow::Cow<'a, str>);
+
+impl AsRef<str> for String {
#[inline]
- fn eq(&self, other: &T) -> bool {
- self.as_str() == other.as_ref()
+ fn as_ref(&self) -> &str {
+ self.as_str()
}
}
-impl Eq for String {}
+impl AsRef<[u8]> for String {
+ #[inline]
+ fn as_ref(&self) -> &[u8] {
+ self.as_bytes()
+ }
+}
+
+impl std::borrow::Borrow<str> for String {
+ #[inline]
+ fn borrow(&self) -> &str {
+ self.as_str()
+ }
+}
-impl<T> PartialEq<T> for Bytes
-where
- T: AsRef<[u8]>,
-{
+impl From<String> for std::string::String {
#[inline]
- fn eq(&self, other: &T) -> bool {
- self.as_slice() == other.as_ref()
+ fn from(value: String) -> Self {
+ value.as_str().to_owned()
}
}
+impl Eq for String {}
+
impl PartialOrd for String {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
@@ -473,11 +550,10 @@ impl Display for String {
}
}
+/// Formats as `str` does, such as `"hi"`.
impl Debug for String {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- f.debug_struct("ffi.String")
- .field("data", &self.as_str())
- .finish()
+ Debug::fmt(self.as_str(), f)
}
}
diff --git a/rust/tvm-ffi/tests/test_device.rs
b/rust/tvm-ffi/tests/test_device.rs
index 05467e95..d70f35d3 100644
--- a/rust/tvm-ffi/tests/test_device.rs
+++ b/rust/tvm-ffi/tests/test_device.rs
@@ -42,7 +42,7 @@ fn test_device_stream_restored_after_error() {
// SAFETY: dummy_stream is a test value; CPU device accepts any stream
handle.
let result: Result<()> = unsafe {
with_stream(&device, dummy_stream, || {
- Err(Error::new(VALUE_ERROR, "closure failed", ""))
+ Err(Error::new(VALUE_ERROR, "closure failed"))
})
};
assert_eq!(result.unwrap_err().message(), "closure failed");
diff --git a/rust/tvm-ffi/tests/test_error.rs b/rust/tvm-ffi/tests/test_error.rs
index e209ddd7..51ea1a61 100644
--- a/rust/tvm-ffi/tests/test_error.rs
+++ b/rust/tvm-ffi/tests/test_error.rs
@@ -20,7 +20,7 @@ use tvm_ffi::*;
#[test]
fn test_error_new() {
- let error = Error::new(RUNTIME_ERROR, "test error", "test traceback");
+ let error = Error::new(RUNTIME_ERROR, "test error").with_backtrace("test
traceback");
assert_eq!(error.kind(), RUNTIME_ERROR);
assert_eq!(error.message(), "test error");
assert_eq!(error.backtrace(), "test traceback");
@@ -28,7 +28,7 @@ fn test_error_new() {
#[test]
fn test_error_from_raised() {
- let error0 = Error::new(RUNTIME_ERROR, "test error", "test traceback");
+ let error0 = Error::new(RUNTIME_ERROR, "test error").with_backtrace("test
traceback");
Error::set_raised(&error0);
let error1 = Error::from_raised();
assert_eq!(error1.kind(), RUNTIME_ERROR);
@@ -62,13 +62,10 @@ fn test_error_with_context() {
let payload = Array::from_vec(vec![7i64]);
for shared in [false, true] {
- let error = Error::new_with_cause_and_extra_context(
- RUNTIME_ERROR,
- "outer",
- "origin",
- Some(&error0),
- Some(&payload),
- );
+ let error = Error::new(RUNTIME_ERROR, "outer")
+ .with_backtrace("origin")
+ .with_cause(&error0)
+ .with_extra_context(&payload);
let retained = shared.then(|| error.clone());
let output = Error::with_appended_backtrace(error, " frame");
assert_eq!(output.backtrace(), "origin frame");
@@ -85,7 +82,7 @@ fn test_error_with_context() {
#[test]
fn test_error_any_convert() {
- let error = Error::new(RUNTIME_ERROR, "test error", "test traceback");
+ let error = Error::new(RUNTIME_ERROR, "test error").with_backtrace("test
traceback");
let any = Any::from(error.clone());
let error2 = any.clone().downcast_ref::<Error>().cloned().unwrap();
assert_eq!(error2.kind(), RUNTIME_ERROR);
diff --git a/rust/tvm-ffi/tests/test_function_metadata.rs
b/rust/tvm-ffi/tests/test_function_metadata.rs
index 753bc986..f0583490 100644
--- a/rust/tvm-ffi/tests/test_function_metadata.rs
+++ b/rust/tvm-ffi/tests/test_function_metadata.rs
@@ -22,7 +22,7 @@
//! `export-metadata` feature, as C++ ones with
//! `TVM_FFI_DLL_EXPORT_INCLUDE_METADATA`; global functions always do.
-use tvm_ffi::function_internal::AsPackedCallable;
+use tvm_ffi::function::AsPackedCallable;
#[cfg(feature = "export-metadata")]
use tvm_ffi::tvm_ffi_sys::TVMFFIAny;
use tvm_ffi::*;
diff --git a/rust/tvm-ffi/tests/test_object.rs
b/rust/tvm-ffi/tests/test_object.rs
index 5a09d921..71f5384d 100644
--- a/rust/tvm-ffi/tests/test_object.rs
+++ b/rust/tvm-ffi/tests/test_object.rs
@@ -89,25 +89,25 @@ fn test_object_arc() {
assert_eq!(view.debug_strong_count(), Some(1));
assert_eq!(obj_arc.value, 11);
assert_eq!(ObjectArc::strong_count(&obj_arc), 1);
- assert_eq!(ObjectArc::weak_count(&obj_arc), 1);
+ assert_eq!(ObjectArc::weak_count(&obj_arc), 0);
let ref1 = obj_arc.clone();
assert_eq!(ObjectArc::strong_count(&obj_arc), 2);
- assert_eq!(ObjectArc::weak_count(&obj_arc), 1);
+ assert_eq!(ObjectArc::weak_count(&obj_arc), 0);
let ref2 = obj_arc.clone();
assert_eq!(ObjectArc::strong_count(&obj_arc), 3);
- assert_eq!(ObjectArc::weak_count(&obj_arc), 1);
+ assert_eq!(ObjectArc::weak_count(&obj_arc), 0);
assert_eq!(ref1.value, 11);
// drop obj_arc
drop(obj_arc);
assert_eq!(ObjectArc::strong_count(&ref1), 2);
- assert_eq!(ObjectArc::weak_count(&ref1), 1);
+ assert_eq!(ObjectArc::weak_count(&ref1), 0);
assert_eq!(delete_counter.load(Ordering::Relaxed), 0);
// drop ref1
drop(ref1);
assert_eq!(ObjectArc::strong_count(&ref2), 1);
- assert_eq!(ObjectArc::weak_count(&ref2), 1);
+ assert_eq!(ObjectArc::weak_count(&ref2), 0);
assert_eq!(delete_counter.load(Ordering::Relaxed), 0);
// drop ref2
drop(ref2);
@@ -121,7 +121,7 @@ fn test_object_arc_with_extra_items() {
unsafe { ObjectArc::new_with_extra_items(TestIntObj::new(12,
delete_counter.clone(), 10)) };
assert_eq!(obj_arc.value, 12);
assert_eq!(ObjectArc::strong_count(&obj_arc), 1);
- assert_eq!(ObjectArc::weak_count(&obj_arc), 1);
+ assert_eq!(ObjectArc::weak_count(&obj_arc), 0);
assert_eq!(delete_counter.load(Ordering::Relaxed), 0);
unsafe {
let raw = ObjectArc::as_mut_ptr(&mut obj_arc);
@@ -267,7 +267,7 @@ fn test_object_arc_from_raw() {
let obj_arc2 = ObjectArc::from_raw(raw_ptr);
assert_eq!(obj_arc2.value, 11);
assert_eq!(ObjectArc::strong_count(&obj_arc2), 1);
- assert_eq!(ObjectArc::weak_count(&obj_arc2), 1);
+ assert_eq!(ObjectArc::weak_count(&obj_arc2), 0);
assert_eq!(delete_counter.load(Ordering::Relaxed), 0);
// drop obj_arc2
drop(obj_arc2);
diff --git a/rust/tvm-ffi/tests/test_object_ref_any.rs
b/rust/tvm-ffi/tests/test_object_ref_any.rs
index c7f484af..5d5dcfbd 100644
--- a/rust/tvm-ffi/tests/test_object_ref_any.rs
+++ b/rust/tvm-ffi/tests/test_object_ref_any.rs
@@ -26,7 +26,7 @@ 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)]
+#[rs_object(type_key = "testing.object_ref_any.Pair", final)]
pub struct PairObj {
base_: Object,
pub first: i64,
diff --git a/rust/tvm-ffi/tests/test_object_reflection.rs
b/rust/tvm-ffi/tests/test_object_reflection.rs
index 7d0f54cf..969a85d3 100644
--- a/rust/tvm-ffi/tests/test_object_reflection.rs
+++ b/rust/tvm-ffi/tests/test_object_reflection.rs
@@ -348,10 +348,12 @@ fn
test_types_with_only_methods_register_their_reflection() {
#[test]
fn test_reflected_fields_read_and_write_through_the_c_abi() {
let p3 = point3(3, 4);
- let x = FieldGetter::new(Point3Obj::type_index(), "x").unwrap();
- let z = FieldGetter::new(Point3Obj::type_index(), "z").unwrap();
- assert_eq!(x.get::<_, i64>(&*p3.data).unwrap(), 3);
- assert_eq!(z.get::<_, i64>(&*p3.data).unwrap(), 4);
+ let x = FieldGetter::of::<Point3Obj>("x").unwrap();
+ assert_eq!(x.get::<i64>(&p3.data).unwrap(), 3);
+ let z: i64 = tvm_ffi::cached_field_getter!(Point3Obj, "z")
+ .get(&p3.data)
+ .unwrap();
+ assert_eq!(z, 4);
let p = point(1, 2.0, "p");
let y = &fields(type_info::<PointObj>())[1];
@@ -600,13 +602,13 @@ fn test_native_function() -> Result<()> {
// A Rust closure as a native hook, whose error is the hook's.
let x_of = |p: &PointObj| -> Result<i64> {
match p.x {
- 0 => Err(Error::new(VALUE_ERROR, "zero", "")),
+ 0 => Err(Error::new(VALUE_ERROR, "zero")),
x => Ok(x),
}
};
let hook = NativeFunction::<(&PointObj,), i64>::from(x_of);
// SAFETY: `testing.x_of` holds `i64(const PointObj*)` hooks.
- unsafe { TypeAttrDef::<PointObj>::new().native_fn_attr("testing.x_of",
hook)? };
+ unsafe { TypeAttrDef::<PointObj>::new().set_native_fn_attr("testing.x_of",
hook)? };
let stored = TypeAttrColumn::lookup("testing.x_of")
.unwrap()
.get(PointObj::type_index());
@@ -627,7 +629,7 @@ fn test_native_function() -> Result<()> {
}
}
let error = TypeAttrDef::<PointObj>::new()
- .attr("testing.raw", RawPointer)
+ .set_attr("testing.raw", RawPointer)
.err()
.unwrap();
assert_eq!(error.kind(), TYPE_ERROR);
diff --git a/rust/tvm-ffi/tests/test_rs_object.rs
b/rust/tvm-ffi/tests/test_rs_object.rs
index 3bffb1e9..25f5d3bd 100644
--- a/rust/tvm-ffi/tests/test_rs_object.rs
+++ b/rust/tvm-ffi/tests/test_rs_object.rs
@@ -30,7 +30,7 @@ use tvm_ffi_sys::{TVMFFIFieldInfo, TVMFFIGetTypeInfo,
TVMFFISEqHashKind, TVMFFIT
/// A root type, under `ffi.Object`.
#[rs_object(
- "testing.rust.RsPoint",
+ type_key = "testing.rust.RsPoint",
s_eq_hash_kind = "TreeNode",
reflection = RsPointObj::reflect_methods
)]
@@ -60,7 +60,7 @@ impl RsPointObj {
}
/// A subtype of a Rust-defined type, with a private field.
-#[rs_object("testing.rust.RsPoint3", final)]
+#[rs_object(type_key = "testing.rust.RsPoint3", final)]
pub struct RsPoint3Obj {
base_: RsPointObj,
pub z: i64,
@@ -76,7 +76,7 @@ pub struct RsPoint3 {
}
/// A subtype without fields, which still registers its reflection.
-#[rs_object("testing.rust.RsMarker", final)]
+#[rs_object(type_key = "testing.rust.RsMarker", final)]
pub struct RsMarkerObj {
base_: RsPointObj,
}
@@ -88,7 +88,7 @@ pub struct RsMarker {
}
/// Scalar fields, whose literal arguments to `new` infer as the field types.
-#[rs_object("testing.rust.RsScalars", final)]
+#[rs_object(type_key = "testing.rust.RsScalars", final)]
pub struct RsScalarsObj {
base_: Object,
pub big: i64,
@@ -133,7 +133,11 @@ pub struct TestCxxClassBase {
}
/// A subtype of a C++ type.
-#[rs_object("testing.rust.RsCxxChild", final, s_eq_hash_kind = "TreeNode")]
+#[rs_object(
+ type_key = "testing.rust.RsCxxChild",
+ final,
+ s_eq_hash_kind = "TreeNode"
+)]
pub struct RsCxxChildObj {
base_: TestCxxClassBaseObj,
pub name: tvm_ffi::String,
@@ -146,7 +150,7 @@ pub struct RsCxxChild {
}
/// A type that defines its own `__ffi_init__`, over which C++ generates none.
-#[rs_object("testing.rust.RsOwnInit", final, reflection =
RsOwnInitObj::reflect_methods)]
+#[rs_object(type_key = "testing.rust.RsOwnInit", final, reflection =
RsOwnInitObj::reflect_methods)]
pub struct RsOwnInitObj {
base_: Object,
pub v: i64,
@@ -173,7 +177,7 @@ impl RsOwnInitObj {
}
/// `init_off` fields, which `__ffi_init__` leaves all-zero: a value of these
types.
-#[rs_object("testing.rust.RsInitOff", final)]
+#[rs_object(type_key = "testing.rust.RsInitOff", final)]
pub struct RsInitOffObj {
base_: Object,
pub v: i64,
@@ -189,7 +193,7 @@ pub struct RsInitOff {
}
/// An `init_off` reference, which all-zero bytes leave undefined: its
registration fails.
-#[rs_object("testing.rust.RsInitOffReference", final)]
+#[rs_object(type_key = "testing.rust.RsInitOffReference", final)]
pub struct RsInitOffReferenceObj {
base_: Object,
#[reflect(init_off)]
@@ -202,7 +206,7 @@ mod reexported {
#[allow(dead_code)]
mod tvm_ffi {}
- #[::tvm_ffi::rs_object("testing.rust.RsReexported", crate = ::tvm_ffi)]
+ #[::tvm_ffi::rs_object(type_key = "testing.rust.RsReexported", crate =
::tvm_ffi)]
pub struct ReexportedObj {
base_: ::tvm_ffi::Object,
pub v: i64,
diff --git a/rust/tvm-ffi/tests/test_static_init.rs
b/rust/tvm-ffi/tests/test_static_init.rs
index f1e4d460..0ab1a644 100644
--- a/rust/tvm-ffi/tests/test_static_init.rs
+++ b/rust/tvm-ffi/tests/test_static_init.rs
@@ -77,7 +77,7 @@ mod failing {
}
tvm_ffi::static_init_block! {
- Err(Error::new(VALUE_ERROR, "the second block fails", ""))
+ Err(Error::new(VALUE_ERROR, "the second block fails"))
}
tvm_ffi::static_init_block! {
diff --git a/rust/tvm-ffi/tests/test_string.rs
b/rust/tvm-ffi/tests/test_string.rs
index 8d8384f5..b7b5fdf6 100644
--- a/rust/tvm-ffi/tests/test_string.rs
+++ b/rust/tvm-ffi/tests/test_string.rs
@@ -287,7 +287,7 @@ fn test_bytes_debug() {
let data = b"hello";
let bytes = Bytes::from(data.as_slice());
let debug_str = format!("{:?}", bytes);
- assert!(debug_str.contains("Bytes"));
+ assert_eq!(debug_str, format!("{:?}", data.as_slice()));
}
#[test]
@@ -295,8 +295,7 @@ fn test_string_debug() {
let input = "world";
let s = String::from(input);
let debug_str = format!("{:?}", s);
- assert!(debug_str.contains("String"));
- assert!(debug_str.contains("world"));
+ assert_eq!(debug_str, "\"world\"");
}
#[test]
diff --git a/rust/tvm-ffi/tests/test_structural_mutate.rs
b/rust/tvm-ffi/tests/test_structural_mutate.rs
index cd2fb8a0..6d981d0a 100644
--- a/rust/tvm-ffi/tests/test_structural_mutate.rs
+++ b/rust/tvm-ffi/tests/test_structural_mutate.rs
@@ -551,9 +551,9 @@ fn
default_mutation_mode_preserves_ownership_and_unchanged_results() {
) -> Result<UnchangedOr<Any>> {
let mode = self.mode;
if self.increment {
- mutator.default_mutate(self, value.with_inplace_mode(mode))
+ mutator.default_mutate(self, value.narrow_inplace_mode(mode))
} else {
- let result = mutator.default_mutate(self,
value.with_inplace_mode(mode))?;
+ let result = mutator.default_mutate(self,
value.narrow_inplace_mode(mode))?;
assert!(result.is_unchanged());
Ok(result)
}
@@ -597,7 +597,7 @@ fn
borrowed_capabilities_cannot_enable_low_level_default_reuse() {
ObjectArc::strong_count(<Array<i64> as
ObjectRefCore>::data(&source)),
1
);
- let value = value.with_inplace_mode(InplaceMode::Allow);
+ let value = value.narrow_inplace_mode(InplaceMode::Allow);
assert_eq!(value.inplace_mode(), InplaceMode::Disallow);
let result = mutator.default_mutate(self, value)?;
let result = Array::<i64>::try_from(
@@ -685,7 +685,7 @@ fn owned_entry_mode_is_forwarded_by_generated_callbacks() {
let mut owner: Any = child.into();
let alias = self.shared.then(|| owner.clone());
let count = if self.shared { 2 } else { 1 };
- let capability = MutationView::from(&mut
owner).with_inplace_mode(self.mode);
+ let capability = MutationView::from(&mut
owner).narrow_inplace_mode(self.mode);
assert_eq!(capability.inplace_mode(), self.mode);
assert_eq!(capability.as_any_view().debug_strong_count(),
Some(count));
assert_eq!(
@@ -804,7 +804,7 @@ fn
consuming_callbacks_forward_permissions_without_temporary_owners() {
});
self.observe(&value);
mutator
- .default_mutate(self, value.with_inplace_mode(self.requested))
+ .default_mutate(self,
value.narrow_inplace_mode(self.requested))
.into_mutate_result()
}
fn mutate_integer(
@@ -871,7 +871,7 @@ fn
consuming_callbacks_forward_permissions_without_temporary_owners() {
assert_eq!(a.size, 1);
assert_eq!(a.inplace_mode(), Allow);
assert_eq!(a.as_any_view().debug_strong_count(), Some(1));
- let mut value = value.with_inplace_mode(Disallow);
+ let mut value = value.narrow_inplace_mode(Disallow);
let b = value.field(|node| &node.b);
assert_eq!(b.inplace_mode(), Disallow);
assert_eq!(b.as_any_view().debug_strong_count(), Some(1));
@@ -965,7 +965,7 @@ fn
consuming_default_descent_preserves_unchanged_and_propagates_errors() {
) -> Result<UnchangedOr<Any>> {
if value.as_any_view().downcast_ref::<i64>().copied().is_some() {
return if self.0 {
- Err(Error::new(RUNTIME_ERROR, "child failed", ""))
+ Err(Error::new(RUNTIME_ERROR, "child failed"))
} else {
Ok(UnchangedOr::<Any>::unchanged())
};
@@ -1131,14 +1131,11 @@ fn
callback_errors_preserve_message_and_add_object_context() {
unsafe impl Send for VisitErrorContext {}
// SAFETY: as for `Send`.
unsafe impl Sync for VisitErrorContext {}
- let cause = Error::new(RUNTIME_ERROR, "cause", "");
- let source = Error::new_with_cause_and_extra_context(
- RUNTIME_ERROR,
- "callback failed",
- "origin",
- Some(&cause),
- Some(&VisitErrorContext::try_from(context.clone()).unwrap()),
- );
+ let cause = Error::new(RUNTIME_ERROR, "cause");
+ let source = Error::new(RUNTIME_ERROR, "callback failed")
+ .with_backtrace("origin")
+ .with_cause(&cause)
+
.with_extra_context(&VisitErrorContext::try_from(context.clone()).unwrap());
let mut errors = Vec::new();
for order in [WalkOrder::PreOrder, WalkOrder::PostOrder] {
let leaf_error = structural_map(
@@ -1258,12 +1255,12 @@ fn map_errors_name_the_replaced_or_rebuilt_node() {
if order == WalkOrder::PreOrder {
Ok(value.into())
} else {
- Err(Error::new(RUNTIME_ERROR, "failed", ""))
+ Err(Error::new(RUNTIME_ERROR, "failed"))
}
},
|value: i64| -> Result<i64> {
if order == WalkOrder::PreOrder {
- Err(Error::new(RUNTIME_ERROR, "failed", ""))
+ Err(Error::new(RUNTIME_ERROR, "failed"))
} else {
Ok(value + 1)
}
@@ -1345,7 +1342,7 @@ fn
shared_map_callback_error_preserves_source_and_reports_object_context() {
let error = match structural_map(
source.clone(),
|_integer: i64| -> Result<i64> {
- Err(Error::new(RUNTIME_ERROR, "map mapper failed", "origin"))
+ Err(Error::new(RUNTIME_ERROR, "map mapper
failed").with_backtrace("origin"))
},
WalkOrder::PostOrder,
) {
@@ -2324,7 +2321,7 @@ fn mutation_descent_preserves_ownership_and_markers() {
if self.retain {
self.retained = Some(Any::from(value.as_any_view()));
}
- let result = mutator.default_mutate(self,
value.with_inplace_mode(self.mode))?;
+ let result = mutator.default_mutate(self,
value.narrow_inplace_mode(self.mode))?;
if !self.increment {
assert!(result.is_unchanged());
}
@@ -2450,7 +2447,7 @@ fn mutation_regions_retargeting_and_error_restore() {
assert_eq!(mutator.def_region_kind(), Use);
let error: Result<()> = mutator.with_def_region_kind(Pattern,
|mutator| {
assert_eq!(mutator.def_region_kind(), Pattern);
- Err(Error::new(RUNTIME_ERROR, "handler region error", ""))
+ Err(Error::new(RUNTIME_ERROR, "handler region error"))
});
assert!(error
.unwrap_err()
@@ -2497,7 +2494,7 @@ fn mutation_regions_retargeting_and_error_restore() {
})?;
let error: Result<()> = mutator.with_def_region_kind(Use,
|mutator| {
assert_eq!(mutator.def_region_kind(), Pattern);
- Err(Error::new(RUNTIME_ERROR, "scoped error", ""))
+ Err(Error::new(RUNTIME_ERROR, "scoped error"))
});
assert!(error.is_err());
assert_eq!(mutator.def_region_kind(), Pattern);
diff --git a/rust/tvm-ffi/tests/test_structural_visit.rs
b/rust/tvm-ffi/tests/test_structural_visit.rs
index 706157be..76a2b985 100644
--- a/rust/tvm-ffi/tests/test_structural_visit.rs
+++ b/rust/tvm-ffi/tests/test_structural_visit.rs
@@ -29,7 +29,7 @@ use tvm_ffi::{
};
fn runtime_error(message: &str) -> Error {
- Error::new(RUNTIME_ERROR, message, "")
+ Error::new(RUNTIME_ERROR, message)
}
// Nested recursion restores regions after normal, interrupt, error and panic
exits.
diff --git a/rust/tvm-ffi/tests/test_structural_visit_base.rs
b/rust/tvm-ffi/tests/test_structural_visit_base.rs
index f2ffba63..488b8512 100644
--- a/rust/tvm-ffi/tests/test_structural_visit_base.rs
+++ b/rust/tvm-ffi/tests/test_structural_visit_base.rs
@@ -61,7 +61,7 @@ impl Drop for CountingBase {
unsafe impl StructuralVisitorBase for CountingBase {
fn new() -> Result<Self> {
if FAIL_INIT.get() {
- return Err(Error::new(RUNTIME_ERROR, "base initialization failed",
""));
+ return Err(Error::new(RUNTIME_ERROR, "base initialization
failed"));
}
Ok(Self {
prefix: StructuralVisitorObj::default(),
@@ -318,7 +318,7 @@ fn base_failure_and_owned_results_release_exactly_once() {
drop(result);
assert_eq!(payload.debug_strong_count(), Some(1));
- let error = Error::new(RUNTIME_ERROR, "base descent failed", "");
+ let error = Error::new(RUNTIME_ERROR, "base descent failed");
let owner = Any::from(error.clone());
BASE_RESULT.with_borrow_mut(|slot| *slot = Some(Err(error)));
let result = visit(&Array::from_vec(vec![-1_i64, 99]), Unmatched)
@@ -366,7 +366,7 @@ fn
callback_outcomes_preserve_reborrows_and_owners_through_specialized_defaults(
for fail in [false, true] {
DROPPED.with_borrow_mut(Vec::clear);
let payload = Any::from(Array::from_vec(vec![42_i64]));
- let error = Error::new(RUNTIME_ERROR, "callback failed", "");
+ let error = Error::new(RUNTIME_ERROR, "callback failed");
let error_owner = Any::from(error.clone());
let mut visitor = Halt {
result: Some(if fail {
diff --git a/tests/python/test_stubgen_rust.py
b/tests/python/test_stubgen_rust.py
index edfbbecc..68db1b8d 100644
--- a/tests/python/test_stubgen_rust.py
+++ b/tests/python/test_stubgen_rust.py
@@ -384,33 +384,23 @@ pub struct PairObj {{
impl PairObj {{
pub fn a(&self) -> Result<i64> {{
- static GETTER: ColdInitLazyLock<Result<FieldGetter>> =
- ColdInitLazyLock::new(|| FieldGetter::new(PairObj::type_index(),
"a"));
- GETTER.clone()?.get(self)
+ cached_field_getter!(PairObj, "a").get(self)
}}
pub fn tag(&self) -> Result<Option<String>> {{
- static GETTER: ColdInitLazyLock<Result<FieldGetter>> =
- ColdInitLazyLock::new(|| FieldGetter::new(PairObj::type_index(),
"tag"));
- GETTER.clone()?.get(self)
+ cached_field_getter!(PairObj, "tag").get(self)
}}
pub fn items(&self) -> Result<Array<Any>> {{
- static GETTER: ColdInitLazyLock<Result<FieldGetter>> =
- ColdInitLazyLock::new(|| FieldGetter::new(PairObj::type_index(),
"items"));
- GETTER.clone()?.get(self)
+ cached_field_getter!(PairObj, "items").get(self)
}}
pub fn owner(&self) -> Result<ObjectRef> {{
- static GETTER: ColdInitLazyLock<Result<FieldGetter>> =
- ColdInitLazyLock::new(|| FieldGetter::new(PairObj::type_index(),
"owner"));
- GETTER.clone()?.get(self)
+ cached_field_getter!(PairObj, "owner").get(self)
}}
pub fn r#type(&self) -> Result<Any> {{
- static GETTER: ColdInitLazyLock<Result<FieldGetter>> =
- ColdInitLazyLock::new(|| FieldGetter::new(PairObj::type_index(),
"type"));
- GETTER.clone()?.get_any(self)
+ cached_field_getter!(PairObj, "type").get_any(self)
}}
}}"""
@@ -432,10 +422,8 @@ def test_render_root_object() -> None:
assert _uses(imports) == {
"tvm_ffi::Object",
"tvm_ffi::ObjectArc",
- "tvm_ffi::ObjectCore",
- "tvm_ffi::reflection::FieldGetter",
+ "tvm_ffi::cached_field_getter",
"tvm_ffi::Result",
- "tvm_ffi::extra::cold_init_lazy_lock::ColdInitLazyLock",
"tvm_ffi::String",
"tvm_ffi::Array",
"tvm_ffi::Any",
@@ -447,7 +435,7 @@ def test_render_object_without_fields_has_no_impl() -> None:
text, imports = _render(_info("demo.Marker", is_final=True))
assert '#[object(type_key = "demo.Marker", final)]' in text
assert "impl MarkerObj" not in text
- assert "FieldGetter" not in _uses(imports)
+ assert "tvm_ffi::cached_field_getter" not in _uses(imports)
def test_render_thread_safe_only_when_not_mutable() -> None:
@@ -591,7 +579,7 @@ impl TryFrom<i64> for IterVarType {{
type Error = Error;
fn try_from(value: i64) -> Result<Self> {{
i32::try_from(value).map(Self).map_err(|_| {{
- Error::new(VALUE_ERROR, &format!("IterVarType value {{value}} does
not fit i32"), "")
+ Error::new(VALUE_ERROR, &format!("IterVarType value {{value}} does
not fit i32"))
}})
}}
}}
@@ -615,34 +603,24 @@ impl Deref for IterVarObj {{
impl IterVarObj {{
pub fn dom(&self) -> Result<Option<Range>> {{
- static GETTER: ColdInitLazyLock<Result<FieldGetter>> =
- ColdInitLazyLock::new(||
FieldGetter::new(IterVarObj::type_index(), "dom"));
- GETTER.clone()?.get(self)
+ cached_field_getter!(IterVarObj, "dom").get(self)
}}
pub fn var(&self) -> Result<PrimVar> {{
- static GETTER: ColdInitLazyLock<Result<FieldGetter>> =
- ColdInitLazyLock::new(||
FieldGetter::new(IterVarObj::type_index(), "var"));
- GETTER.clone()?.get(self)
+ cached_field_getter!(IterVarObj, "var").get(self)
}}
pub fn iter_type(&self) -> Result<IterVarType> {{
- static GETTER: ColdInitLazyLock<Result<FieldGetter>> =
- ColdInitLazyLock::new(||
FieldGetter::new(IterVarObj::type_index(), "iter_type"));
- let raw: i64 = GETTER.clone()?.get(self)?;
+ let raw: i64 = cached_field_getter!(IterVarObj,
"iter_type").get(self)?;
IterVarType::try_from(raw)
}}
pub fn thread_tag(&self) -> Result<String> {{
- static GETTER: ColdInitLazyLock<Result<FieldGetter>> =
- ColdInitLazyLock::new(||
FieldGetter::new(IterVarObj::type_index(), "thread_tag"));
- GETTER.clone()?.get(self)
+ cached_field_getter!(IterVarObj, "thread_tag").get(self)
}}
pub fn span(&self) -> Result<Option<Span>> {{
- static GETTER: ColdInitLazyLock<Result<FieldGetter>> =
- ColdInitLazyLock::new(||
FieldGetter::new(IterVarObj::type_index(), "span"));
- GETTER.clone()?.get(self)
+ cached_field_getter!(IterVarObj, "span").get(self)
}}
}}
@@ -932,8 +910,7 @@ def test_reflected_base_and_data_keep_their_names() -> None:
RUST.add_directive(imports, "visibility", "demo.Node.cache_ ->
pub(crate)", 1)
text, _ = _render(_info("demo.Node", (_field("base", "int"),
_field("cache_", "int"))), imports)
assert (
- "pub fn base(&self) -> Result<i64> {\n static GETTER:
ColdInitLazyLock<Result<FieldGetter>> ="
- in text
+ 'pub fn base(&self) -> Result<i64> {\n
cached_field_getter!(NodeObj, "base")' in text
)
assert " pub(crate) fn cache_(&self) -> Result<i64> {" in text
with pytest.raises(ValueError, match=r"`visibility` directive at line 2"):
@@ -954,7 +931,7 @@ def test_render_complete_enum_field() -> None:
assert " pub kind: Kind,\n" in text
assert " pub fn new(a: i64, kind: impl Into<Kind>) -> Self {" in text
assert {"tvm_ffi::Result", "tvm_ffi::Error", "tvm_ffi::VALUE_ERROR"} <=
_uses(imports)
- assert "tvm_ffi::reflection::FieldGetter" not in _uses(imports)
+ assert "tvm_ffi::cached_field_getter" not in _uses(imports)
def test_allow_missing_docs_and_closed_enum() -> None:
@@ -983,7 +960,7 @@ def test_allow_missing_docs_and_closed_enum() -> None:
" match value {\n"
" 0 => Ok(Self::A),\n"
" -1 => Ok(Self::B),\n"
- ' _ => Err(Error::new(VALUE_ERROR, &format!("Kind has no
value {value}"), "")),\n'
+ ' _ => Err(Error::new(VALUE_ERROR, &format!("Kind has no
value {value}"))),\n'
" }\n"
) in text
# The enum, the object struct, the wrapper and both allocator `impl`s.
@@ -1448,7 +1425,7 @@ def test_registry_complete_chain_is_mirrored() -> None:
in base
)
assert "assert!(::core::mem::size_of::<TestCxxClassBaseObj>() == 40);" in
base
- assert "FieldGetter" not in base
+ assert "cached_field_getter" not in base
assert " pub fn new(v_i64: i64, v_i32: i32) -> Self {" in base
dd, imports = _render(
@@ -1547,9 +1524,7 @@ def test_cli_init_generates_a_module_tree(tmp_path: Path,
monkeypatch: pytest.Mo
assert (root / "mod.rs").read_text(encoding="utf-8") == "pub mod
testing;\n"
text = (root / "testing" / "mod.rs").read_text(encoding="utf-8")
assert text.startswith("#![allow(dead_code, unused_imports)]\n")
- assert (
- "use tvm_ffi::reflection::FieldGetter;" in text
- ) # the opaque fixtures read through getters
+ assert "use tvm_ffi::cached_field_getter;" in text # the opaque fixtures
read through getters
assert '#[object(type_key = "testing.TestCxxClassDerivedDerived"' in text
# Without a directive for the narrow `v_i32`, the chain is opaque.
assert (
@@ -1891,9 +1866,7 @@ def
test_ffi_field_type_off_the_default_ty_map_reads_as_any() -> None:
text, imports = _render(_info("demo.Doc", (_field("interrupt",
"ffi.VisitInterrupt"),)))
assert (
" pub fn interrupt(&self) -> Result<Any> {\n"
- " static GETTER: ColdInitLazyLock<Result<FieldGetter>> =\n"
- ' ColdInitLazyLock::new(||
FieldGetter::new(DocObj::type_index(), "interrupt"));\n'
- " GETTER.clone()?.get_any(self)\n"
+ ' cached_field_getter!(DocObj, "interrupt").get_any(self)\n'
) in text
assert not any("VisitInterrupt" in path for path in _uses(imports))
@@ -1913,9 +1886,7 @@ def
test_any_field_of_an_opaque_type_reads_through_get_any() -> None:
text, _ = _render(_info("demo.Literal", (_field("value", "Any"),)))
assert (
" pub fn value(&self) -> Result<Any> {\n"
- " static GETTER: ColdInitLazyLock<Result<FieldGetter>> =\n"
- ' ColdInitLazyLock::new(||
FieldGetter::new(LiteralObj::type_index(), "value"));\n'
- " GETTER.clone()?.get_any(self)\n"
+ ' cached_field_getter!(LiteralObj, "value").get_any(self)\n'
) in text