tvm_ffi::derive

Derive Macro Object

#[derive(Object)]
{
    // Attributes available to this derive:
    #[type_key]
    #[type_index]
    #[type_final]
    #[type_register]
    #[type_child_slots]
    #[type_child_slots_can_overflow]
    #[type_s_eq_hash_kind]
    #[type_reflection]
    #[reflect]
}
Expand description

namespace to re-export derive macros Derive ObjectCore for an object struct whose first field is its parent.

#[type_key = "..."] names the type, and #[type_index(...)] gives it a static index. Otherwise a type defined in Rust declares #[type_register]: it is registered under its parent on first use of type_index(), or takes the index its key already has, as C++ TVM_FFI_DECLARE_OBJECT_INFO does. Without #[type_register], the type binds a key its defining library, usually C++, has registered, and type_index() panics if that library is not loaded yet, so a binding never registers a key with the wrong child slots.

#[type_final], and with #[type_register] #[type_child_slots = N] and #[type_child_slots_can_overflow = bool], mirror C++ _type_final, _type_child_slots (default 0) and _type_child_slots_can_overflow (default true).

With #[type_register], fields marked #[reflect] are registered with the type table when the type is, as C++ refl::ObjectDef<T>().def_ro(...) does: a getter, a setter, the doc string and the type schema per field, the metadata, and the __ffi_shallow_copy__ and __ffi_type_final__ type attributes. C++ structural equality and hashing, repr, deep copy and Python then see the fields. #[reflect(writable, seq_hash_ignore, ...)] adds the FieldFlags of those names, name = "..." registers the field under another name, and doc = "..." sets its doc string, otherwise the field’s /// doc comment. #[type_s_eq_hash_kind = "TreeNode"] names the SEqHashKind; without it the type keeps its nearest reflected ancestor’s, as a C++ subclass inherits _type_s_eq_hash_kind. #[type_reflection(path)] names a function fn(&mut ObjectDef<Self>) that registers the type’s methods, as C++ refl::ObjectDef<T>().def(...) does. A type with any of these attributes also gets ShallowCopy, which shallow-copies the parent and clones every other field, so the parent must implement ShallowCopy (Object and reflected Rust types do) and the fields must be Clone; a reflected field’s type must implement AnyCompatible.

Registrations from Rust hold one lock, so they do not race each other. The runtime’s type table is not locked otherwise: as for C++, a type must not be registered while another thread loads a library that registers types. Calling type_index() before starting such threads registers a type eagerly.