Repository navigation
[FEAT][RUST] Register the reflection of Rust-defined object types - #859
Merged
Merged
Conversation
Rust gains what Python `_register_py_class` and C++ `refl::ObjectDef<T>` already do: register a type's fields and metadata with the type table, so that C++ structural equality and hashing, `ReprPrint`, `DeepCopy` and Python see the fields of a Rust type. - tvm-ffi-sys declares `TVMFFITypeRegisterField`, `TVMFFITypeRegisterMethod` and `TVMFFITypeRegisterMetadata`. - `#[derive(Object)]` on a `#[type_register]` type registers its `#[reflect]` / `#[reflect(writable, ...)]` fields when the type is registered: the metadata, fields (C ABI getter/setter, type schema, static type index), and the `__ffi_shallow_copy__` and `__ffi_type_final__` type attributes. It takes `#[type_s_eq_hash_kind = "..."]` (otherwise the nearest reflected ancestor's kind) and implements `ShallowCopy`. - `tvm_ffi::reflection` adds `FieldFlags`, `SEqHashKind` and `ShallowCopy`. The registration builder it uses, `ObjectDef`, is hidden and `unsafe`: only the derive, which takes each field's type and offset from the same field, calls it. - `#[derive(ObjectRef)]` names `ObjectArc` by path, so it needs nothing in scope.
Seven-Streams
force-pushed
the
feat/rust-reflect-register
branch
from
October 20, 2026 01:41
65a5774 to
d6e7dca
Compare
Seven-Streams
marked this pull request as ready for review
October 20, 2026 13:58
tqchen
approved these changes
Oct 20, 2026
This was referenced Oct 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Let an object type defined in Rust register its reflection with the type
table, as Python
_register_py_classand C++refl::ObjectDef<T>already do.Once registered, the fields of a Rust type are visible to C++ structural
equality and hashing,
ffi.ReprPrint,ffi.DeepCopyand Python's generatedclasses, so every language can define first-class types.
Changes
tvm-ffi-sys: declareTVMFFITypeRegisterField,TVMFFITypeRegisterMethodand
TVMFFITypeRegisterMetadata.tvm_ffi::reflection:FieldFlags,SEqHashKindandShallowCopy(implemented for
Object), plus a hidden builder,ObjectDef<N>, that onlythe derive calls. It registers the type metadata (size, structural eq/hash
kind), one field record per field (C ABI getter and setter,
type_schema,static type index), and the
__ffi_shallow_copy__and__ffi_type_final__type attributes. If the type already has metadata, every call is a no-op.
Its entry point and
fieldareunsafe: the derive takes each field's typeand
offset_of!from the same field, which is what makes the registeredgetter and setter sound. There is no public API for registering fields by
hand.
#[derive(Object)]: on a#[type_register]type,#[reflect]/#[reflect(flag, ...)]fields are registered when the type is (on its firsttype_index(), under the existing registration lock), with offsets fromoffset_of!. Flag names map toFieldFlagsconstants.#[type_s_eq_hash_kind = "..."]sets the kind. Without it, the type takesits nearest reflected ancestor's kind, as a C++ subclass inherits
_type_s_eq_hash_kind. The derive also implementsShallowCopy: it copiesthe parent with
ShallowCopyand clones every other field.#[derive(ObjectRef)]: nameObjectArcby path in the three places thatused the bare name, so the derive no longer needs
ObjectArcin scope.Compatibility
#[reflect]/#[type_s_eq_hash_kind]expand asbefore.
ShallowCopy.Objectandreflected Rust types do, but a parent bound from C++ needs a hand-written
impl. The reflected type's other fields must be
Clone, and its reflectedfields must be
AnyCompatible.__ffi_new__is registered, soffi.ToJSONGraphrejectsthese types; serialization is out of scope for this PR.
TypeTraits:Optional<T>,String,BytesandBigIntarekTVMFFIAny;ArrayandListhavetheir own indices;
Map,Dictand other object references arekTVMFFIObject.Tests
tests/test_object_reflection.rscovers:TypeErroron a value of the wrong typeffi.StructuralEqual/ffi.StructuralHash, including aseq_hash_ignorefieldffi.ReprPrintandffi.DeepCopy__ffi_shallow_copy__and__ffi_type_final__#[type_register]type without#[reflect]registers no reflectioncargo test(whole workspace),cargo clippy --all-targets,cargo doc,and
pre-commit runon the changed files.