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 7bedac88 [Rust] structural_equal/hash/mismatch; name the parent
`base_` and the generated handle `data_` (#951)
7bedac88 is described below
commit 7bedac88ac4f2737d089244bae4d7be9d3599140
Author: Tianqi Chen <[email protected]>
AuthorDate: Fri Nov 6 20:12:40 2026 -0500
[Rust] structural_equal/hash/mismatch; name the parent `base_` and the
generated handle `data_` (#951)
Two commits:
1. **Name the parent `base_` and the generated handle `data_`.** Object
structs embed their parent as `base_`, and stubgen references hold their
`ObjectArc` as `data_`, so reflected fields keep their registered names
(`base`, `data`, `global_var_map_`); stubgen rejects only a reflected
`base_`/`data_`. `#[rs_object]` requires `base_` and points a `base`
field to the new name (one-time break). Stubgen emits the reference
before its `*Obj` (a `///` above the block documents the reference; the
`*Obj` gets a one-line summary), adds a `visibility` directive
(`<type_key>.<field> -> pub(crate)`), and no longer imports the parent's
type for a parameter a `field` directive narrows.
2. **`structural_equal`, `structural_hash`,
`get_first_structural_mismatch`** (and `_with` variants taking
`StructuralOptions { map_free_vars, skip_tensor_content }`), thin
wrappers over the registered
`ffi.StructuralEqual`/`ffi.StructuralHash`/`ffi.GetFirstStructuralMismatch`,
re-exported at the crate root.
---
docs/guides/rust_lang_guide.rst | 8 +-
examples/rust_stubgen/README.md | 8 +-
.../rust/src/generated/rust_stubgen/mod.rs | 22 +-
python/tvm_ffi/stub/rust_generator/codegen.py | 104 +++++++---
python/tvm_ffi/stub/rust_generator/consts.py | 12 +-
python/tvm_ffi/stub/rust_generator/directives.py | 12 +-
python/tvm_ffi/stub/rust_generator/utils.py | 19 +-
rust/tvm-ffi-macros/src/object_macros.rs | 2 +-
rust/tvm-ffi-macros/src/rs_object.rs | 15 +-
rust/tvm-ffi/src/collections/tensor.rs | 18 +-
rust/tvm-ffi/src/derive.rs | 4 +-
rust/tvm-ffi/src/extra/mod.rs | 1 +
rust/tvm-ffi/src/extra/structural_eq_hash.rs | 189 +++++++++++++++++
rust/tvm-ffi/src/extra/structural_mutate.rs | 6 +-
rust/tvm-ffi/src/extra/structural_visit.rs | 8 +-
rust/tvm-ffi/src/extra/visit_error_context.rs | 6 +-
rust/tvm-ffi/src/lib.rs | 15 +-
rust/tvm-ffi/src/macros.rs | 2 +-
rust/tvm-ffi/src/object.rs | 6 +-
rust/tvm-ffi/src/reflection/registry.rs | 2 +-
rust/tvm-ffi/tests/test_any.rs | 6 +-
rust/tvm-ffi/tests/test_cast.rs | 16 +-
rust/tvm-ffi/tests/test_match_any_ast_lookup.rs | 8 +-
rust/tvm-ffi/tests/test_object_ref_any.rs | 2 +-
rust/tvm-ffi/tests/test_object_reflection.rs | 24 +--
rust/tvm-ffi/tests/test_object_registration.rs | 12 +-
rust/tvm-ffi/tests/test_rs_object.rs | 32 +--
rust/tvm-ffi/tests/test_structural_eq_hash.rs | 51 +++++
rust/tvm-ffi/tests/test_structural_mutate.rs | 8 +-
tests/python/test_stubgen_rust.py | 227 +++++++++++----------
30 files changed, 587 insertions(+), 258 deletions(-)
diff --git a/docs/guides/rust_lang_guide.rst b/docs/guides/rust_lang_guide.rst
index 1332452a..c32e44f0 100644
--- a/docs/guides/rust_lang_guide.rst
+++ b/docs/guides/rust_lang_guide.rst
@@ -323,7 +323,7 @@ first ``type_index()``, which allocating an instance calls:
#[type_s_eq_hash_kind = "TreeNode"]
#[type_reflection(PointObj::reflect_methods)]
pub struct PointObj {
- base: Object,
+ base_: Object,
/// The x coordinate.
#[reflect]
pub x: i64,
@@ -366,7 +366,7 @@ Defining Object Types from Their Fields
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``#[rs_object]`` defines such a type from its object struct, like Python's
-``py_class``. The first field, ``base``, is the parent's object; every other
field
+``py_class``. The first field, ``base_``, is the parent's object; every other
field
is reflected, and ``#[reflect(...)]`` sets its flags, name and doc string.
``reflection = path`` after the type key is ``#[type_reflection(path)]``.
``#[derive(ObjectRef)]`` declares the reference, and ``#[object_ref(parent =
Point)]``
@@ -379,7 +379,7 @@ names the reference to the parent's object:
#[rs_object("my.Point", s_eq_hash_kind = "TreeNode")]
pub struct PointObj {
- base: Object,
+ base_: Object,
pub x: i64,
#[reflect(writable, seq_hash_ignore)]
pub label: tvm_ffi::String,
@@ -392,7 +392,7 @@ names the reference to the parent's object:
#[rs_object("my.Point3", final)]
pub struct Point3Obj {
- base: PointObj,
+ base_: PointObj,
pub z: i64,
}
diff --git a/examples/rust_stubgen/README.md b/examples/rust_stubgen/README.md
index ed67c858..d7ebeda0 100644
--- a/examples/rust_stubgen/README.md
+++ b/examples/rust_stubgen/README.md
@@ -31,6 +31,9 @@ and alignment to the reflected facts, and a generated
allocator builds the
object in Rust. `main.rs` builds one that way, reads `pair.a` directly, and
hands it to a C++ function that reads it back.
+The generated members end in `_` (`base_`, the embedded parent; `data_`, the
+reference's handle), so reflected fields keep their registered names.
+
An object whose layout cannot be reproduced (a polymorphic one, say, with a
vtable in front of the object header) is bound *opaquely* instead: the struct
embeds only the parent, every field is read through an accessor that calls the
@@ -112,10 +115,11 @@ Besides `prefix` and `custom-new`, this example declares
the integer field
// tvm-ffi-stubgen(enum): rust_stubgen.IntPair.kind -> PairKind(i32) {
Unordered=0, Ordered=1 }
```
-Three more are available: `field` names the Rust type of a field
+Four more are available: `field` names the Rust type of a field
(`// tvm-ffi-stubgen(field): rust_stubgen.IntPair.a -> MyInt`), `nullable`
wraps it in `Option` (`// tvm-ffi-stubgen(nullable): rust_stubgen.IntPair.a`),
-and `upcast` adds a conversion to a hand-written typed view
+`visibility` makes it `pub(crate)` instead of `pub`
+(`// tvm-ffi-stubgen(visibility): rust_stubgen.IntPair.a -> pub(crate)`), and
`upcast` adds a conversion to a hand-written typed view
(`// tvm-ffi-stubgen(upcast): rust_stubgen.IntPair -> MyView`).
When the C++ side fixes the value set in its ABI, `closed` before the member
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 a920ad01..cbf4eece 100644
--- a/examples/rust_stubgen/rust/src/generated/rust_stubgen/mod.rs
+++ b/examples/rust_stubgen/rust/src/generated/rust_stubgen/mod.rs
@@ -38,6 +38,12 @@ use tvm_ffi::VALUE_ERROR;
// tvm-ffi-stubgen(custom-new): rust_stubgen.IntPair
// tvm-ffi-stubgen(begin): object/rust_stubgen.IntPair
+#[repr(C)]
+#[derive(tvm_ffi::derive::ObjectRef, Clone)]
+pub struct IntPair {
+ data_: ObjectArc<IntPairObj>,
+}
+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct PairKind(i32);
@@ -63,13 +69,15 @@ impl TryFrom<i64> for PairKind {
}
}
+/// The object behind [`IntPair`].
+///
/// Complete: reflected fields fill [24, 48) exactly (alignment padding [44,
48)).
#[repr(C)]
#[derive(tvm_ffi::derive::Object)]
#[type_key = "rust_stubgen.IntPair"]
#[type_final]
pub struct IntPairObj {
- base: Object,
+ base_: Object,
pub a: i64,
pub b: i64,
pub kind: PairKind,
@@ -80,16 +88,10 @@ const _: () = {
assert!(::core::mem::align_of::<IntPairObj>() == 8);
};
-#[repr(C)]
-#[derive(tvm_ffi::derive::ObjectRef, Clone)]
-pub struct IntPair {
- base: ObjectArc<IntPairObj>,
-}
-
impl IntPairObj {
pub(crate) fn new(a: i64, b: i64, kind: PairKind) -> Self {
- let base = Object::new();
- Self { base, a, b, kind }
+ let base_ = Object::new();
+ Self { base_, a, b, kind }
}
}
@@ -97,7 +99,7 @@ impl IntPair {
/// Lossless complete-field allocation.
pub fn from_fields(a: i64, b: i64, kind: impl Into<PairKind>) -> Self {
let obj = IntPairObj::new(a, b, kind.into());
- Self { base: ObjectArc::new(obj) }
+ Self { data_: ObjectArc::new(obj) }
}
}
diff --git a/python/tvm_ffi/stub/rust_generator/codegen.py
b/python/tvm_ffi/stub/rust_generator/codegen.py
index fbd58c4b..24281716 100644
--- a/python/tvm_ffi/stub/rust_generator/codegen.py
+++ b/python/tvm_ffi/stub/rust_generator/codegen.py
@@ -221,7 +221,7 @@ class _ObjectRenderer:
return None
def _base_type(self) -> tuple[str, bool]:
- """Resolve the ``base`` struct and whether it is a generated or
``ty-map``'d parent.
+ """Resolve the ``base_`` struct and whether it is a generated or
``ty-map``'d parent.
A builtin parent below ``ffi.Object`` is embedded as its header-only
stand-in (see :meth:`RustImports.record_builtin_base`).
@@ -334,6 +334,10 @@ class _ObjectRenderer:
"""``#[allow(missing_docs)]`` under ``missing-docs: allow``, for a
crate that warns on it."""
return ["#[allow(missing_docs)]"] if
self.imports.directives.allow_missing_docs else []
+ def _visibility(self, target: str) -> str:
+ """``pub``, or what a ``visibility`` directive sets."""
+ return self.imports.directives.visibility.get(target, "pub")
+
def _accessor_lines(self, field: NamedTypeSchema) -> list[str]:
"""One ``pub fn <field>(&self) -> Result<T>`` through the C ABI getter.
@@ -342,6 +346,7 @@ class _ObjectRenderer:
directives = self.imports.directives
target = f"{self.type_key}.{field.name}"
name = rust_ident(field.name)
+ vis = self._visibility(target)
getter = f'FieldGetter::new(Self::type_index(), "{field.name}")?'
allow = _allow_non_snake_case([name])
@@ -349,7 +354,7 @@ class _ObjectRenderer:
if enum is not None:
return [
*allow,
- f"pub fn {name}(&self) -> Result<{enum.name}> {{",
+ f"{vis} fn {name}(&self) -> Result<{enum.name}> {{",
f" let raw: i64 = {getter}.get(self)?;",
f" {enum.name}::try_from(raw)",
"}",
@@ -365,7 +370,7 @@ class _ObjectRenderer:
any_type = self.imports.record("tvm_ffi::Any")
return [
*allow,
- f"pub fn {name}(&self) -> Result<{any_type}> {{",
+ f"{vis} fn {name}(&self) -> Result<{any_type}> {{",
f" {getter}.get_any(self)",
"}",
]
@@ -373,7 +378,7 @@ class _ObjectRenderer:
rust_type = f"Option<{rust_type}>"
return [
*allow,
- f"pub fn {name}(&self) -> Result<{rust_type}> {{",
+ f"{vis} fn {name}(&self) -> Result<{rust_type}> {{",
f" {getter}.get(self)",
"}",
]
@@ -471,10 +476,12 @@ class _ObjectRenderer:
f'#[type_key = "{self.type_key}"]',
*(["#[type_final]"] if self.info.is_final else []),
f"pub struct {self.obj_struct} {{",
- f" base: {base},", # a reflected `base` field becomes `base_`
(`rust_ident`)
+ f" base_: {base},", # a reflected `base_` is rejected
(`rust_ident`)
]
+ summary = [f"/// The object behind [`{self.leaf}`].", "///"]
if not verdict.is_complete:
return [
+ *summary,
f"/// Opaque: {verdict.detail}. Fields are read through the C
ABI getters.",
*header,
"}",
@@ -483,8 +490,10 @@ class _ObjectRenderer:
for field in sorted(self.info.fields, key=lambda f: f.offset or 0):
mirror = self._field_mirror(self.type_key, field, self.imports)
assert mirror is not None # the verdict already ran the
renderability check
- members.append(f" pub {rust_ident(field.name)}: {mirror},")
+ vis = self._visibility(f"{self.type_key}.{field.name}")
+ members.append(f" {vis} {rust_ident(field.name)}: {mirror},")
return [
+ *summary,
f"/// Complete: {verdict.detail}.",
*_allow_non_snake_case(rust_ident(f.name) for f in
self.info.fields),
*header,
@@ -499,34 +508,59 @@ class _ObjectRenderer:
# --- allocators --------------------------------------------------------
- def _allocator_params(self, key: str, info: ObjectInfo) -> list[tuple[str,
str]]:
- """``(field, type)`` of every physical field root to leaf, as
``<key>Obj::new`` takes."""
+ def _allocator_params(
+ self, key: str, info: ObjectInfo, narrowed: frozenset[str] =
frozenset()
+ ) -> list[tuple[str, str]]:
+ """``(field, type)`` of every physical field root to leaf, as
``<key>Obj::new`` takes.
+
+ ``narrowed`` names the fields a subtype of ``key`` narrows with a
``field`` directive.
+ """
parent = info.parent_type_key
inherited: list[tuple[str, str]] = []
if parent is not None and self._provider(parent) != "crate":
- inherited = self._allocator_params(parent,
object_info_from_type_key(parent))
- return self._level_params(key, info, inherited)
+ inherited = self._allocator_params(
+ parent, object_info_from_type_key(parent), narrowed |
self._narrowed_names(key)
+ )
+ return self._level_params(key, info, inherited, narrowed)
def _level_params(
- self, key: str, info: ObjectInfo, inherited: list[tuple[str, str]]
+ self,
+ key: str,
+ info: ObjectInfo,
+ inherited: list[tuple[str, str]],
+ narrowed: frozenset[str] = frozenset(),
) -> list[tuple[str, str]]:
"""Extend the parent's parameters with ``key``'s own fields by offset.
A ``field`` directive on an inherited field narrows that parameter; the
body hands it to the parent with ``.into()``.
"""
- params = [(name, self._narrowed(key, name, rust_type)) for name,
rust_type in inherited]
+
+ # A type a subtype narrows away is not spelled in this file: don't
import it.
+ def imports(name: str) -> RustImports:
+ return RustImports() if name in narrowed else self.imports
+
+ params = [
+ (name, self._narrowed(key, name, rust_type, imports(name)))
+ for name, rust_type in inherited
+ ]
for field in sorted(info.fields, key=lambda f: f.offset or 0):
- mirror = self._field_mirror(key, field, self.imports)
+ mirror = self._field_mirror(key, field, imports(field.name))
assert mirror is not None # complete: every field along the chain
has a mirror
params.append((field.name, mirror))
return params
- def _narrowed(self, key: str, field_name: str, rust_type: str) -> str:
+ def _narrowed_names(self, key: str) -> frozenset[str]:
+ """Name the inherited fields ``key`` narrows with a ``field``
directive."""
+ prefix = f"{key}."
+ targets = self.imports.directives.field_types
+ return frozenset(t[len(prefix) :] for t in targets if
t.startswith(prefix))
+
+ def _narrowed(self, key: str, field_name: str, rust_type: str, imports:
RustImports) -> str:
override =
self.imports.directives.field_types.get(f"{key}.{field_name}")
if override is None:
return rust_type
- return self.imports.record(override) if "::" in override else override
+ return imports.record(override) if "::" in override else override
def _fn_lines(
self,
@@ -569,7 +603,9 @@ class _ObjectRenderer:
if has_parent:
parent = self.info.parent_type_key
assert parent is not None
- inherited = self._allocator_params(parent,
object_info_from_type_key(parent))
+ inherited = self._allocator_params(
+ parent, object_info_from_type_key(parent),
self._narrowed_names(self.type_key)
+ )
params = self._level_params(self.type_key, self.info, inherited)
to_parent = [
f"{rust_ident(name)}.into()" if rust_type != parent_type else
rust_ident(name)
@@ -585,8 +621,8 @@ class _ObjectRenderer:
*self._fn_lines(
"pub(crate) fn new" if self.info.is_final else "pub fn
new",
params,
- (f"base = {base}::new", to_parent),
- ["base", *own],
+ (f"base_ = {base}::new", to_parent),
+ ["base_", *own],
),
"}",
]
@@ -601,7 +637,7 @@ class _ObjectRenderer:
"pub fn from_fields" if custom else "pub fn new",
params,
(f"obj = {self.obj_struct}::new", forward),
- ["base: ObjectArc::new(obj)"],
+ ["data_: ObjectArc::new(obj)"],
generic=True,
),
"}",
@@ -636,7 +672,7 @@ class _ObjectRenderer:
shallow_copy = self.imports.record("tvm_ffi::ShallowCopy")
own = [rust_ident(f.name) for f in sorted(self.info.fields, key=lambda
f: f.offset or 0)]
init = [
- f"base: {shallow_copy}::shallow_copy(&self.base)",
+ f"base_: {shallow_copy}::shallow_copy(&self.base_)",
*[f"{name}: Clone::clone(&self.{name})" for name in own],
]
return [
@@ -660,7 +696,7 @@ class _ObjectRenderer:
unsafe_init = self.imports.record("tvm_ffi::UnsafeInit")
addr = self.imports.record("std::ptr::addr_of_mut")
directives = self.imports.directives
- drops = [f"{unsafe_init}::drop_defined_fields({addr}!((*this).base));"]
+ drops =
[f"{unsafe_init}::drop_defined_fields({addr}!((*this).base_));"]
for field in sorted(self.info.fields, key=lambda f: f.offset or 0):
target = f"{self.type_key}.{field.name}"
if target in directives.enums:
@@ -701,26 +737,26 @@ class _ObjectRenderer:
self.imports.record("tvm_ffi::FieldGetter")
self.imports.record("tvm_ffi::Result")
- sections: list[list[str]] = []
- enums = self.imports.directives.enums
- sections += [
- self._enum_lines(enums[f"{self.type_key}.{f.name}"])
- for f in fields
- if f"{self.type_key}.{f.name}" in enums
- ]
- sections.append(self._struct_lines(verdict, base))
- sections.append(
+ # The reference comes first, so a `///` written above the block
documents it.
+ sections: list[list[str]] = [
[
*self._allow_missing_docs(),
"#[repr(C)]",
"#[derive(tvm_ffi::derive::ObjectRef, Clone)]",
f"pub struct {self.leaf} {{",
- f" base: ObjectArc<{self.obj_struct}>,",
+ f" data_: ObjectArc<{self.obj_struct}>,",
"}",
]
- )
+ ]
+ enums = self.imports.directives.enums
+ sections += [
+ self._enum_lines(enums[f"{self.type_key}.{f.name}"])
+ for f in fields
+ if f"{self.type_key}.{f.name}" in enums
+ ]
+ sections.append(self._struct_lines(verdict, base))
if has_parent:
- sections.append(self._deref_lines(self.obj_struct, base, "base"))
+ sections.append(self._deref_lines(self.obj_struct, base, "base_"))
if has_accessors:
accessors: list[str] = []
for i, field in enumerate(fields):
@@ -807,7 +843,7 @@ def _builtin_mirror_lines(type_key: str, base: str) ->
list[str]:
"#[derive(tvm_ffi::derive::Object)]",
f'#[type_key = "{type_key}"]',
f"struct {builtin_mirror_name(type_key)} {{",
- f" base: {base},",
+ f" base_: {base},",
"}",
]
diff --git a/python/tvm_ffi/stub/rust_generator/consts.py
b/python/tvm_ffi/stub/rust_generator/consts.py
index 6dab7590..2ae25f92 100644
--- a/python/tvm_ffi/stub/rust_generator/consts.py
+++ b/python/tvm_ffi/stub/rust_generator/consts.py
@@ -24,6 +24,7 @@ RUST_DIRECTIVE_KINDS = frozenset(
"import-object",
"field",
"nullable",
+ "visibility",
"enum",
"opaque",
"upcast",
@@ -45,7 +46,7 @@ RUST_TY_MAP_DEFAULTS = {
"Array": "tvm_ffi::Array", # the crate's own Array<T>, NOT Vec
"List": "tvm_ffi::List", # the crate's own List<T>, NOT Vec
"Map": "tvm_ffi::Map", # the crate's own Map<K, V>, NOT HashMap
- # An object value is the `ObjectRef` handle; `Object` only appears as an
embedded `base`.
+ # An object value is the `ObjectRef` handle; `Object` only appears as an
embedded `base_`.
"Object": "tvm_ffi::object::ObjectRef",
"Tensor": "tvm_ffi::Tensor",
"Shape": "tvm_ffi::Shape",
@@ -132,10 +133,11 @@ RUST_KEYWORDS = frozenset(
)
RUST_NOT_RAW_IDENTIFIERS = frozenset({"self", "Self", "super", "crate"})
-#: The one member name the generated structs use themselves: ``base`` is the
parent slot of every
-#: object struct and the ``ObjectArc`` slot of every reference wrapper. A
reflected field of that
-#: name would collide (or shadow through ``Deref``), so ``rust_ident`` spells
it ``base_``.
-RUST_RESERVED_MEMBERS = frozenset({"base"})
+#: The members the generated structs use themselves: ``base_``, the parent's
object in every
+#: object struct, and ``data_``, the ``ObjectArc`` of every reference. The
trailing ``_`` keeps
+#: them apart from reflected names such as ``base`` and ``data``.
+RUST_GENERATED_MEMBERS = frozenset({"base_", "data_"})
+
#: ``rustfmt``'s default ``max_width``; a wider allocator signature wraps one
parameter per line.
RUST_MAX_WIDTH = 100
diff --git a/python/tvm_ffi/stub/rust_generator/directives.py
b/python/tvm_ffi/stub/rust_generator/directives.py
index c322bbc7..68f67b9f 100644
--- a/python/tvm_ffi/stub/rust_generator/directives.py
+++ b/python/tvm_ffi/stub/rust_generator/directives.py
@@ -16,11 +16,12 @@
# under the License.
"""The Rust backend's one-line directives: payload grammar and per-file
storage.
-Three address one reflected field as ``<type_key>.<field>``, three address a
type,
+Four address one reflected field as ``<type_key>.<field>``, three address a
type,
and one applies to the whole file::
// tvm-ffi-stubgen(field): tirx.Add.a -> PrimExpr
// tvm-ffi-stubgen(nullable): ir.Expr.span
+ // tvm-ffi-stubgen(visibility): ir.IRModule.global_var_map_ -> pub(crate)
// tvm-ffi-stubgen(enum): tirx.For.kind -> ForKind(i32) { Serial=0,
Parallel=1 }
// tvm-ffi-stubgen(opaque): ir.SourceName
// tvm-ffi-stubgen(upcast): tirx.Add -> PrimExpr
@@ -29,7 +30,8 @@ and one applies to the whole file::
``field`` sets the field's Rust type (a name in scope, or a ``::`` path to
``use``); on a field inherited from an ancestor it narrows the allocator
-parameter instead. ``nullable`` wraps it in ``Option``; ``enum`` declares an
open
+parameter instead. ``nullable`` wraps it in ``Option``; ``visibility`` makes
the
+field and its accessor ``pub(crate)`` instead of ``pub``; ``enum`` declares an
open
integer newtype for it, or with ``closed`` before the member list a
``#[repr(<int>)]`` enum of exactly those members; ``opaque`` keeps a type
opaque
even when its layout is reproducible. ``upcast`` adds an upcast to a reference
@@ -72,6 +74,7 @@ class Directives:
field_types: dict[str, str] = dataclasses.field(default_factory=dict)
nullable: set[str] = dataclasses.field(default_factory=set)
+ visibility: dict[str, str] = dataclasses.field(default_factory=dict)
enums: dict[str, EnumSpec] = dataclasses.field(default_factory=dict)
opaque: set[str] = dataclasses.field(default_factory=set)
upcasts: dict[str, list[str]] = dataclasses.field(default_factory=dict)
@@ -85,6 +88,11 @@ class Directives:
self.field_types[_field_target(name, lhs, lineno)] = rust_type
elif name == "nullable":
self.nullable.add(_field_target(name, payload, lineno))
+ elif name == "visibility":
+ lhs, vis = _split_arrow(name, payload, lineno, "<type_key>.<field>
-> pub(crate)")
+ if vis != "pub(crate)": # the one non-default level so far
+ raise _invalid(name, lineno, "<type_key>.<field> ->
pub(crate)")
+ self.visibility[_field_target(name, lhs, lineno)] = vis
elif name == "enum":
target, spec = _parse_enum(payload, lineno)
self.enums[target] = spec
diff --git a/python/tvm_ffi/stub/rust_generator/utils.py
b/python/tvm_ffi/stub/rust_generator/utils.py
index 375b217e..8f99d480 100644
--- a/python/tvm_ffi/stub/rust_generator/utils.py
+++ b/python/tvm_ffi/stub/rust_generator/utils.py
@@ -196,17 +196,16 @@ def check_tokens(lines: Iterable[str]) -> tuple[str, ...]:
def rust_ident(name: str) -> str:
"""Spell a reflected field name in Rust.
- Two naming conventions are unwrapped, and nothing else is renamed: a C++
- member with one trailing underscore (``global_var_map_``) loses it, and a
- dunder name (``__dict__``) loses both pairs. Keywords become raw
- identifiers; the four that cannot, and ``base`` (the member name the
- generated structs use themselves), get a trailing underscore.
+ The name is kept as registered: keywords become raw identifiers, and the
+ four that cannot get a trailing underscore. The generated structs' own
+ members, ``base_`` and ``data_``, are rejected as reflected names.
"""
- if len(name) > 4 and name.startswith("__") and name.endswith("__"):
- name = name[2:-2]
- elif len(name) > 1 and name.endswith("_") and not name.endswith("__"):
- name = name[:-1]
- if name in C.RUST_NOT_RAW_IDENTIFIERS or name in C.RUST_RESERVED_MEMBERS:
+ if name in C.RUST_GENERATED_MEMBERS:
+ raise ValueError(
+ f"reflected field `{name}` collides with the member the generated
Rust bindings "
+ f"name `{name}`; register the field under another name"
+ )
+ if name in C.RUST_NOT_RAW_IDENTIFIERS:
return f"{name}_"
if name in C.RUST_KEYWORDS:
return f"r#{name}"
diff --git a/rust/tvm-ffi-macros/src/object_macros.rs
b/rust/tvm-ffi-macros/src/object_macros.rs
index b6d3aba1..ed500c2e 100644
--- a/rust/tvm-ffi-macros/src/object_macros.rs
+++ b/rust/tvm-ffi-macros/src/object_macros.rs
@@ -530,7 +530,7 @@ pub(crate) fn expand_object_ref(
};
// The `ObjectArc<T>` slot is the first field; its name is the struct's
choice
- // (`data` in this crate, `base` in stubgen output, so that reflected
fields
+ // (`data` in this crate, `data_` in stubgen output, so that reflected
fields
// named `data` keep their name).
let (data_id, data_ty) = match &derive_input.data {
syn::Data::Struct(s) => s
diff --git a/rust/tvm-ffi-macros/src/rs_object.rs
b/rust/tvm-ffi-macros/src/rs_object.rs
index d268ea1e..90bc2cf3 100644
--- a/rust/tvm-ffi-macros/src/rs_object.rs
+++ b/rust/tvm-ffi-macros/src/rs_object.rs
@@ -107,8 +107,8 @@ impl Parse for RsObjectArgs {
}
}
-const EXPECTED_BASE: &str =
- "the first field holds the parent's object: `base: Object` at the root,
else `base: ParentObj`";
+const EXPECTED_BASE: &str = "the first field holds the parent's object: \
+ `base_: Object` at the root, else `base_: ParentObj`";
fn expand(args: &RsObjectArgs, item: &DeriveInput) ->
syn::Result<TokenStream2> {
if !item.generics.params.is_empty() ||
item.generics.where_clause.is_some() {
@@ -134,16 +134,23 @@ fn expand(args: &RsObjectArgs, item: &DeriveInput) ->
syn::Result<TokenStream2>
}
};
let (base, fields) = match fields.split_first() {
- Some((base, fields)) if base.ident.as_ref().is_some_and(|id|
id.unraw() == "base") => {
+ Some((base, fields)) if base.ident.as_ref().is_some_and(|id|
id.unraw() == "base_") => {
(*base, fields)
}
+ // The trailing `_` marks plumbing, so a reflected field can be named
`base`.
+ Some((field, _)) if field.ident.as_ref().is_some_and(|id| id.unraw()
== "base") => {
+ return Err(syn::Error::new_spanned(
+ field,
+ "the parent's object is named `base_`: rename `base` to
`base_`",
+ ));
+ }
Some((field, _)) => return Err(syn::Error::new_spanned(field,
EXPECTED_BASE)),
None => return Err(syn::Error::new(item.ident.span(), EXPECTED_BASE)),
};
if let Some(attr) = base.attrs.iter().find(|a| a.path.is_ident("reflect"))
{
return Err(syn::Error::new_spanned(
attr,
- "`base` is the parent's object, whose fields the parent reflects",
+ "`base_` is the parent's object, whose fields the parent reflects",
));
}
let base_id = &base.ident;
diff --git a/rust/tvm-ffi/src/collections/tensor.rs
b/rust/tvm-ffi/src/collections/tensor.rs
index c22511af..1dd1b2da 100644
--- a/rust/tvm-ffi/src/collections/tensor.rs
+++ b/rust/tvm-ffi/src/collections/tensor.rs
@@ -279,7 +279,7 @@ struct TensorObjFromNDAlloc<TNDAlloc>
where
TNDAlloc: NDAllocator,
{
- base: TensorObj,
+ base_: TensorObj,
alloc: TNDAlloc,
}
@@ -290,21 +290,21 @@ unsafe impl<TNDAlloc: NDAllocator> ObjectCore for
TensorObjFromNDAlloc<TNDAlloc>
TensorObj::type_index()
}
unsafe fn object_header_mut(this: &mut Self) -> &mut
tvm_ffi_sys::TVMFFIObject {
- TensorObj::object_header_mut(&mut this.base)
+ TensorObj::object_header_mut(&mut this.base_)
}
}
unsafe impl<TNDAlloc: NDAllocator> ObjectCoreWithExtraItems for
TensorObjFromNDAlloc<TNDAlloc> {
type ExtraItem = i64;
fn extra_items_count(this: &Self) -> usize {
- (this.base.dltensor.ndim * 2) as usize
+ (this.base_.dltensor.ndim * 2) as usize
}
}
impl<TNDAlloc: NDAllocator> Drop for TensorObjFromNDAlloc<TNDAlloc> {
fn drop(&mut self) {
unsafe {
- self.alloc.free_data(&self.base.dltensor);
+ self.alloc.free_data(&self.base_.dltensor);
}
}
}
@@ -330,7 +330,7 @@ impl Tensor {
TNDAlloc: NDAllocator,
{
let tensor_obj = TensorObjFromNDAlloc {
- base: TensorObj {
+ base_: TensorObj {
object: Object::new(),
dltensor: DLTensor {
data: std::ptr::null_mut(),
@@ -348,14 +348,14 @@ impl Tensor {
let mut obj_arc = ObjectArc::new_with_extra_items(tensor_obj);
// The fresh allocation is unique and has not been exposed.
let object = &mut *ObjectArc::as_mut_ptr(&mut obj_arc);
- object.base.dltensor.shape =
+ object.base_.dltensor.shape =
TensorObjFromNDAlloc::extra_items(object).as_ptr() as *mut i64;
- object.base.dltensor.strides =
object.base.dltensor.shape.add(shape.len());
+ object.base_.dltensor.strides =
object.base_.dltensor.shape.add(shape.len());
let extra_items = TensorObjFromNDAlloc::extra_items_mut(object);
extra_items[..shape.len()].copy_from_slice(shape);
Shape::fill_strides_from_shape(shape, &mut
extra_items[shape.len()..]);
- let dltensor_ptr = &object.base.dltensor as *const DLTensor;
- object.base.dltensor.data =
object.alloc.alloc_data(&*dltensor_ptr);
+ let dltensor_ptr = &object.base_.dltensor as *const DLTensor;
+ object.base_.dltensor.data =
object.alloc.alloc_data(&*dltensor_ptr);
Self {
data: ObjectArc::from_raw(ObjectArc::into_raw(obj_arc) as *mut
TensorObj),
}
diff --git a/rust/tvm-ffi/src/derive.rs b/rust/tvm-ffi/src/derive.rs
index 89d54be9..7f89db37 100644
--- a/rust/tvm-ffi/src/derive.rs
+++ b/rust/tvm-ffi/src/derive.rs
@@ -54,7 +54,7 @@ pub use tvm_ffi_macros::Object;
///
/// #[rs_object("my_ext.MyPair")]
/// pub struct MyPairObj {
-/// base: Object,
+/// base_: Object,
/// pub first: i64,
/// pub second: i64,
/// }
@@ -66,7 +66,7 @@ pub use tvm_ffi_macros::Object;
///
/// #[rs_object("my_ext.MyTriple", final)]
/// pub struct MyTripleObj {
-/// base: MyPairObj,
+/// base_: MyPairObj,
/// pub third: i64,
/// }
///
diff --git a/rust/tvm-ffi/src/extra/mod.rs b/rust/tvm-ffi/src/extra/mod.rs
index af025a4b..192a0208 100644
--- a/rust/tvm-ffi/src/extra/mod.rs
+++ b/rust/tvm-ffi/src/extra/mod.rs
@@ -29,6 +29,7 @@ pub mod cold_init_lazy_lock;
pub mod dispatch;
pub mod module;
mod structural_common;
+pub mod structural_eq_hash;
pub mod structural_map;
pub mod structural_mutate;
pub mod structural_visit;
diff --git a/rust/tvm-ffi/src/extra/structural_eq_hash.rs
b/rust/tvm-ffi/src/extra/structural_eq_hash.rs
new file mode 100644
index 00000000..d388236d
--- /dev/null
+++ b/rust/tvm-ffi/src/extra/structural_eq_hash.rs
@@ -0,0 +1,189 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+//! Structural equality and hashing of values.
+
+use crate::reflection::AccessPath;
+use crate::{AnyView, Array, Result};
+
+/// Options of structural equality and hashing.
+///
+/// See also [`structural_equal_with`], [`structural_hash_with`] and
+/// [`get_first_structural_mismatch_with`].
+#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
+pub struct StructuralOptions {
+ /// Whether free variables (variables without a definition site) can be
+ /// mapped to each other.
+ pub map_free_vars: bool,
+ /// Whether to skip the data content of tensors.
+ pub skip_tensor_content: bool,
+}
+
+/// Whether two values are structurally equal: they have the same structure
+/// and content, compared recursively through containers and reflected
+/// fields, not by identity.
+///
+/// Objects compare as their [`SEqHashKind`](crate::SEqHashKind) declares.
+///
+/// # Arguments
+///
+/// * `lhs` - The left value.
+/// * `rhs` - The right value.
+///
+/// # Returns
+///
+/// Whether the values are structurally equal.
+///
+/// # Errors
+///
+/// Returns an error if an object in either value does not support structural
+/// equality.
+///
+/// # Examples
+///
+/// ```
+/// 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")]
+/// pub struct MyPairObj {
+/// base_: Object,
+/// pub first: i64,
+/// pub second: Array<i64>,
+/// }
+///
+/// #[derive(Clone, ObjectRef)]
+/// pub struct MyPair {
+/// data: ObjectArc<MyPairObj>,
+/// }
+///
+/// let pair = |second: Vec<i64>| {
+/// MyPair::from_object(MyPairObj::new(Object::new(), 1,
Array::from_vec(second)))
+/// };
+/// assert!(structural_equal(&pair(vec![2, 3]), &pair(vec![2, 3]))?);
+/// assert!(!structural_equal(&pair(vec![2, 3]), &pair(vec![2, 4]))?);
+/// # Ok::<(), tvm_ffi::Error>(())
+/// ```
+pub fn structural_equal<'l, 'r>(
+ lhs: impl Into<AnyView<'l>>,
+ rhs: impl Into<AnyView<'r>>,
+) -> Result<bool> {
+ structural_equal_with(lhs, rhs, StructuralOptions::default())
+}
+
+/// As [`structural_equal`], with [`StructuralOptions`].
+pub fn structural_equal_with<'l, 'r>(
+ lhs: impl Into<AnyView<'l>>,
+ rhs: impl Into<AnyView<'r>>,
+ options: StructuralOptions,
+) -> Result<bool> {
+ let args = [
+ lhs.into(),
+ rhs.into(),
+ AnyView::from(&options.map_free_vars),
+ AnyView::from(&options.skip_tensor_content),
+ ];
+ crate::cached_global_func!("ffi.StructuralEqual")
+ .call_packed(&args)?
+ .try_into()
+}
+
+/// The structural hash of a value, consistent with [`structural_equal`]:
+/// structurally equal values have the same hash.
+///
+/// # Arguments
+///
+/// * `value` - The value to hash.
+///
+/// # Returns
+///
+/// The hash.
+///
+/// # Errors
+///
+/// Returns an error if an object in the value does not support structural
+/// hashing.
+///
+/// See also [`structural_equal`].
+pub fn structural_hash<'a>(value: impl Into<AnyView<'a>>) -> Result<u64> {
+ structural_hash_with(value, StructuralOptions::default())
+}
+
+/// As [`structural_hash`], with [`StructuralOptions`].
+pub fn structural_hash_with<'a>(
+ value: impl Into<AnyView<'a>>,
+ options: StructuralOptions,
+) -> Result<u64> {
+ let args = [
+ value.into(),
+ AnyView::from(&options.map_free_vars),
+ AnyView::from(&options.skip_tensor_content),
+ ];
+ let hash: i64 = crate::cached_global_func!("ffi.StructuralHash")
+ .call_packed(&args)?
+ .try_into()?;
+ // The hash crosses the ABI as an `i64` bit pattern.
+ Ok(hash as u64)
+}
+
+/// The first place where two values are not structurally equal, as the
+/// [`AccessPath`] of that place in each value.
+///
+/// # Arguments
+///
+/// * `lhs` - The left value.
+/// * `rhs` - The right value.
+///
+/// # Returns
+///
+/// `None` if the values are structurally equal, else the paths of the first
+/// mismatch in `lhs` and in `rhs`.
+///
+/// # Errors
+///
+/// Returns an error if an object in either value does not support structural
+/// equality.
+///
+/// See also [`structural_equal`].
+pub fn get_first_structural_mismatch<'l, 'r>(
+ lhs: impl Into<AnyView<'l>>,
+ rhs: impl Into<AnyView<'r>>,
+) -> Result<Option<(AccessPath, AccessPath)>> {
+ get_first_structural_mismatch_with(lhs, rhs, StructuralOptions::default())
+}
+
+/// As [`get_first_structural_mismatch`], with [`StructuralOptions`].
+pub fn get_first_structural_mismatch_with<'l, 'r>(
+ lhs: impl Into<AnyView<'l>>,
+ rhs: impl Into<AnyView<'r>>,
+ options: StructuralOptions,
+) -> Result<Option<(AccessPath, AccessPath)>> {
+ let args = [
+ lhs.into(),
+ rhs.into(),
+ AnyView::from(&options.map_free_vars),
+ AnyView::from(&options.skip_tensor_content),
+ ];
+ // The pair crosses the ABI as a two-item array.
+ let pair: Option<Array<AccessPath>> =
+ crate::cached_global_func!("ffi.GetFirstStructuralMismatch")
+ .call_packed(&args)?
+ .try_into()?;
+ pair.map(|pair| Ok((pair.get(0)?, pair.get(1)?)))
+ .transpose()
+}
diff --git a/rust/tvm-ffi/src/extra/structural_mutate.rs
b/rust/tvm-ffi/src/extra/structural_mutate.rs
index e6e564d6..2d205453 100644
--- a/rust/tvm-ffi/src/extra/structural_mutate.rs
+++ b/rust/tvm-ffi/src/extra/structural_mutate.rs
@@ -94,7 +94,7 @@ struct StructuralMutatorVTable {
/// `StructuralMutatorObj`, with no Rust traversal state.
#[repr(C)]
pub struct StructuralMutatorObj {
- base: Object,
+ base_: Object,
vtable: *const StructuralMutatorVTable,
def_region_mode: UnsafeCell<i32>,
}
@@ -108,7 +108,7 @@ impl Default for StructuralMutatorObj {
#[inline(always)]
fn default() -> Self {
Self {
- base: Object::new(),
+ base_: Object::new(),
vtable: std::ptr::null(),
def_region_mode: UnsafeCell::new(DefRegionKind::None as i32),
}
@@ -516,7 +516,7 @@ impl<T: object::ObjectRefCore> MutationView<'_, T> {
/// # use tvm_ffi::object::ObjectRef;
/// # use tvm_ffi::*;
/// # #[rs_object("my_ext.MyPair", final)]
- /// # pub struct MyPairObj { base: Object, pub first: Array<i64>, pub
second: Array<i64> }
+ /// # 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> }
/// # struct Pass;
diff --git a/rust/tvm-ffi/src/extra/structural_visit.rs
b/rust/tvm-ffi/src/extra/structural_visit.rs
index 1b928028..e1a7eb60 100644
--- a/rust/tvm-ffi/src/extra/structural_visit.rs
+++ b/rust/tvm-ffi/src/extra/structural_visit.rs
@@ -100,7 +100,7 @@ struct StructuralVisitorVTable {
/// `StructuralVisitorObj`.
#[repr(C)]
pub struct StructuralVisitorObj {
- base: Object,
+ base_: Object,
vtable: *const StructuralVisitorVTable,
def_region_mode: UnsafeCell<i32>,
}
@@ -114,7 +114,7 @@ impl Default for StructuralVisitorObj {
#[inline(always)]
fn default() -> Self {
Self {
- base: Object::new(),
+ base_: Object::new(),
vtable: std::ptr::null(),
def_region_mode: UnsafeCell::new(DefRegionKind::None as i32),
}
@@ -128,7 +128,7 @@ impl Default for StructuralVisitorObj {
#[type_index(TVMFFITypeIndex::kTVMFFIVisitInterrupt)]
#[type_final]
pub struct VisitInterruptObj {
- base: Object,
+ base_: Object,
/// Payload returned with the interrupt, or FFI `None` for no payload.
pub value: Any,
}
@@ -200,7 +200,7 @@ impl VisitInterrupt {
#[inline(always)]
pub fn new<T: Into<Any>>(payload: T) -> Self {
Self::from_object(VisitInterruptObj {
- base: Object::new(),
+ base_: Object::new(),
value: payload.into(),
})
}
diff --git a/rust/tvm-ffi/src/extra/visit_error_context.rs
b/rust/tvm-ffi/src/extra/visit_error_context.rs
index 3d9855e0..f33a0c7b 100644
--- a/rust/tvm-ffi/src/extra/visit_error_context.rs
+++ b/rust/tvm-ffi/src/extra/visit_error_context.rs
@@ -36,7 +36,7 @@ use crate::tvm_ffi_sys::{TVMFFIAny, TVMFFIObject,
TVMFFITypeIndex};
#[type_key = "ffi.VisitErrorContext"]
#[type_final]
pub(crate) struct VisitErrorContextObj {
- base: Object,
+ base_: Object,
/// Visited nodes, innermost first; an entry may be `None`.
reverse_visit_pattern: Array<Option<ObjectRef>>,
/// The `extra_context` the error carried before this context replaced it.
@@ -93,7 +93,7 @@ pub(crate) fn update_visit_error_context(mut error: Error,
raw: TVMFFIAny) -> Er
};
if !ObjectArc::is_unique(&context) {
context = ObjectArc::new(VisitErrorContextObj {
- base: Object::new(),
+ base_: Object::new(),
reverse_visit_pattern:
context.reverse_visit_pattern.clone(),
prev_error_context: context.prev_error_context.clone(),
});
@@ -107,7 +107,7 @@ pub(crate) fn update_visit_error_context(mut error: Error,
raw: TVMFFIAny) -> Er
context
}
previous => ObjectArc::new(VisitErrorContextObj {
- base: Object::new(),
+ base_: Object::new(),
reverse_visit_pattern: Array::from_vec(vec![Some(node)]),
prev_error_context: previous,
}),
diff --git a/rust/tvm-ffi/src/lib.rs b/rust/tvm-ffi/src/lib.rs
index 72f0da38..36651f58 100644
--- a/rust/tvm-ffi/src/lib.rs
+++ b/rust/tvm-ffi/src/lib.rs
@@ -83,6 +83,10 @@ pub use crate::error::{
};
pub use crate::extra::dispatch::{Dispatcher, IntoDispatcher};
pub use crate::extra::module::Module;
+pub use crate::extra::structural_eq_hash::{
+ get_first_structural_mismatch, get_first_structural_mismatch_with,
structural_equal,
+ structural_equal_with, structural_hash, structural_hash_with,
StructuralOptions,
+};
pub use crate::extra::structural_map::structural_map;
pub use crate::extra::structural_mutate::{
structural_mutate, InplaceMode, MutationView, StructuralMutateEngine,
StructuralMutatorBase,
@@ -229,15 +233,16 @@ pub use tvm_ffi_macros::match_any;
/// `#[rs_object("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:
-/// `base: Object` for a type under `ffi.Object`. Declare the managed reference
-/// with [`#[derive(ObjectRef)]`](derive::ObjectRef).
+/// `base_`, is the parent type's object, embedded at the start of the struct:
+/// `base_: Object` for a type under `ffi.Object`; the trailing `_` leaves
+/// `base` free for a reflected field. Declare the managed reference with
+/// [`#[derive(ObjectRef)]`](derive::ObjectRef).
///
/// The macro adds to the struct:
///
/// * `#[repr(C)]` and `Deref` to the parent's object, so `obj.field` reads the
/// fields of both.
-/// * `MyPairObj::new(base, fields...)`, which takes the fields in order: a
+/// * `MyPairObj::new(base_, fields...)`, which takes the fields in order: a
/// scalar (`bool`, an integer or a float) as its own type, so that a literal
/// infers as it, and any other field from anything that converts into it.
///
@@ -276,7 +281,7 @@ pub use tvm_ffi_macros::match_any;
///
/// #[rs_object("my_ext.MyPair", final)]
/// pub struct MyPairObj {
-/// base: Object,
+/// base_: Object,
/// pub first: i64,
/// #[reflect(seq_hash_ignore)]
/// pub second: tvm_ffi::String,
diff --git a/rust/tvm-ffi/src/macros.rs b/rust/tvm-ffi/src/macros.rs
index aecaa0af..13e0383d 100644
--- a/rust/tvm-ffi/src/macros.rs
+++ b/rust/tvm-ffi/src/macros.rs
@@ -355,7 +355,7 @@ macro_rules! unsafe_impl_object_ref_upcast {
///
/// #[rs_object("my_ext.MyPair", final)]
/// pub struct MyPairObj {
-/// base: Object,
+/// base_: Object,
/// pub first: i64,
/// pub second: i64,
/// }
diff --git a/rust/tvm-ffi/src/object.rs b/rust/tvm-ffi/src/object.rs
index d16487a8..91d8f3e6 100644
--- a/rust/tvm-ffi/src/object.rs
+++ b/rust/tvm-ffi/src/object.rs
@@ -39,7 +39,7 @@ pub static TYPE_REGISTRATION: std::sync::Mutex<()> =
std::sync::Mutex::new(());
/// The root object type, `ffi.Object`: just the object header.
///
/// An object type defined in Rust starts with its parent's object, which for
-/// a direct subtype is `base: Object`.
+/// a direct subtype is `base_: Object`.
#[repr(C)]
pub struct Object {
header: TVMFFIObject,
@@ -511,7 +511,7 @@ pub unsafe trait ObjectRefCore: Sized + Clone +
AnyCompatible {
///
/// #[rs_object("my_ext.MyPair", final)]
/// pub struct MyPairObj {
-/// base: Object,
+/// base_: Object,
/// pub first: i64,
/// pub second: i64,
/// }
@@ -675,7 +675,7 @@ unsafe impl<T: ObjectRefAnyCompatible> AnyCompatible for T {
///
/// #[rs_object("my_ext.MyPair", final)]
/// pub struct MyPairObj {
-/// base: Object,
+/// base_: Object,
/// pub first: i64,
/// pub second: i64,
/// }
diff --git a/rust/tvm-ffi/src/reflection/registry.rs
b/rust/tvm-ffi/src/reflection/registry.rs
index b6a5da06..d21279d9 100644
--- a/rust/tvm-ffi/src/reflection/registry.rs
+++ b/rust/tvm-ffi/src/reflection/registry.rs
@@ -169,7 +169,7 @@ impl BitOr for FieldFlags {
///
/// #[rs_object("my_ext.MyPair", final, reflection =
MyPairObj::reflect_methods)]
/// pub struct MyPairObj {
-/// base: Object,
+/// base_: Object,
/// pub first: i64,
/// pub second: i64,
/// }
diff --git a/rust/tvm-ffi/tests/test_any.rs b/rust/tvm-ffi/tests/test_any.rs
index c7ae479c..fa91998b 100644
--- a/rust/tvm-ffi/tests/test_any.rs
+++ b/rust/tvm-ffi/tests/test_any.rs
@@ -360,7 +360,7 @@ fn test_any_dl_device() {
// Fields only reachable through `Deref`, where `Any::from` is not applicable.
#[repr(C)]
struct FieldHolderObj {
- base: Object,
+ base_: Object,
lhs: String,
rhs: Option<String>,
count: i64,
@@ -375,7 +375,7 @@ unsafe impl ObjectCore for FieldHolderObj {
}
#[inline]
unsafe fn object_header_mut(this: &mut Self) -> &mut TVMFFIObject {
- Object::object_header_mut(&mut this.base)
+ Object::object_header_mut(&mut this.base_)
}
}
@@ -419,7 +419,7 @@ fn test_to_any_matches_from_value() {
fn test_to_any_from_borrowed_object_field() {
let lhs = String::from("hello world this is a long string");
let holder = ObjectArc::new(FieldHolderObj {
- base: Object::new(),
+ base_: Object::new(),
lhs: lhs.clone(),
rhs: Some(lhs.clone()),
count: 11,
diff --git a/rust/tvm-ffi/tests/test_cast.rs b/rust/tvm-ffi/tests/test_cast.rs
index f4159806..228f3198 100644
--- a/rust/tvm-ffi/tests/test_cast.rs
+++ b/rust/tvm-ffi/tests/test_cast.rs
@@ -33,7 +33,7 @@ use tvm_ffi::*;
#[derive(Object)]
#[type_key = "testing.TestObjectBase"]
struct TestBaseObj {
- base: Object,
+ base_: Object,
value: i64,
// counter for recording the number of times the object is deleted
delete_counter: Arc<AtomicU32>,
@@ -56,7 +56,7 @@ struct TestBase {
#[type_key = "testing.TestObjectDerived"]
#[type_final]
struct TestDerivedObj {
- base: TestBaseObj,
+ base_: TestBaseObj,
extra: i64,
}
@@ -79,7 +79,7 @@ fn expect_err<T>(res: Result<T>) -> Error {
fn new_base(value: i64, delete_counter: Arc<AtomicU32>) -> TestBase {
TestBase {
data: ObjectArc::new(TestBaseObj {
- base: Object::new(),
+ base_: Object::new(),
value,
delete_counter,
}),
@@ -89,8 +89,8 @@ fn new_base(value: i64, delete_counter: Arc<AtomicU32>) ->
TestBase {
fn new_derived(value: i64, extra: i64, delete_counter: Arc<AtomicU32>) ->
TestDerived {
TestDerived {
data: ObjectArc::new(TestDerivedObj {
- base: TestBaseObj {
- base: Object::new(),
+ base_: TestBaseObj {
+ base_: Object::new(),
value,
delete_counter,
},
@@ -134,7 +134,7 @@ fn test_upcast_downcast_roundtrip() {
let obj: ObjectRef = base.into();
// downcast all the way back
let derived2: TestDerived = obj.downcast().unwrap();
- assert_eq!(derived2.data.base.value, 7);
+ assert_eq!(derived2.data.base_.value, 7);
assert_eq!(derived2.data.extra, 8);
// every step moved ownership; no extra references were created
assert_eq!(ObjectArc::strong_count(&derived2.data), 1);
@@ -150,7 +150,7 @@ fn test_borrowed_node_cast_preserves_reference_count() {
let strong_count = ObjectArc::strong_count(TestBase::data(&base));
let derived = base.downcast_ref::<TestDerivedObj>().unwrap();
- assert_eq!(derived.base.value, 7);
+ assert_eq!(derived.base_.value, 7);
assert_eq!(derived.extra, 8);
let base_node = base.downcast_ref::<TestBaseObj>().unwrap();
assert_eq!(base_node.value, 7);
@@ -228,7 +228,7 @@ fn test_try_cast_from_any_view_preserves_runtime_subtype() {
// Array::get calls this method directly, without check_any_strict first.
let base = unsafe { TestBase::try_cast_from_any_view(&raw) }.unwrap();
let derived: TestDerived = base.downcast().unwrap();
- assert_eq!(derived.data.base.value, 3);
+ assert_eq!(derived.data.base_.value, 3);
assert_eq!(derived.data.extra, 4);
assert_eq!(ObjectArc::strong_count(&derived.data), 2);
drop(derived);
diff --git a/rust/tvm-ffi/tests/test_match_any_ast_lookup.rs
b/rust/tvm-ffi/tests/test_match_any_ast_lookup.rs
index f68d82b0..a06bee7d 100644
--- a/rust/tvm-ffi/tests/test_match_any_ast_lookup.rs
+++ b/rust/tvm-ffi/tests/test_match_any_ast_lookup.rs
@@ -30,7 +30,7 @@ use tvm_ffi::{
#[type_child_slots = 20]
#[type_child_slots_can_overflow = false]
struct ExprObj {
- base: ObjectBase,
+ base_: ObjectBase,
}
fn register_type<T: ObjectCore>() -> i32 {
@@ -48,7 +48,7 @@ macro_rules! define_expr_leaves {
#[type_register]
#[type_final]
struct $object {
- base: ExprObj,
+ base_: ExprObj,
}
#[repr(C)]
@@ -61,8 +61,8 @@ macro_rules! define_expr_leaves {
fn default() -> Self {
Self {
data: ObjectArc::new($object {
- base: ExprObj {
- base: ObjectBase::new(),
+ base_: ExprObj {
+ base_: ObjectBase::new(),
},
}),
}
diff --git a/rust/tvm-ffi/tests/test_object_ref_any.rs
b/rust/tvm-ffi/tests/test_object_ref_any.rs
index c3c18582..02536d98 100644
--- a/rust/tvm-ffi/tests/test_object_ref_any.rs
+++ b/rust/tvm-ffi/tests/test_object_ref_any.rs
@@ -28,7 +28,7 @@ use tvm_ffi::{Any, AnyCompatible, AnyView, Object,
ObjectRefCore};
// A derived reference.
#[rs_object("testing.object_ref_any.Pair", final)]
pub struct PairObj {
- base: Object,
+ base_: Object,
pub first: i64,
pub second: i64,
}
diff --git a/rust/tvm-ffi/tests/test_object_reflection.rs
b/rust/tvm-ffi/tests/test_object_reflection.rs
index cd4cc99c..e58436a0 100644
--- a/rust/tvm-ffi/tests/test_object_reflection.rs
+++ b/rust/tvm-ffi/tests/test_object_reflection.rs
@@ -36,7 +36,7 @@ use tvm_ffi_sys::{
#[type_s_eq_hash_kind = "TreeNode"]
#[type_reflection(PointObj::reflect_methods)]
struct PointObj {
- base: Object,
+ base_: Object,
/// The x coordinate.
///
/// Signed.
@@ -85,7 +85,7 @@ struct Point {
#[type_register]
#[type_final]
struct Point3Obj {
- base: PointObj,
+ base_: PointObj,
#[reflect]
z: i64,
#[reflect(name = "labels")]
@@ -104,7 +104,7 @@ tvm_ffi::unsafe_impl_object_ref_upcast!(Point3 => Point);
fn point_obj(x: i64, y: f64, label: &str) -> PointObj {
PointObj {
- base: Object::new(),
+ base_: Object::new(),
x,
y,
label: label.into(),
@@ -121,7 +121,7 @@ fn point(x: i64, y: f64, label: &str) -> Point {
fn point3(x: i64, z: i64) -> Point3 {
Point3 {
data: ObjectArc::new(Point3Obj {
- base: point_obj(x, 0.5, "p3"),
+ base_: point_obj(x, 0.5, "p3"),
z,
tags: Array::from_vec(vec!["a".into(), "b".into()]),
r#type: Some(point(1, 2.0, "inner")),
@@ -311,7 +311,7 @@ fn test_methods_are_called_through_the_type_table() {
#[type_final]
#[type_reflection(Self::reflect_methods)]
struct TallyObj {
- base: Object,
+ base_: Object,
count: i64,
}
@@ -343,7 +343,7 @@ fn test_types_with_only_methods_register_their_reflection()
{
let count =
tvm_ffi::reflection::get_method_by_index(TallyObj::type_index(),
"count").unwrap();
let tally = Tally {
data: ObjectArc::new(TallyObj {
- base: Object::new(),
+ base_: Object::new(),
count: 11,
}),
};
@@ -433,7 +433,7 @@ fn test_repr_and_deep_copy_see_reflected_fields() {
let any = Any::from(original.clone());
let copy = Point3::try_from(call("ffi.DeepCopy",
&[AnyView::from(&any)])).unwrap();
assert!(!copy.same_as(&original));
- assert_eq!((copy.data.base.x, copy.data.z), (1, 2));
+ assert_eq!((copy.data.base_.x, copy.data.z), (1, 2));
assert_eq!(copy.data.tags.len(), 2);
// The nested object is copied too.
let (inner, original_inner) = (
@@ -468,7 +468,7 @@ fn test_shallow_copy_and_finality_are_type_attributes() {
// Applied to another type, it is a type error.
let other = Counter {
data: ObjectArc::new(CounterObj {
- base: Object::new(),
+ base_: Object::new(),
count: 0,
}),
};
@@ -495,7 +495,7 @@ fn test_shallow_copy_and_finality_are_type_attributes() {
#[type_register]
#[type_final]
struct CounterObj {
- base: Object,
+ base_: Object,
count: i64,
}
@@ -523,7 +523,7 @@ mod no_imports {
#[type_key = "testing.rust.ReflectedNoImports"]
#[type_register]
pub struct LeafObj {
- base: super::PointObj,
+ base_: super::PointObj,
#[reflect]
pub w: i64,
}
@@ -537,7 +537,7 @@ mod no_imports {
pub fn leaf(w: i64) -> Leaf {
Leaf {
data: tvm_ffi::ObjectArc::new(LeafObj {
- base: super::point_obj(0, 0.0, ""),
+ base_: super::point_obj(0, 0.0, ""),
w,
}),
}
@@ -558,7 +558,7 @@ fn test_derives_need_nothing_in_scope() {
#[type_register]
#[type_reflection(Self::reflect_methods)]
struct DuplicateMethodObj {
- base: Object,
+ base_: Object,
}
impl DuplicateMethodObj {
diff --git a/rust/tvm-ffi/tests/test_object_registration.rs
b/rust/tvm-ffi/tests/test_object_registration.rs
index 03403289..f7df78e4 100644
--- a/rust/tvm-ffi/tests/test_object_registration.rs
+++ b/rust/tvm-ffi/tests/test_object_registration.rs
@@ -32,7 +32,7 @@ use tvm_ffi_sys::{TVMFFIByteArray, TVMFFIGetTypeInfo,
TVMFFITypeKeyToIndex};
#[type_child_slots = 2]
#[type_child_slots_can_overflow = false]
struct RegisteredBaseObj {
- base: Object,
+ base_: Object,
value: i64,
}
@@ -48,7 +48,7 @@ struct RegisteredBase {
#[type_register]
#[type_final]
struct RegisteredLeafObj {
- base: RegisteredBaseObj,
+ base_: RegisteredBaseObj,
extra: i64,
}
@@ -94,8 +94,8 @@ fn test_derived_object_types_are_registered_on_first_use() {
fn test_registered_object_types_round_trip_through_any() {
let leaf = RegisteredLeaf {
data: ObjectArc::new(RegisteredLeafObj {
- base: RegisteredBaseObj {
- base: Object::new(),
+ base_: RegisteredBaseObj {
+ base_: Object::new(),
value: 7,
},
extra: 8,
@@ -119,7 +119,7 @@ macro_rules! concurrent_types {
#[type_key = $key]
#[type_register]
struct $name {
- base: Object,
+ base_: Object,
}
)*
};
@@ -180,7 +180,7 @@ fn test_registration_from_threads() {
#[derive(Object)]
#[type_key = "testing.rust.NeverRegistered"]
struct UnregisteredBindingObj {
- base: Object,
+ base_: Object,
}
#[test]
diff --git a/rust/tvm-ffi/tests/test_rs_object.rs
b/rust/tvm-ffi/tests/test_rs_object.rs
index e3dc68b8..ab334b95 100644
--- a/rust/tvm-ffi/tests/test_rs_object.rs
+++ b/rust/tvm-ffi/tests/test_rs_object.rs
@@ -33,7 +33,7 @@ use tvm_ffi_sys::{TVMFFIFieldInfo, TVMFFIGetTypeInfo,
TVMFFISEqHashKind, TVMFFIT
reflection = RsPointObj::reflect_methods
)]
pub struct RsPointObj {
- base: Object,
+ base_: Object,
/// The x coordinate.
pub x: i64,
#[reflect(writable, seq_hash_ignore, doc = "A label.")]
@@ -60,7 +60,7 @@ impl RsPointObj {
/// A subtype of a Rust-defined type, with a private field.
#[rs_object("testing.rust.RsPoint3", final)]
pub struct RsPoint3Obj {
- base: RsPointObj,
+ base_: RsPointObj,
pub z: i64,
pub tags: Array<tvm_ffi::String>,
tag: i64,
@@ -76,7 +76,7 @@ pub struct RsPoint3 {
/// A subtype without fields, which still registers its reflection.
#[rs_object("testing.rust.RsMarker", final)]
pub struct RsMarkerObj {
- base: RsPointObj,
+ base_: RsPointObj,
}
#[derive(Clone, ObjectRef)]
@@ -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)]
pub struct RsScalarsObj {
- base: Object,
+ base_: Object,
pub big: i64,
pub count: u64,
pub port: u16,
@@ -108,22 +108,26 @@ pub struct RsScalars {
#[derive(Object)]
#[type_key = "testing.TestCxxClassBase"]
pub struct TestCxxClassBaseObj {
- base: Object,
+ base_: Object,
pub v_i64: i64,
pub v_i32: i32,
}
impl TestCxxClassBaseObj {
pub fn new(v_i64: i64, v_i32: i32) -> Self {
- let base = Object::new();
- Self { base, v_i64, v_i32 }
+ let base_ = Object::new();
+ Self {
+ base_,
+ v_i64,
+ v_i32,
+ }
}
}
impl ShallowCopy for TestCxxClassBaseObj {
fn shallow_copy(&self) -> Self {
Self {
- base: ShallowCopy::shallow_copy(&self.base),
+ base_: ShallowCopy::shallow_copy(&self.base_),
v_i64: self.v_i64,
v_i32: self.v_i32,
}
@@ -133,7 +137,7 @@ impl ShallowCopy for TestCxxClassBaseObj {
unsafe impl UnsafeInit for TestCxxClassBaseObj {
unsafe fn drop_defined_fields(this: *mut Self) {
unsafe {
-
UnsafeInit::drop_defined_fields(std::ptr::addr_of_mut!((*this).base));
+
UnsafeInit::drop_defined_fields(std::ptr::addr_of_mut!((*this).base_));
drop_if_defined(std::ptr::addr_of_mut!((*this).v_i64));
drop_if_defined(std::ptr::addr_of_mut!((*this).v_i32));
}
@@ -149,7 +153,7 @@ pub struct TestCxxClassBase {
/// A subtype of a C++ type.
#[rs_object("testing.rust.RsCxxChild", final, s_eq_hash_kind = "TreeNode")]
pub struct RsCxxChildObj {
- base: TestCxxClassBaseObj,
+ base_: TestCxxClassBaseObj,
pub name: tvm_ffi::String,
}
@@ -162,7 +166,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)]
pub struct RsOwnInitObj {
- base: Object,
+ base_: Object,
pub v: i64,
}
@@ -189,7 +193,7 @@ impl RsOwnInitObj {
/// `init_off` fields, which `__ffi_init__` leaves all-zero: a value of these
types.
#[rs_object("testing.rust.RsInitOff", final)]
pub struct RsInitOffObj {
- base: Object,
+ base_: Object,
pub v: i64,
#[reflect(init_off)]
pub count: i64,
@@ -205,7 +209,7 @@ pub struct RsInitOff {
/// An `init_off` reference, which all-zero bytes leave undefined: its
registration fails.
#[rs_object("testing.rust.RsInitOffReference", final)]
pub struct RsInitOffReferenceObj {
- base: Object,
+ base_: Object,
#[reflect(init_off)]
pub items: Array<i64>,
}
@@ -218,7 +222,7 @@ mod reexported {
#[::tvm_ffi::rs_object("testing.rust.RsReexported", crate = ::tvm_ffi)]
pub struct ReexportedObj {
- base: ::tvm_ffi::Object,
+ base_: ::tvm_ffi::Object,
pub v: i64,
}
diff --git a/rust/tvm-ffi/tests/test_structural_eq_hash.rs
b/rust/tvm-ffi/tests/test_structural_eq_hash.rs
new file mode 100644
index 00000000..1b6bb724
--- /dev/null
+++ b/rust/tvm-ffi/tests/test_structural_eq_hash.rs
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+use tvm_ffi::reflection::AccessKind;
+use tvm_ffi::{
+ get_first_structural_mismatch, structural_equal, structural_equal_with,
structural_hash, Array,
+ StructuralOptions,
+};
+
+#[test]
+fn structural_functions_agree_and_locate_the_mismatch() -> tvm_ffi::Result<()>
{
+ let a = Array::from_vec(vec![1_i64, 2, 3]);
+ let same = Array::from_vec(vec![1_i64, 2, 3]);
+ let other = Array::from_vec(vec![1_i64, 2, 4]);
+
+ assert!(structural_equal(&a, &same)?);
+ assert!(!structural_equal(&a, &other)?);
+ let options = StructuralOptions {
+ map_free_vars: true,
+ ..Default::default()
+ };
+ assert!(structural_equal_with(&a, &same, options)?);
+ assert_eq!(structural_hash(&a)?, structural_hash(&same)?);
+ assert_ne!(structural_hash(&a)?, structural_hash(&other)?);
+
+ assert!(get_first_structural_mismatch(&a, &same)?.is_none());
+ let (lhs, rhs) = get_first_structural_mismatch(&a, &other)?.unwrap();
+ for path in [lhs, rhs] {
+ let step = path.step.clone().unwrap();
+ assert_eq!(
+ (step.kind, i64::try_from(step.key.clone())?),
+ (AccessKind::ArrayItem, 2)
+ );
+ }
+ Ok(())
+}
diff --git a/rust/tvm-ffi/tests/test_structural_mutate.rs
b/rust/tvm-ffi/tests/test_structural_mutate.rs
index 5f0a2faf..6d9a388a 100644
--- a/rust/tvm-ffi/tests/test_structural_mutate.rs
+++ b/rust/tvm-ffi/tests/test_structural_mutate.rs
@@ -191,7 +191,7 @@ impl RecursiveEntryMutator {
#[type_register]
#[type_s_eq_hash_kind = "DAGNode"]
struct RemapNodeObj {
- base: Object,
+ base_: Object,
#[reflect]
value: i64,
}
@@ -207,7 +207,7 @@ struct RemapNode {
#[type_key = "testing.rust.MutationFieldNode"]
#[type_register]
struct FieldNodeObj {
- base: Object,
+ base_: Object,
a: Array<i64>,
b: Any,
}
@@ -365,7 +365,7 @@ fn
user_driven_mutator_controls_default_recursion_and_in_place_opt_in() {
let key = Any::from(RemapNode {
data: ObjectArc::new(RemapNodeObj {
- base: Object::new(),
+ base_: Object::new(),
value: 7,
}),
});
@@ -852,7 +852,7 @@ fn
consuming_callbacks_forward_permissions_without_temporary_owners() {
}
let mut owner: Any = FieldNode {
data: ObjectArc::new(FieldNodeObj {
- base: Object::new(),
+ base_: Object::new(),
a: Array::from_vec(vec![1_i64]),
b: Array::from_vec(vec![2_i64]).into(),
}),
diff --git a/tests/python/test_stubgen_rust.py
b/tests/python/test_stubgen_rust.py
index 0cbcfa83..1ad7c48b 100644
--- a/tests/python/test_stubgen_rust.py
+++ b/tests/python/test_stubgen_rust.py
@@ -255,18 +255,16 @@ def test_render_rust_type_without_mirror(schema:
TypeSchema) -> None:
def test_rust_ident() -> None:
assert rust_ident("value") == "value"
- assert rust_ident("imports_") == "imports" # C++ member convention: one
trailing underscore
- assert rust_ident("__dict__") == "dict" # dunder convention: both pairs
- assert rust_ident("__dict") == "__dict" # neither convention: kept
verbatim
+ assert rust_ident("imports_") == "imports_" # names are kept as registered
+ assert rust_ident("__dict__") == "__dict__"
assert rust_ident("_private") == "_private"
- assert rust_ident("odd__") == "odd__"
- assert rust_ident("_") == "_"
- assert rust_ident("____") == "____"
assert rust_ident("type") == "r#type"
assert rust_ident("self") == "self_"
assert rust_ident("crate") == "crate_"
- assert rust_ident("base") == "base_" # the parent slot of every generated
object struct
- assert rust_ident("data") == "data" # the wrapper slot is `base`, so
`data` is free
+ assert rust_ident("base") == "base" # the generated members are `base_`
and `data_`
+ for name in ("base_", "data_"):
+ with pytest.raises(ValueError, match="collides"):
+ rust_ident(name)
def test_check_tokens_ignores_spelling() -> None:
@@ -274,7 +272,7 @@ def test_check_tokens_ignores_spelling() -> None:
"use super::super::ir::ExprObj;",
"/// Complete: every field is mirrored.",
"#[derive(tvm_ffi::derive::Object)]",
- "pub struct AddObj { base: ExprObj, pub a: i64, pub b: i64 }",
+ "pub struct AddObj { base_: ExprObj, pub a: i64, pub b: i64 }",
"const _: () = assert!(::core::mem::size_of::<AddObj>() == 40);",
"tvm_ffi::unsafe_impl_object_ref_upcast!(Add =>
super::super::ir::Expr);",
]
@@ -282,7 +280,7 @@ def test_check_tokens_ignores_spelling() -> None:
"use crate::ir::Expr;",
"#[derive(Object)]",
"pub struct AddObj {",
- " base: crate::ir::ExprObj,",
+ " base_: crate::ir::ExprObj,",
" pub a: i64,",
" pub b: i64,",
"}",
@@ -349,6 +347,7 @@ def
test_generator_declares_its_directives_and_records_imports() -> None:
"import-object",
"field",
"nullable",
+ "visibility",
"enum",
"opaque",
"upcast",
@@ -367,18 +366,20 @@ def
test_generator_declares_its_directives_and_records_imports() -> None:
# ---------------------------------------------------------------------------
ROOT_EXPECTED = f"""\
+#[repr(C)]
+#[derive(tvm_ffi::derive::ObjectRef, Clone)]
+pub struct Pair {{
+ data_: ObjectArc<PairObj>,
+}}
+
+/// The object behind [`Pair`].
+///
{NO_METADATA}
#[repr(C)]
#[derive(tvm_ffi::derive::Object)]
#[type_key = "demo.Pair"]
pub struct PairObj {{
- base: Object,
-}}
-
-#[repr(C)]
-#[derive(tvm_ffi::derive::ObjectRef, Clone)]
-pub struct Pair {{
- base: ObjectArc<PairObj>,
+ base_: Object,
}}
impl PairObj {{
@@ -456,7 +457,7 @@ def test_render_derived_object_same_module() -> None:
is_final=True,
)
text, imports = _render(info)
- assert "#[type_final]\npub struct AddObj {\n base: ExprObj,\n}" in text
+ assert "#[type_final]\npub struct AddObj {\n base_: ExprObj,\n}" in text
assert "impl Deref for AddObj {\n type Target = ExprObj;" in text
assert "pub fn a(&self) -> Result<Expr> {" in text
assert text.endswith("tvm_ffi::unsafe_impl_object_ref_upcast!(Add =>
BaseExpr, Add => Expr);")
@@ -468,7 +469,7 @@ def test_render_derived_object_cross_module() -> None:
"""A parent in another module is reached through the generated root."""
info = _info("tirx.Add", parent="ir.Expr", ancestors=["ffi.Object",
"ir.Expr"])
text, imports = _render(info)
- assert " base: ExprObj," in text
+ assert " base_: ExprObj," in text
assert text.endswith("tvm_ffi::unsafe_impl_object_ref_upcast!(Add =>
Expr);")
assert {"crate::ir::ExprObj", "crate::ir::Expr"} <= _uses(imports)
@@ -482,7 +483,7 @@ def test_render_object_under_builtin_parent() -> None:
ancestors=["ffi.Object", "ffi.Enum", "ffi.IntEnum"],
)
text, imports = _render(info)
- assert " base: FfiIntEnumObj," in text
+ assert " base_: FfiIntEnumObj," in text
# One stand-in per builtin ancestor below `ffi.Object`, chained through
`base`.
assert imports.builtin_mirrors == {"ffi.Enum": "Object", "ffi.IntEnum":
"FfiEnumObj"}
assert "tvm_ffi::Object" in _uses(imports)
@@ -492,7 +493,7 @@ def test_render_object_under_builtin_parent() -> None:
assert not any("Enum" in path for path in _uses(imports))
# A child of `ffi.Object` embeds the crate's header.
text, imports = _render(_info("demo.Root"))
- assert " base: Object," in text
+ assert " base_: Object," in text
assert imports.builtin_mirrors == {}
# A generated parent is embedded by name.
red = _info(
@@ -501,7 +502,7 @@ def test_render_object_under_builtin_parent() -> None:
ancestors=["ffi.Object", "ffi.Enum", "ffi.IntEnum", "demo.Color"],
)
text, imports = _render(red)
- assert " base: ColorObj," in text
+ assert " base_: ColorObj," in text
assert imports.builtin_mirrors == {}
@@ -515,7 +516,7 @@ use tvm_ffi::ObjectArc;
#[derive(tvm_ffi::derive::Object)]
#[type_key = "ffi.Enum"]
struct FfiEnumObj {
- base: Object,
+ base_: Object,
}
/// Header-only stand-in for the builtin `ffi.IntEnum`; it only carries the
ancestor depth.
@@ -524,7 +525,7 @@ struct FfiEnumObj {
#[derive(tvm_ffi::derive::Object)]
#[type_key = "ffi.IntEnum"]
struct FfiIntEnumObj {
- base: FfiEnumObj,
+ base_: FfiEnumObj,
}
/// Header-only stand-in for the builtin `ffi.StrEnum`; it only carries the
ancestor depth.
@@ -533,7 +534,7 @@ struct FfiIntEnumObj {
#[derive(tvm_ffi::derive::Object)]
#[type_key = "ffi.StrEnum"]
struct FfiStrEnumObj {
- base: FfiEnumObj,
+ base_: FfiEnumObj,
}"""
@@ -561,6 +562,12 @@ def test_import_section_defines_builtin_mirrors_once() ->
None:
ITER_VAR_EXPECTED = f"""\
+#[repr(C)]
+#[derive(tvm_ffi::derive::ObjectRef, Clone)]
+pub struct IterVar {{
+ data_: ObjectArc<IterVarObj>,
+}}
+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct IterVarType(i32);
@@ -586,25 +593,21 @@ impl TryFrom<i64> for IterVarType {{
}}
}}
+/// The object behind [`IterVar`].
+///
{NO_METADATA}
#[repr(C)]
#[derive(tvm_ffi::derive::Object)]
#[type_key = "tirx.IterVar"]
#[type_final]
pub struct IterVarObj {{
- base: PrimExprConvertibleObj,
-}}
-
-#[repr(C)]
-#[derive(tvm_ffi::derive::ObjectRef, Clone)]
-pub struct IterVar {{
- base: ObjectArc<IterVarObj>,
+ base_: PrimExprConvertibleObj,
}}
impl Deref for IterVarObj {{
type Target = PrimExprConvertibleObj;
fn deref(&self) -> &PrimExprConvertibleObj {{
- &self.base
+ &self.base_
}}
}}
@@ -723,12 +726,20 @@ def _add() -> ObjectInfo:
EXPR_EXPECTED = """\
+#[repr(C)]
+#[derive(tvm_ffi::derive::ObjectRef, Clone)]
+pub struct Expr {
+ data_: ObjectArc<ExprObj>,
+}
+
+/// The object behind [`Expr`].
+///
/// Complete: reflected fields fill [24, 40) exactly.
#[repr(C)]
#[derive(tvm_ffi::derive::Object)]
#[type_key = "ir.Expr"]
pub struct ExprObj {
- base: Object,
+ base_: Object,
pub span: Option<Span>,
pub ty: Type,
}
@@ -738,16 +749,10 @@ const _: () = {
assert!(::core::mem::align_of::<ExprObj>() == 8);
};
-#[repr(C)]
-#[derive(tvm_ffi::derive::ObjectRef, Clone)]
-pub struct Expr {
- base: ObjectArc<ExprObj>,
-}
-
impl ExprObj {
pub fn new(span: Option<Span>, ty: Type) -> Self {
- let base = Object::new();
- Self { base, span, ty }
+ let base_ = Object::new();
+ Self { base_, span, ty }
}
}
@@ -755,14 +760,14 @@ impl Expr {
/// Lossless complete-field allocation.
pub fn new(span: impl Into<Option<Span>>, ty: impl Into<Type>) -> Self {
let obj = ExprObj::new(span.into(), ty.into());
- Self { base: ObjectArc::new(obj) }
+ Self { data_: ObjectArc::new(obj) }
}
}
impl ShallowCopy for ExprObj {
fn shallow_copy(&self) -> Self {
Self {
- base: ShallowCopy::shallow_copy(&self.base),
+ base_: ShallowCopy::shallow_copy(&self.base_),
span: Clone::clone(&self.span),
ty: Clone::clone(&self.ty),
}
@@ -772,7 +777,7 @@ impl ShallowCopy for ExprObj {
unsafe impl UnsafeInit for ExprObj {
unsafe fn drop_defined_fields(this: *mut Self) {
unsafe {
- UnsafeInit::drop_defined_fields(addr_of_mut!((*this).base));
+ UnsafeInit::drop_defined_fields(addr_of_mut!((*this).base_));
drop_if_defined(addr_of_mut!((*this).span));
drop_if_defined(addr_of_mut!((*this).ty));
}
@@ -780,13 +785,21 @@ unsafe impl UnsafeInit for ExprObj {
}"""
ADD_EXPECTED = """\
+#[repr(C)]
+#[derive(tvm_ffi::derive::ObjectRef, Clone)]
+pub struct Add {
+ data_: ObjectArc<AddObj>,
+}
+
+/// The object behind [`Add`].
+///
/// Complete: reflected fields fill [40, 56) exactly.
#[repr(C)]
#[derive(tvm_ffi::derive::Object)]
#[type_key = "tirx.Add"]
#[type_final]
pub struct AddObj {
- base: ExprObj,
+ base_: ExprObj,
pub a: PrimExpr,
pub b: PrimExpr,
}
@@ -796,23 +809,17 @@ const _: () = {
assert!(::core::mem::align_of::<AddObj>() == 8);
};
-#[repr(C)]
-#[derive(tvm_ffi::derive::ObjectRef, Clone)]
-pub struct Add {
- base: ObjectArc<AddObj>,
-}
-
impl Deref for AddObj {
type Target = ExprObj;
fn deref(&self) -> &ExprObj {
- &self.base
+ &self.base_
}
}
impl AddObj {
pub(crate) fn new(span: Option<Span>, ty: PrimType, a: PrimExpr, b:
PrimExpr) -> Self {
- let base = ExprObj::new(span, ty.into());
- Self { base, a, b }
+ let base_ = ExprObj::new(span, ty.into());
+ Self { base_, a, b }
}
}
@@ -825,7 +832,7 @@ impl Add {
b: impl Into<PrimExpr>,
) -> Self {
let obj = AddObj::new(span.into(), ty.into(), a.into(), b.into());
- Self { base: ObjectArc::new(obj) }
+ Self { data_: ObjectArc::new(obj) }
}
}
@@ -854,7 +861,7 @@ def test_render_shallow_copy_that_fits_is_one_line() ->
None:
assert (
"impl ShallowCopy for StmtObj {\n"
" fn shallow_copy(&self) -> Self {\n"
- " Self { base: ShallowCopy::shallow_copy(&self.base), "
+ " Self { base_: ShallowCopy::shallow_copy(&self.base_), "
"span: Clone::clone(&self.span) }\n"
" }\n"
"}"
@@ -883,7 +890,6 @@ def test_render_complete_add_golden() -> None:
"crate::ir::ExprObj",
"crate::ir::Expr",
"crate::ir::Span",
- "crate::ir::Type",
}
@@ -892,7 +898,7 @@ def test_render_complete_span_and_prim_type() -> None:
text, _ = _render(_span())
assert (
"pub struct SpanObj {\n"
- " base: Object,\n"
+ " base_: Object,\n"
" pub source_name: SourceName,\n"
" pub line: i32,\n"
" pub column: i32,\n"
@@ -914,29 +920,41 @@ def test_render_complete_span_and_prim_type() -> None:
assert (
"/// Complete: reflected fields fill [32, 40) exactly (alignment
padding [36, 40))." in text
)
- assert "pub struct PrimTypeObj {\n base: TypeObj,\n pub dtype:
DLDataType,\n}" in text
+ assert "pub struct PrimTypeObj {\n base_: TypeObj,\n pub dtype:
DLDataType,\n}" in text
assert "assert!(::core::mem::size_of::<PrimTypeObj>() == 40);" in text
assert "tvm_ffi::DLDataType" in _uses(imports)
-def test_reserved_member_names_get_a_trailing_underscore() -> None:
- """A reflected `base` or `data` field must not collide with the generated
members."""
+def test_reflected_base_and_data_keep_their_names() -> None:
+ """Reflected names are kept (`base`, `data`, `cache_`); `visibility` makes
one `pub(crate)`."""
info = _info(
"demo.Ramp",
- (_field("base", "int", 24, 8), _field("data", "int", 32, 8)),
- total_size=40,
+ (
+ _field("base", "int", 24, 8),
+ _field("data", "int", 32, 8),
+ _field("cache_", "int", 40, 8),
+ ),
+ total_size=48,
is_final=True,
)
- text, _ = _render(info)
- assert " base: Object,\n pub base_: i64,\n pub data: i64,\n" in
text
- assert " pub fn new(base_: i64, data: i64) -> Self {" in text
- assert " Self { base, base_, data }" in text
- # The opaque form keeps the reflected name on the C ABI side.
- text, _ = _render(_info("demo.Node", (_field("base", "int"),)))
+ imports = RUST.new_imports()
+ RUST.add_directive(imports, "visibility", "demo.Ramp.cache_ ->
pub(crate)", 1)
+ text, _ = _render(info, imports)
+ assert (
+ " base_: Object,\n pub base: i64,\n pub data: i64,\n
pub(crate) cache_: i64,\n"
+ ) in text
+ assert " pub fn new(base: i64, data: i64, cache_: i64) -> Self {" in
text
+ assert " Self { base_, base, data, cache_ }" in text
+ imports = RUST.new_imports()
+ 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
FieldGetter::new(Self::type_index(), "base")'
+ 'pub fn base(&self) -> Result<i64> {\n
FieldGetter::new(Self::type_index(), "base")'
in text
)
+ assert " pub(crate) fn cache_(&self) -> Result<i64> {" in text
+ with pytest.raises(ValueError, match=r"`visibility` directive at line 2"):
+ Directives().add("visibility", "demo.Node.cache_ -> pub(super)", 2)
def test_render_complete_enum_field() -> None:
@@ -969,6 +987,10 @@ def test_allow_missing_docs_and_closed_enum() -> None:
RUST.add_directive(imports, "enum", "demo.Pair.kind -> Kind(i32) closed {
A=0, B=-1 }", 2)
text, _ = _render(info, imports)
assert text.startswith(
+ "#[allow(missing_docs)]\n"
+ "#[repr(C)]\n"
+ "#[derive(tvm_ffi::derive::ObjectRef, Clone)]\n"
+ "pub struct Pair {\n data_: ObjectArc<PairObj>,\n}\n\n"
"#[allow(missing_docs)]\n"
"#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]\n"
"#[repr(i32)]\n"
@@ -1030,7 +1052,7 @@ def
test_unsafe_init_drops_each_field_by_its_all_zero_bytes() -> None:
"unsafe impl UnsafeInit for MixedObj {\n"
" unsafe fn drop_defined_fields(this: *mut Self) {\n"
" unsafe {\n"
- "
UnsafeInit::drop_defined_fields(addr_of_mut!((*this).base));\n"
+ "
UnsafeInit::drop_defined_fields(addr_of_mut!((*this).base_));\n"
" drop_in_place(addr_of_mut!((*this).count));\n"
" drop_in_place(addr_of_mut!((*this).value));\n"
" drop_if_defined(addr_of_mut!((*this).items));\n"
@@ -1083,7 +1105,7 @@ def test_complete_big_int_field_mirrors() -> None:
)
text, imports = _render(info)
assert (
- " base: Object,\n"
+ " base_: Object,\n"
" pub value: BigInt,\n"
" pub maybe: Optional<BigInt>,\n"
" pub count: i64,\n"
@@ -1160,7 +1182,7 @@ def
test_registry_big_int_fields_are_mirrored(big_int_holder: ObjectInfo) -> Non
text, imports = _render(big_int_holder)
assert (
"pub struct BigIntHolderObj {\n"
- " base: Object,\n"
+ " base_: Object,\n"
" pub value: BigInt,\n"
" pub maybe: Optional<BigInt>,\n"
" pub count: i64,\n"
@@ -1210,7 +1232,7 @@ def test_custom_new_renames_the_wrapper_allocator() ->
None:
" b: impl Into<Expr>,\n"
" ) -> Self {\n"
" let obj = AddObj::new(span.into(), ty.into(), a.into(),
b.into());\n"
- " Self { base: ObjectArc::new(obj) }\n"
+ " Self { data_: ObjectArc::new(obj) }\n"
" }\n}"
) in text
assert " pub fn new(" not in text
@@ -1226,13 +1248,12 @@ def test_allocator_body_wraps_like_rustfmt() -> None:
name, text = render(70) # the `let` is exactly 100 columns
assert f" let obj = WObj::new({name});\n" in text
- name, text = render(76) # 106 columns; the call alone is exactly 100
+ name, text = render(76) # 106 columns; the call alone and `Self { .. }`
are exactly 100
assert f" let obj =\n WObj::new({name});\n" in text
- name, text = render(77) # the call alone is 101 columns; `Self { .. }` is
exactly 100
+ assert f" Self {{ base_, {name} }}\n" in text
+ name, text = render(77) # the call alone is 101 columns
assert f" let obj = WObj::new(\n {name},\n );\n"
in text
- assert f" Self {{ base, {name} }}\n" in text
- name, text = render(78)
- assert f" Self {{\n base,\n {name},\n
}}\n" in text
+ assert f" Self {{\n base_,\n {name},\n
}}\n" in text
def test_upcast_directive_adds_typed_views() -> None:
@@ -1265,7 +1286,7 @@ def test_builtin_parent_keeps_the_type_opaque() -> None:
)
text, _ = _render(info)
assert "/// Opaque: parent 'ffi.Enum' is opaque (no-mirror)." in text
- assert " base: FfiEnumObj," in text
+ assert " base_: FfiEnumObj," in text
assert "const _: () =" not in text
assert "fn new(" not in text
# The registry fixtures under `ffi.Enum` / `ffi.IntEnum` / `ffi.StrEnum`
follow the same rule.
@@ -1285,7 +1306,7 @@ def test_opaque_parent_keeps_the_child_opaque() -> None:
_register(_info("ir.Expr", (_field("span", "ir.Span", 24, 8),),
total_size=40)) # hole
text, _ = _render(_add())
assert "/// Opaque: parent 'ir.Expr' is opaque (uncovered-bytes)." in text
- assert " base: ExprObj," in text
+ assert " base_: ExprObj," in text
assert "pub fn a(&self) -> Result<Expr> {" in text
@@ -1366,7 +1387,7 @@ def test_registry_complete_chain_is_mirrored() -> None:
"/// Complete: reflected fields fill [24, 40) exactly (alignment
padding [36, 40))." in base
)
assert (
- "pub struct TestCxxClassBaseObj {\n base: Object,\n pub v_i64:
i64,\n pub v_i32: i32,\n}"
+ "pub struct TestCxxClassBaseObj {\n base_: Object,\n pub v_i64:
i64,\n pub v_i32: i32,\n}"
in base
)
assert "assert!(::core::mem::size_of::<TestCxxClassBaseObj>() == 40);" in
base
@@ -1375,7 +1396,7 @@ def test_registry_complete_chain_is_mirrored() -> None:
dd, imports =
_render(object_info_from_type_key("testing.TestCxxClassDerivedDerived"))
assert (
- " base: TestCxxClassDerivedObj,\n pub v_str: String,\n pub
v_bool: bool,\n}" in dd
+ " base_: TestCxxClassDerivedObj,\n pub v_str: String,\n pub
v_bool: bool,\n}" in dd
)
# The allocator flattens the chain; a signature over 100 columns wraps.
assert (
@@ -1388,8 +1409,8 @@ def test_registry_complete_chain_is_mirrored() -> None:
" v_str: String,\n"
" v_bool: bool,\n"
" ) -> Self {\n"
- " let base = TestCxxClassDerivedObj::new(v_i64, v_i32, v_f64,
v_f32);\n"
- " Self { base, v_str, v_bool }\n"
+ " let base_ = TestCxxClassDerivedObj::new(v_i64, v_i32, v_f64,
v_f32);\n"
+ " Self { base_, v_str, v_bool }\n"
" }\n"
"}"
) in dd
@@ -1406,7 +1427,7 @@ def test_registry_complete_chain_is_mirrored() -> None:
"impl ShallowCopy for TestCxxClassDerivedDerivedObj {\n"
" fn shallow_copy(&self) -> Self {\n"
" Self {\n"
- " base: ShallowCopy::shallow_copy(&self.base),\n"
+ " base_: ShallowCopy::shallow_copy(&self.base_),\n"
" v_str: Clone::clone(&self.v_str),\n"
" v_bool: Clone::clone(&self.v_bool),\n"
" }\n"
@@ -1479,19 +1500,19 @@ def test_cli_init_generates_a_module_tree(tmp_path:
Path, monkeypatch: pytest.Mo
assert text.startswith("#![allow(dead_code, unused_imports)]\n")
assert "use tvm_ffi::FieldGetter;" in text # the opaque fixtures read
through getters
assert '#[type_key = "testing.TestCxxClassDerivedDerived"]' in text
- assert " base: TestCxxClassDerivedObj,\n pub v_str: String," in text
+ assert " base_: TestCxxClassDerivedObj,\n pub v_str: String," in text
assert (
"tvm_ffi::unsafe_impl_object_ref_upcast!(TestCxxClassDerivedDerived =>
TestCxxClassBase, "
"TestCxxClassDerivedDerived => TestCxxClassDerived);"
) in text
# Builtin ancestors are mirrored once, in the import section; enum
fixtures embed the last one.
- assert text.count("struct FfiEnumObj {\n base: Object,\n}") == 1
- assert text.count("struct FfiIntEnumObj {\n base: FfiEnumObj,\n}") == 1
- assert text.count("struct FfiStrEnumObj {\n base: FfiEnumObj,\n}") == 1
+ assert text.count("struct FfiEnumObj {\n base_: Object,\n}") == 1
+ assert text.count("struct FfiIntEnumObj {\n base_: FfiEnumObj,\n}") == 1
+ assert text.count("struct FfiStrEnumObj {\n base_: FfiEnumObj,\n}") == 1
assert text.index("struct FfiIntEnumObj") <
text.index(f"{C.RUST_SYNTAX.begin} object/")
- assert "pub struct TestEnumVariantObj {\n base: FfiEnumObj,\n}" in text
- assert "pub struct TestCxxIntEnumObj {\n base: FfiIntEnumObj,\n}" in
text
- assert "pub struct TestCxxStrEnumObj {\n base: FfiStrEnumObj,\n}" in
text
+ assert "pub struct TestEnumVariantObj {\n base_: FfiEnumObj,\n}" in text
+ assert "pub struct TestCxxIntEnumObj {\n base_: FfiIntEnumObj,\n}" in
text
+ assert "pub struct TestCxxStrEnumObj {\n base_: FfiStrEnumObj,\n}" in
text
# Running again over the generated tree is a no-op.
assert stub_cli.__main__() == 0
assert (root / "testing" / "mod.rs").read_text(encoding="utf-8") == text
@@ -1566,7 +1587,7 @@ def test_cli_check_ignores_how_a_binding_is_spelled(
for old, new in [
("use tvm_ffi::String;\n", ""),
("use std::ops::Deref;\n", "use std::ops::Deref;\nuse
crate::testing::TestCompare;\n"),
- (" base: Object,\n", " base: tvm_ffi::Object,\n"),
+ (" base_: Object,\n", " base_: tvm_ffi::Object,\n"),
("/// Complete: ", "/// Mirrored: "),
(
" pub fn new(\n"
@@ -1652,7 +1673,7 @@ def test_unasked_dependencies_are_an_error() -> None:
_render(info, declared=_declared("tirx.Add"))
# Declared in another file of the run: reached by the `crate::` path of
its module.
text, imports = _render(info, declared=_declared("tirx.Add", "ir.Expr",
"tirx.Var"))
- assert " base: ExprObj," in text
+ assert " base_: ExprObj," in text
assert "pub fn a(&self) -> Result<Var> {" in text # same module: a bare
name
assert {"crate::ir::ExprObj", "crate::ir::Expr"} <= _uses(imports)
@@ -1702,7 +1723,7 @@ def test_blocks_are_named_by_the_module_of_their_file(
assert stub_cli.__main__() == 0
text = nested.read_text(encoding="utf-8")
assert "use crate::TestCxxClassBase;\nuse crate::TestCxxClassBaseObj;\n"
in text
- assert " base: TestCxxClassBaseObj," in text
+ assert " base_: TestCxxClassBaseObj," in text
assert (
"tvm_ffi::unsafe_impl_object_ref_upcast!(TestCxxClassDerived =>
TestCxxClassBase);" in text
)
@@ -1726,7 +1747,7 @@ def test_ty_map_parent_redirects_base_deref_and_upcast()
-> None:
imports = RustImports()
generate_rust_object(block, ty_map, imports, Options(), info,
_declared("tirx.Add"))
text = "\n".join(block.lines[1:-1])
- assert " base: ExprObj," in text
+ assert " base_: ExprObj," in text
assert "impl Deref for AddObj {\n type Target = ExprObj;" in text
assert text.endswith("tvm_ffi::unsafe_impl_object_ref_upcast!(Add =>
Expr);")
assert {"crate::ir::ExprObj", "crate::ir::Expr"} <= _uses(imports)
@@ -1762,8 +1783,8 @@ def test_allocator_hands_a_ty_map_parent_its_own_types()
-> None:
assert (
"impl ChildObj {\n"
" pub(crate) fn new(name: String, kind: Kind, n: i64) -> Self {\n"
- " let base = BaseObj::new(name, kind.into());\n"
- " Self { base, n }\n"
+ " let base_ = BaseObj::new(name, kind.into());\n"
+ " Self { base_, n }\n"
) in text
assert (
" pub fn new(name: impl Into<String>, kind: impl Into<Kind>, n:
i64) -> Self {\n"
@@ -1796,7 +1817,7 @@ def
test_stage_3_checks_dependencies_across_the_run(tmp_path: Path) -> None:
_stage_1(info, ty_map)
_stage_3(info, Options(dry_run=True), ty_map, {}, RUST, declared)
text = "\n".join(line for block in info.code_blocks for line in
block.lines)
- assert " base: TestCxxClassBaseObj," in text
+ assert " base_: TestCxxClassBaseObj," in text
assert "use crate::hand::TestCxxClassBaseObj;" in text
# Not final: public `new`; a hand-written parent may lack `ShallowCopy`,
so the type has none.
assert "impl TestCxxClassDerivedObj {\n pub fn new(" in text
@@ -1826,7 +1847,7 @@ def
test_parent_with_the_objects_own_name_is_spelled_in_full() -> None:
info = _info("demo.s.Frame", parent="demo.t.Frame",
ancestors=["ffi.Object", "demo.t.Frame"])
text, imports = _render(info)
parent = "crate::demo::t::Frame"
- assert f" base: {parent}Obj," in text
+ assert f" base_: {parent}Obj," in text
assert f" type Target = {parent}Obj;" in text
assert text.endswith(f"tvm_ffi::unsafe_impl_object_ref_upcast!(Frame =>
{parent});")
assert not any(path.startswith(f"{parent}") for path in _uses(imports))
@@ -1866,7 +1887,7 @@ def
test_ty_map_target_in_the_files_own_module_is_not_imported() -> None:
assert " pub fn op(&self) -> Result<Op> {" in text
assert "crate::ir::Op" not in _uses(imports)
text, imports = render(_info("ir.Special", parent="ir.Op",
ancestors=["ffi.Object", "ir.Op"]))
- assert " base: OpObj," in text
+ assert " base_: OpObj," in text
assert text.endswith("tvm_ffi::unsafe_impl_object_ref_upcast!(Special =>
Op);")
assert not any(path.startswith("crate::") for path in _uses(imports))
# Elsewhere the target is imported as written.