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-tirx.git


The following commit(s) were added to refs/heads/main by this push:
     new a7ed1015a [Rust] Bump tvm-ffi to 3718ae5b: soundness fixes and API 
lock (#224)
a7ed1015a is described below

commit a7ed1015a5fc9ab7d9342d594dcd5f5fa78f7556
Author: Tianqi Chen <[email protected]>
AuthorDate: Sat Nov 7 14:33:39 2026 -0500

    [Rust] Bump tvm-ffi to 3718ae5b: soundness fixes and API lock (#224)
    
     migrate TIRx's Rust crate.
    
    - Regenerated bindings: upcasts are checked unless the target is
    refined, immutability is declared with `#[type_immutable]`, and
    reflection-built objects drop through `#[type_rs_parent]`;
    builtin-ancestor stand-ins are hidden public structs.
    - C++ `int` fields keep their `i32` mirrors through `field` directives,
    since the generator now leaves integers narrower than 64 bits opaque
    unless a directive types them.
    - `IntExpr` declares its refined parent with `unsafe parent = PrimExpr`;
    `IntExpr`, `PrimVar` and `Op` declare their upcasts to `Expr` in the
    checked form.
    - `OpDef` stores its native hooks with `unsafe
    NativeFunction::into_any`, each vouching for the signature the operator
    attribute's readers call.
    - `TypeAttrColumn::lookup`, `Any::is_none` and `ObjectArc::is_unique`
    replace `TypeAttrColumn::new`, type-index comparisons with None, and the
    strong-count check of the in-place mutation.
    
    For users of `tvm_tirx::tvm_ffi`: items that moved off the tvm-ffi crate
    root are imported from their modules, e.g.
    `tvm_ffi::extra::structural_visit::{DefRegionKind,
    StructuralVisitorView}`,
    `tvm_ffi::extra::structural_mutate::StructuralMutatorView`,
    `tvm_ffi::object::ObjectIdentity` and `tvm_ffi::rvalue_ref::RValueRef`;
    `tvm_ffi::String`'s `Display` no longer quotes the text.
---
 3rdparty/tvm-ffi                                   |   2 +-
 examples/ir_guides/rust/src/lib.rs                 |   8 +-
 rust/src/backend/cuda/op.rs                        |  15 +-
 rust/src/ir/attrs.rs                               |  26 +--
 rust/src/ir/base.rs                                |   3 +-
 rust/src/ir/expr.rs                                | 187 ++++++---------------
 rust/src/ir/function.rs                            |  29 +---
 rust/src/ir/global_info.rs                         |  21 +--
 rust/src/ir/location.rs                            |  45 ++---
 rust/src/ir/module.rs                              |   7 +-
 rust/src/ir/op.rs                                  |  10 +-
 rust/src/ir/op_def.rs                              |  18 +-
 rust/src/ir/prim/expr.rs                           | 162 +++++++++---------
 rust/src/ir/prim/vector_expr.rs                    |  18 +-
 rust/src/ir/stmt.rs                                |  85 +++-------
 rust/src/ir/transform/core.rs                      |  22 ++-
 rust/src/ir/transform/instrument.rs                |   2 +-
 rust/src/ir/type.rs                                |  73 +++-----
 rust/src/macro/def_expr_op_overloads.rs            |   2 +-
 rust/src/runtime.rs                                |   5 +-
 rust/src/sym/analyzer.rs                           |  17 +-
 rust/src/sym/constraint.rs                         |  36 +---
 rust/src/sym/int_set.rs                            |   5 +-
 rust/src/sym/iter_affine_map.rs                    |  18 +-
 rust/src/tirx/function.rs                          |   5 +-
 rust/src/tirx/index_map.rs                         |   3 +-
 rust/src/tirx/layout.rs                            |  18 +-
 rust/src/tirx/op.rs                                |   5 +-
 rust/src/tirx/structural.rs                        |   6 +-
 .../tirx/structural_map_with_constraint_context.rs |   4 +-
 .../structural_mutate_with_constraint_context.rs   |  18 +-
 .../structural_visit_with_constraint_context.rs    |  11 +-
 .../structural_walk_with_constraint_context.rs     |   6 +-
 rust/src/tirx/tensor.rs                            |   6 +-
 rust/src/tirx/thread_scope.rs                      |   5 +-
 rust/src/tirx/tile/dispatch.rs                     |   3 +-
 rust/src/tirx/tile/instruction.rs                  |  10 +-
 rust/src/tirx/transform/function_pass.rs           |   9 +-
 rust/src/tirx/transform/transform.rs               |  11 +-
 rust/src/tirx/type.rs                              |   5 +-
 rust/tests/ir/op_def.rs                            |   2 +-
 rust/tests/runtime/main.rs                         |   4 +-
 rust/tests/sym/iter_affine_map.rs                  |   3 +-
 rust/tests/target/main.rs                          |   7 +-
 .../tirx/structural_map_with_constraint_context.rs |   3 +-
 .../structural_mutate_with_constraint_context.rs   |   5 +-
 .../structural_visit_with_constraint_context.rs    |   4 +-
 .../structural_walk_with_constraint_context.rs     |   5 +-
 48 files changed, 358 insertions(+), 616 deletions(-)

diff --git a/3rdparty/tvm-ffi b/3rdparty/tvm-ffi
index ee81cc80f..3718ae5bf 160000
--- a/3rdparty/tvm-ffi
+++ b/3rdparty/tvm-ffi
@@ -1 +1 @@
-Subproject commit ee81cc80f609c4fc83c5b69cd0120e462d987706
+Subproject commit 3718ae5bfae64fd38e1258f8c7305bde827a7e43
diff --git a/examples/ir_guides/rust/src/lib.rs 
b/examples/ir_guides/rust/src/lib.rs
index 91413a0a6..39cab8495 100644
--- a/examples/ir_guides/rust/src/lib.rs
+++ b/examples/ir_guides/rust/src/lib.rs
@@ -33,10 +33,12 @@ use tvm_tirx::sym::{Analyzer, ConstraintContext, 
ProofStrength};
 use 
tvm_tirx::tirx::structural_map_with_constraint_context::structural_map_with_constraint_context;
 use 
tvm_tirx::tirx::structural_walk_with_constraint_context::structural_walk_with_constraint_context;
 use tvm_tirx::tirx::Function;
+use tvm_tirx::tvm_ffi::extra::structural_mutate::StructuralMutatorView;
+use tvm_tirx::tvm_ffi::extra::structural_visit::{DefRegionKind, 
StructuralVisitorView};
+use tvm_tirx::tvm_ffi::object::ObjectIdentity;
 use tvm_tirx::tvm_ffi::{
-    self, dispatch, match_any, Any, AnyView, Array, DefRegionKind, 
MutationView, ObjectIdentity,
-    ObjectRefCore, Result, StructuralMutatorView, StructuralVisitorView, 
Unchanged, UnchangedOr,
-    VisitInterrupt, WalkOrder, WalkResult,
+    self, dispatch, match_any, Any, AnyView, Array, MutationView, 
ObjectRefCore, Result, Unchanged,
+    UnchangedOr, VisitInterrupt, WalkOrder, WalkResult,
 };
 
 /// A Python list of strings.
diff --git a/rust/src/backend/cuda/op.rs b/rust/src/backend/cuda/op.rs
index 7c329abb8..68caa5c1a 100644
--- a/rust/src/backend/cuda/op.rs
+++ b/rust/src/backend/cuda/op.rs
@@ -57,6 +57,7 @@ pub struct TCGen05InstrDescriptorAttrs {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "tirx.cuda.TCGen05InstrDescriptorAttrs"]
 #[type_final]
+#[type_immutable]
 pub struct TCGen05InstrDescriptorAttrsObj {
     base_: AttrsObj,
     pub d_dtype: String,
@@ -162,9 +163,7 @@ impl TCGen05InstrDescriptorAttrs {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for 
TCGen05InstrDescriptorAttrsObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(TCGen05InstrDescriptorAttrs => Attrs);
+tvm_ffi::impl_object_ref_upcast!(TCGen05InstrDescriptorAttrs => Attrs);
 // tvm-ffi-stubgen(end)
 
 /// The static options of a block-scaled `tcgen05` instruction descriptor, 
which
@@ -188,6 +187,7 @@ pub struct TCGen05InstrDescriptorBlockScaledAttrs {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "tirx.cuda.TCGen05InstrDescriptorBlockScaledAttrs"]
 #[type_final]
+#[type_immutable]
 pub struct TCGen05InstrDescriptorBlockScaledAttrsObj {
     base_: AttrsObj,
     pub d_dtype: String,
@@ -298,9 +298,7 @@ impl TCGen05InstrDescriptorBlockScaledAttrs {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for 
TCGen05InstrDescriptorBlockScaledAttrsObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(TCGen05InstrDescriptorBlockScaledAttrs 
=> Attrs);
+tvm_ffi::impl_object_ref_upcast!(TCGen05InstrDescriptorBlockScaledAttrs => 
Attrs);
 // tvm-ffi-stubgen(end)
 
 /// The fixed options of a `tirx.cuda.tensormap_encode_tiled` call, which 
encodes a tiled tensor map
@@ -322,6 +320,7 @@ pub struct TensorMapEncodeTiledAttr {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "tirx.cuda.TensorMapEncodeTiledAttr"]
 #[type_final]
+#[type_immutable]
 pub struct TensorMapEncodeTiledAttrObj {
     base_: AttrsObj,
     pub descriptor_dtype: DLDataType,
@@ -395,7 +394,5 @@ impl TensorMapEncodeTiledAttr {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for TensorMapEncodeTiledAttrObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(TensorMapEncodeTiledAttr => Attrs);
+tvm_ffi::impl_object_ref_upcast!(TensorMapEncodeTiledAttr => Attrs);
 // tvm-ffi-stubgen(end)
diff --git a/rust/src/ir/attrs.rs b/rust/src/ir/attrs.rs
index a48dc17b1..ba12dd560 100644
--- a/rust/src/ir/attrs.rs
+++ b/rust/src/ir/attrs.rs
@@ -26,14 +26,11 @@
 
 // tvm-ffi-stubgen(begin): import-section
 use std::ops::Deref;
-use std::ptr::addr_of_mut;
 use tvm_ffi::Any;
 use tvm_ffi::Map;
 use tvm_ffi::Object;
 use tvm_ffi::ObjectArc;
 use tvm_ffi::String;
-use tvm_ffi::reflection::ShallowCopy;
-use tvm_ffi::reflection::UnsafeInit;
 // tvm-ffi-stubgen(end)
 
 /// The base of every attribute object, the metadata attached to a call, a 
function or a module.
@@ -52,6 +49,8 @@ pub struct Attrs {
 #[repr(C)]
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.Attrs"]
+#[type_immutable]
+#[type_rs_parent]
 pub struct AttrsObj {
     base_: Object,
 }
@@ -77,22 +76,6 @@ impl Attrs {
         Self { data_: ObjectArc::new(obj) }
     }
 }
-
-impl ShallowCopy for AttrsObj {
-    fn shallow_copy(&self) -> Self {
-        Self { base_: ShallowCopy::shallow_copy(&self.base_) }
-    }
-}
-
-unsafe impl UnsafeInit for AttrsObj {
-    unsafe fn drop_defined_fields(this: *mut Self) {
-        unsafe {
-            UnsafeInit::drop_defined_fields(addr_of_mut!((*this).base_));
-        }
-    }
-}
-
-unsafe impl tvm_ffi::object::ImmutableObject for AttrsObj {}
 // tvm-ffi-stubgen(end)
 
 /// Attributes as a map `dict` from names to values, such as the attributes of 
a function or a
@@ -128,6 +111,7 @@ pub struct DictAttrs {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.DictAttrs"]
 #[type_final]
+#[type_immutable]
 pub struct DictAttrsObj {
     base_: AttrsObj,
     pub dict: Map<String, Any>,
@@ -162,7 +146,5 @@ impl DictAttrs {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for DictAttrsObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(DictAttrs => Attrs);
+tvm_ffi::impl_object_ref_upcast!(DictAttrs => Attrs);
 // tvm-ffi-stubgen(end)
diff --git a/rust/src/ir/base.rs b/rust/src/ir/base.rs
index 79f78d8a0..3e2a0e61b 100644
--- a/rust/src/ir/base.rs
+++ b/rust/src/ir/base.rs
@@ -68,6 +68,7 @@ pub struct EnvFunc {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.EnvFunc"]
 #[type_final]
+#[type_immutable]
 pub struct EnvFuncObj {
     base_: Object,
     pub name: String,
@@ -95,8 +96,6 @@ impl EnvFunc {
         Self { data_: ObjectArc::new(obj) }
     }
 }
-
-unsafe impl tvm_ffi::object::ImmutableObject for EnvFuncObj {}
 // tvm-ffi-stubgen(end)
 
 impl EnvFunc {
diff --git a/rust/src/ir/expr.rs b/rust/src/ir/expr.rs
index 6875110ac..d1eb86355 100644
--- a/rust/src/ir/expr.rs
+++ b/rust/src/ir/expr.rs
@@ -51,6 +51,7 @@
 // A range is bounded by `PrimExpr`s, as C++ `RangeObj` holds `PrimExpr begin, 
extent`.
 // tvm-ffi-stubgen(field): ir.Range.begin -> PrimExpr
 // tvm-ffi-stubgen(field): ir.Range.extent -> PrimExpr
+// tvm-ffi-stubgen(field): ir.TupleGetItem.index -> i32
 // Names the hand-written wrappers use: the import section keeps them even 
when no generated block
 // does.
 // tvm-ffi-stubgen(import-object): crate::ir::prim::op::ExprResult
@@ -100,7 +101,6 @@ use crate::ir::r#type::Type;
 use crate::ir::support;
 use std::borrow::Borrow;
 use std::ops::Deref;
-use std::ptr::addr_of_mut;
 use std::sync::LazyLock;
 use tvm_ffi::Any;
 use tvm_ffi::AnyView;
@@ -115,10 +115,7 @@ use tvm_ffi::ObjectRefCore;
 use tvm_ffi::Result;
 use tvm_ffi::String;
 use tvm_ffi::reflection::NativeFunction;
-use tvm_ffi::reflection::ShallowCopy;
 use tvm_ffi::reflection::TypeAttrColumn;
-use tvm_ffi::reflection::UnsafeInit;
-use tvm_ffi::reflection::drop_if_defined;
 use tvm_ffi::tvm_ffi_sys::TVMFFITypeIndex;
 // tvm-ffi-stubgen(end)
 
@@ -143,6 +140,8 @@ pub struct Expr {
 #[repr(C)]
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.Expr"]
+#[type_immutable]
+#[type_rs_parent]
 pub struct ExprObj {
     base_: Object,
     pub loc: Location,
@@ -170,28 +169,6 @@ impl Expr {
         Self { data_: ObjectArc::new(obj) }
     }
 }
-
-impl ShallowCopy for ExprObj {
-    fn shallow_copy(&self) -> Self {
-        Self {
-            base_: ShallowCopy::shallow_copy(&self.base_),
-            loc: Clone::clone(&self.loc),
-            ty: Clone::clone(&self.ty),
-        }
-    }
-}
-
-unsafe impl UnsafeInit for ExprObj {
-    unsafe fn drop_defined_fields(this: *mut Self) {
-        unsafe {
-            UnsafeInit::drop_defined_fields(addr_of_mut!((*this).base_));
-            drop_if_defined(addr_of_mut!((*this).loc));
-            drop_if_defined(addr_of_mut!((*this).ty));
-        }
-    }
-}
-
-unsafe impl tvm_ffi::object::ImmutableObject for ExprObj {}
 // tvm-ffi-stubgen(end)
 
 impl Expr {
@@ -485,7 +462,7 @@ impl PrimExpr {
 /// ```
 #[repr(transparent)]
 #[derive(Clone, tvm_ffi::derive::ObjectRef)]
-#[object_ref(parent = PrimExpr, skip_any_compatible)]
+#[object_ref(unsafe parent = PrimExpr, skip_any_compatible)]
 pub struct IntExpr {
     data_: ObjectArc<ExprObj>,
 }
@@ -525,7 +502,7 @@ impl IntExpr {
     }
 }
 
-tvm_ffi::unsafe_impl_object_ref_upcast!(IntExpr => Expr);
+tvm_ffi::impl_object_ref_upcast!(IntExpr => Expr);
 
 /// The built expression, checked to be an integer, or the first error raised 
while building it.
 impl TryFrom<ExprResult> for IntExpr {
@@ -636,6 +613,8 @@ pub struct StagingExpr {
 #[repr(C)]
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.StagingExpr"]
+#[type_immutable]
+#[type_rs_parent]
 pub struct StagingExprObj {
     base_: ExprObj,
 }
@@ -669,23 +648,7 @@ impl StagingExpr {
     }
 }
 
-impl ShallowCopy for StagingExprObj {
-    fn shallow_copy(&self) -> Self {
-        Self { base_: ShallowCopy::shallow_copy(&self.base_) }
-    }
-}
-
-unsafe impl UnsafeInit for StagingExprObj {
-    unsafe fn drop_defined_fields(this: *mut Self) {
-        unsafe {
-            UnsafeInit::drop_defined_fields(addr_of_mut!((*this).base_));
-        }
-    }
-}
-
-unsafe impl tvm_ffi::object::ImmutableObject for StagingExprObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(StagingExpr => Expr);
+tvm_ffi::impl_object_ref_upcast!(StagingExpr => Expr);
 // tvm-ffi-stubgen(end)
 
 /// The base of opaque construction-time expressions, which generic expression 
visitors and
@@ -706,6 +669,8 @@ pub struct OpaqueExpr {
 #[repr(C)]
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.OpaqueExpr"]
+#[type_immutable]
+#[type_rs_parent]
 pub struct OpaqueExprObj {
     base_: ExprObj,
 }
@@ -739,23 +704,7 @@ impl OpaqueExpr {
     }
 }
 
-impl ShallowCopy for OpaqueExprObj {
-    fn shallow_copy(&self) -> Self {
-        Self { base_: ShallowCopy::shallow_copy(&self.base_) }
-    }
-}
-
-unsafe impl UnsafeInit for OpaqueExprObj {
-    unsafe fn drop_defined_fields(this: *mut Self) {
-        unsafe {
-            UnsafeInit::drop_defined_fields(addr_of_mut!((*this).base_));
-        }
-    }
-}
-
-unsafe impl tvm_ffi::object::ImmutableObject for OpaqueExprObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(OpaqueExpr => Expr);
+tvm_ffi::impl_object_ref_upcast!(OpaqueExpr => Expr);
 // tvm-ffi-stubgen(end)
 
 /// A global variable: a reference to a function of the module, by which 
functions call each
@@ -782,6 +731,7 @@ pub struct GlobalVar {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.GlobalVar"]
 #[type_final]
+#[type_immutable]
 pub struct GlobalVarObj {
     base_: ExprObj,
     pub name_hint: String,
@@ -824,9 +774,7 @@ impl GlobalVar {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for GlobalVarObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(GlobalVar => Expr);
+tvm_ffi::impl_object_ref_upcast!(GlobalVar => Expr);
 // tvm-ffi-stubgen(end)
 
 impl GlobalVar {
@@ -858,6 +806,7 @@ pub struct Tuple {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.Tuple"]
 #[type_final]
+#[type_immutable]
 pub struct TupleObj {
     base_: ExprObj,
     pub fields: Array<Expr>,
@@ -897,9 +846,7 @@ impl Tuple {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for TupleObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(Tuple => Expr);
+tvm_ffi::impl_object_ref_upcast!(Tuple => Expr);
 // tvm-ffi-stubgen(end)
 
 impl Tuple {
@@ -940,6 +887,7 @@ pub struct TupleGetItem {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.TupleGetItem"]
 #[type_final]
+#[type_immutable]
 pub struct TupleGetItemObj {
     base_: ExprObj,
     pub tuple_value: Expr,
@@ -985,9 +933,7 @@ impl TupleGetItem {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for TupleGetItemObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(TupleGetItem => Expr);
+tvm_ffi::impl_object_ref_upcast!(TupleGetItem => Expr);
 // tvm-ffi-stubgen(end)
 
 impl TupleGetItem {
@@ -1066,6 +1012,7 @@ pub struct TensorLoad {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.TensorLoad"]
 #[type_final]
+#[type_immutable]
 pub struct TensorLoadObj {
     base_: ExprObj,
     pub source: Expr,
@@ -1111,9 +1058,8 @@ impl TensorLoad {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for TensorLoadObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(TensorLoad => Expr, TensorLoad => 
PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(TensorLoad => Expr);
+tvm_ffi::impl_object_ref_upcast!(unsafe TensorLoad => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 impl TensorLoad {
@@ -1131,7 +1077,7 @@ impl TensorLoad {
     ) -> Result<Self> {
         // The native library registers the column when it loads, so one 
lookup suffices.
         static INFER_TYPE: LazyLock<Option<TypeAttrColumn>> =
-            LazyLock::new(|| 
TypeAttrColumn::new("__tvm_tensor_load_infer_type__"));
+            LazyLock::new(|| 
TypeAttrColumn::lookup("__tvm_tensor_load_infer_type__"));
         let (source, loc) = (source.into_owned_expr()?, 
loc.into_owned_object_ref());
         let indices = indices
             .into_iter()
@@ -1175,6 +1121,8 @@ pub struct Constant {
 #[repr(C)]
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.Constant"]
+#[type_immutable]
+#[type_rs_parent]
 pub struct ConstantObj {
     base_: ExprObj,
 }
@@ -1208,23 +1156,7 @@ impl Constant {
     }
 }
 
-impl ShallowCopy for ConstantObj {
-    fn shallow_copy(&self) -> Self {
-        Self { base_: ShallowCopy::shallow_copy(&self.base_) }
-    }
-}
-
-unsafe impl UnsafeInit for ConstantObj {
-    unsafe fn drop_defined_fields(this: *mut Self) {
-        unsafe {
-            UnsafeInit::drop_defined_fields(addr_of_mut!((*this).base_));
-        }
-    }
-}
-
-unsafe impl tvm_ffi::object::ImmutableObject for ConstantObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(Constant => Expr);
+tvm_ffi::impl_object_ref_upcast!(Constant => Expr);
 // tvm-ffi-stubgen(end)
 
 /// A floating-point constant of a scalar floating-point type, its value held 
as an `f64`.
@@ -1245,6 +1177,7 @@ pub struct FloatImm {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.FloatImm"]
 #[type_final]
+#[type_immutable]
 pub struct FloatImmObj {
     base_: ConstantObj,
     pub value: f64,
@@ -1283,9 +1216,8 @@ impl FloatImm {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for FloatImmObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(FloatImm => Expr, FloatImm => 
Constant, FloatImm => PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(FloatImm => Expr, FloatImm => Constant);
+tvm_ffi::impl_object_ref_upcast!(unsafe FloatImm => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 impl FloatImm {
@@ -1363,6 +1295,7 @@ pub struct IntImm {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.IntImm"]
 #[type_final]
+#[type_immutable]
 pub struct IntImmObj {
     base_: ConstantObj,
     pub value: BigInt,
@@ -1402,9 +1335,8 @@ impl IntImm {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for IntImmObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(IntImm => Expr, IntImm => Constant, 
IntImm => PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(IntImm => Expr, IntImm => Constant);
+tvm_ffi::impl_object_ref_upcast!(unsafe IntImm => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 impl IntImm {
@@ -1497,6 +1429,7 @@ pub struct GenericConst {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.GenericConst"]
 #[type_final]
+#[type_immutable]
 pub struct GenericConstObj {
     base_: ConstantObj,
     pub value: Any,
@@ -1539,9 +1472,7 @@ impl GenericConst {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for GenericConstObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(GenericConst => Expr, GenericConst => 
Constant);
+tvm_ffi::impl_object_ref_upcast!(GenericConst => Expr, GenericConst => 
Constant);
 // tvm-ffi-stubgen(end)
 
 impl GenericConst {
@@ -1597,6 +1528,7 @@ pub struct DataTypeImm {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.DataTypeImm"]
 #[type_final]
+#[type_immutable]
 pub struct DataTypeImmObj {
     base_: ConstantObj,
     pub value: DLDataType,
@@ -1639,9 +1571,7 @@ impl DataTypeImm {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for DataTypeImmObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(DataTypeImm => Expr, DataTypeImm => 
Constant);
+tvm_ffi::impl_object_ref_upcast!(DataTypeImm => Expr, DataTypeImm => Constant);
 // tvm-ffi-stubgen(end)
 
 impl DataTypeImm {
@@ -1670,6 +1600,7 @@ pub struct StringImm {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.StringImm"]
 #[type_final]
+#[type_immutable]
 pub struct StringImmObj {
     base_: ConstantObj,
     pub value: String,
@@ -1712,9 +1643,7 @@ impl StringImm {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for StringImmObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(StringImm => Expr, StringImm => 
Constant);
+tvm_ffi::impl_object_ref_upcast!(StringImm => Expr, StringImm => Constant);
 // tvm-ffi-stubgen(end)
 
 impl StringImm {
@@ -1747,6 +1676,7 @@ pub struct Call {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.Call"]
 #[type_final]
+#[type_immutable]
 pub struct CallObj {
     base_: ExprObj,
     pub op: Expr,
@@ -1805,9 +1735,7 @@ impl Call {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for CallObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(Call => Expr);
+tvm_ffi::impl_object_ref_upcast!(Call => Expr);
 // tvm-ffi-stubgen(end)
 
 impl Call {
@@ -1855,7 +1783,7 @@ impl Call {
     /// Returns the error the validator raises.
     pub fn validate(&self) -> Result<()> {
         let Some(op) = self.op.downcast_ref::<OpObj>() else { return Ok(()) };
-        if op.validator().type_index() == TVMFFITypeIndex::kTVMFFINone as i32 {
+        if op.validator().is_none() {
             return Ok(());
         }
         // SAFETY: an operator validator is a `void(const CallObj*)` hook.
@@ -1908,6 +1836,7 @@ pub struct TensorRegion {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.TensorRegion"]
 #[type_final]
+#[type_immutable]
 pub struct TensorRegionObj {
     base_: ExprObj,
     pub source: Expr,
@@ -1953,9 +1882,7 @@ impl TensorRegion {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for TensorRegionObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(TensorRegion => Expr);
+tvm_ffi::impl_object_ref_upcast!(TensorRegion => Expr);
 // tvm-ffi-stubgen(end)
 
 impl TensorRegion {
@@ -1991,6 +1918,8 @@ pub struct Var {
 #[repr(C)]
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.Var"]
+#[type_immutable]
+#[type_rs_parent]
 pub struct VarObj {
     base_: ExprObj,
     pub name: String,
@@ -2029,24 +1958,7 @@ impl Var {
     }
 }
 
-impl ShallowCopy for VarObj {
-    fn shallow_copy(&self) -> Self {
-        Self { base_: ShallowCopy::shallow_copy(&self.base_), name: 
Clone::clone(&self.name) }
-    }
-}
-
-unsafe impl UnsafeInit for VarObj {
-    unsafe fn drop_defined_fields(this: *mut Self) {
-        unsafe {
-            UnsafeInit::drop_defined_fields(addr_of_mut!((*this).base_));
-            drop_if_defined(addr_of_mut!((*this).name));
-        }
-    }
-}
-
-unsafe impl tvm_ffi::object::ImmutableObject for VarObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(Var => Expr);
+tvm_ffi::impl_object_ref_upcast!(Var => Expr);
 // tvm-ffi-stubgen(end)
 
 impl Var {
@@ -2126,7 +2038,8 @@ impl PrimVar {
     }
 }
 
-tvm_ffi::unsafe_impl_object_ref_upcast!(PrimVar => Expr, PrimVar => PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(PrimVar => Expr);
+tvm_ffi::impl_object_ref_upcast!(unsafe PrimVar => PrimExpr);
 
 impl PrimVar {
     /// Create the variable `name` of the primitive type `ty`.
@@ -2164,6 +2077,7 @@ pub struct LambdaExpr {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.LambdaExpr"]
 #[type_final]
+#[type_immutable]
 pub struct LambdaExprObj {
     base_: StagingExprObj,
     pub vars: Array<Var>,
@@ -2209,9 +2123,7 @@ impl LambdaExpr {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for LambdaExprObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(LambdaExpr => Expr, LambdaExpr => 
StagingExpr);
+tvm_ffi::impl_object_ref_upcast!(LambdaExpr => Expr, LambdaExpr => 
StagingExpr);
 // tvm-ffi-stubgen(end)
 
 impl LambdaExpr {
@@ -2279,6 +2191,7 @@ pub struct Range {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.Range"]
 #[type_final]
+#[type_immutable]
 pub struct RangeObj {
     base_: Object,
     pub begin: PrimExpr,
@@ -2315,8 +2228,6 @@ impl Range {
         Self { data_: ObjectArc::new(obj) }
     }
 }
-
-unsafe impl tvm_ffi::object::ImmutableObject for RangeObj {}
 // tvm-ffi-stubgen(end)
 
 impl Range {
diff --git a/rust/src/ir/function.rs b/rust/src/ir/function.rs
index 4eaf9a069..4cf5f488d 100644
--- a/rust/src/ir/function.rs
+++ b/rust/src/ir/function.rs
@@ -26,7 +26,7 @@
 // does.
 // tvm-ffi-stubgen(import-object): tvm_ffi::Any
 // tvm-ffi-stubgen(import-object): tvm_ffi::Map
-// tvm-ffi-stubgen(import-object): tvm_ffi::RValueRef
+// tvm-ffi-stubgen(import-object): tvm_ffi::rvalue_ref::RValueRef
 // tvm-ffi-stubgen(import-object): tvm_ffi::Result
 // tvm-ffi-stubgen(import-object): tvm_ffi::String
 
@@ -37,17 +37,13 @@ use crate::ir::expr::ExprObj;
 use crate::ir::location::Location;
 use crate::ir::r#type::Type;
 use std::ops::Deref;
-use std::ptr::addr_of_mut;
 use tvm_ffi::Any;
 use tvm_ffi::IntoObjectRef;
 use tvm_ffi::Map;
 use tvm_ffi::ObjectArc;
-use tvm_ffi::RValueRef;
 use tvm_ffi::Result;
 use tvm_ffi::String;
-use tvm_ffi::reflection::ShallowCopy;
-use tvm_ffi::reflection::UnsafeInit;
-use tvm_ffi::reflection::drop_if_defined;
+use tvm_ffi::rvalue_ref::RValueRef;
 // tvm-ffi-stubgen(end)
 
 /// The base of every function: an expression, typed by the function's type, 
with the function
@@ -86,6 +82,8 @@ pub struct BaseFunc {
 #[repr(C)]
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.BaseFunc"]
+#[type_immutable]
+#[type_rs_parent]
 pub struct BaseFuncObj {
     base_: ExprObj,
     pub attrs: DictAttrs,
@@ -128,24 +126,7 @@ impl BaseFunc {
     }
 }
 
-impl ShallowCopy for BaseFuncObj {
-    fn shallow_copy(&self) -> Self {
-        Self { base_: ShallowCopy::shallow_copy(&self.base_), attrs: 
Clone::clone(&self.attrs) }
-    }
-}
-
-unsafe impl UnsafeInit for BaseFuncObj {
-    unsafe fn drop_defined_fields(this: *mut Self) {
-        unsafe {
-            UnsafeInit::drop_defined_fields(addr_of_mut!((*this).base_));
-            drop_if_defined(addr_of_mut!((*this).attrs));
-        }
-    }
-}
-
-unsafe impl tvm_ffi::object::ImmutableObject for BaseFuncObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(BaseFunc => Expr);
+tvm_ffi::impl_object_ref_upcast!(BaseFunc => Expr);
 // tvm-ffi-stubgen(end)
 
 impl BaseFunc {
diff --git a/rust/src/ir/global_info.rs b/rust/src/ir/global_info.rs
index 85d10c816..8d523f78e 100644
--- a/rust/src/ir/global_info.rs
+++ b/rust/src/ir/global_info.rs
@@ -25,11 +25,8 @@
 // tvm-ffi-stubgen(missing-docs): allow
 
 // tvm-ffi-stubgen(begin): import-section
-use std::ptr::addr_of_mut;
 use tvm_ffi::Object;
 use tvm_ffi::ObjectArc;
-use tvm_ffi::reflection::ShallowCopy;
-use tvm_ffi::reflection::UnsafeInit;
 // tvm-ffi-stubgen(end)
 
 /// The base of the module-level information an [`IRModule`](super::IRModule) 
keeps by name in
@@ -49,6 +46,8 @@ pub struct GlobalInfo {
 #[repr(C)]
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.GlobalInfo"]
+#[type_immutable]
+#[type_rs_parent]
 pub struct GlobalInfoObj {
     base_: Object,
 }
@@ -74,20 +73,4 @@ impl GlobalInfo {
         Self { data_: ObjectArc::new(obj) }
     }
 }
-
-impl ShallowCopy for GlobalInfoObj {
-    fn shallow_copy(&self) -> Self {
-        Self { base_: ShallowCopy::shallow_copy(&self.base_) }
-    }
-}
-
-unsafe impl UnsafeInit for GlobalInfoObj {
-    unsafe fn drop_defined_fields(this: *mut Self) {
-        unsafe {
-            UnsafeInit::drop_defined_fields(addr_of_mut!((*this).base_));
-        }
-    }
-}
-
-unsafe impl tvm_ffi::object::ImmutableObject for GlobalInfoObj {}
 // tvm-ffi-stubgen(end)
diff --git a/rust/src/ir/location.rs b/rust/src/ir/location.rs
index e489935f2..3a46d108a 100644
--- a/rust/src/ir/location.rs
+++ b/rust/src/ir/location.rs
@@ -27,23 +27,22 @@
 // C++ has one `UnknownLoc`, which `UnknownLoc::new` returns: `opaque` 
generates no allocator for
 // another one. It has no fields, so it loses no field access.
 // tvm-ffi-stubgen(opaque): ir.UnknownLoc
+// tvm-ffi-stubgen(field): ir.SourceLoc.start_line -> i32
+// tvm-ffi-stubgen(field): ir.SourceLoc.start_column -> i32
+// tvm-ffi-stubgen(field): ir.SourceLoc.end_line -> i32
+// tvm-ffi-stubgen(field): ir.SourceLoc.end_column -> i32
 // Names the hand-written wrappers use: the import section keeps them even 
when no generated block
 // does.
-// tvm-ffi-stubgen(import-object): tvm_ffi::Result
 // tvm-ffi-stubgen(import-object): crate::ir::support
 // tvm-ffi-stubgen(import-object): tvm_ffi::IntoObjectRef
 
 // tvm-ffi-stubgen(begin): import-section
 use crate::ir::support;
 use std::ops::Deref;
-use std::ptr::addr_of_mut;
 use tvm_ffi::IntoObjectRef;
 use tvm_ffi::Object;
 use tvm_ffi::ObjectArc;
-use tvm_ffi::Result;
 use tvm_ffi::String;
-use tvm_ffi::reflection::ShallowCopy;
-use tvm_ffi::reflection::UnsafeInit;
 // tvm-ffi-stubgen(end)
 
 /// Where an IR node comes from, for diagnostics: an [`UnknownLoc`], a 
[`SourceLoc`] or a
@@ -78,6 +77,8 @@ pub struct Location {
 #[repr(C)]
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.Location"]
+#[type_immutable]
+#[type_rs_parent]
 pub struct LocationObj {
     base_: Object,
 }
@@ -103,22 +104,6 @@ impl Location {
         Self { data_: ObjectArc::new(obj) }
     }
 }
-
-impl ShallowCopy for LocationObj {
-    fn shallow_copy(&self) -> Self {
-        Self { base_: ShallowCopy::shallow_copy(&self.base_) }
-    }
-}
-
-unsafe impl UnsafeInit for LocationObj {
-    unsafe fn drop_defined_fields(this: *mut Self) {
-        unsafe {
-            UnsafeInit::drop_defined_fields(addr_of_mut!((*this).base_));
-        }
-    }
-}
-
-unsafe impl tvm_ffi::object::ImmutableObject for LocationObj {}
 // tvm-ffi-stubgen(end)
 
 /// The location of a node with no source information, a single shared object.
@@ -138,6 +123,7 @@ pub struct UnknownLoc {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.UnknownLoc"]
 #[type_final]
+#[type_immutable]
 pub struct UnknownLocObj {
     base_: LocationObj,
 }
@@ -149,9 +135,7 @@ impl Deref for UnknownLocObj {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for UnknownLocObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(UnknownLoc => Location);
+tvm_ffi::impl_object_ref_upcast!(UnknownLoc => Location);
 // tvm-ffi-stubgen(end)
 
 impl UnknownLoc {
@@ -209,6 +193,7 @@ pub struct SourceLoc {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.SourceLoc"]
 #[type_final]
+#[type_immutable]
 pub struct SourceLocObj {
     base_: LocationObj,
     pub source_name: SourceName,
@@ -265,9 +250,7 @@ impl SourceLoc {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for SourceLocObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(SourceLoc => Location);
+tvm_ffi::impl_object_ref_upcast!(SourceLoc => Location);
 // tvm-ffi-stubgen(end)
 
 // `ir.SourceLoc` stores its arguments as given: the generated 
`SourceLoc::new` is the same.
@@ -290,6 +273,7 @@ pub struct CallSiteLoc {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.CallSiteLoc"]
 #[type_final]
+#[type_immutable]
 pub struct CallSiteLocObj {
     base_: LocationObj,
     pub callee: Location,
@@ -326,9 +310,7 @@ impl CallSiteLoc {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for CallSiteLocObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(CallSiteLoc => Location);
+tvm_ffi::impl_object_ref_upcast!(CallSiteLoc => Location);
 // tvm-ffi-stubgen(end)
 
 // `ir.CallSiteLoc` stores its arguments as given: the generated 
`CallSiteLoc::new` is the same.
@@ -351,6 +333,7 @@ pub struct SourceName {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.SourceName"]
 #[type_final]
+#[type_immutable]
 pub struct SourceNameObj {
     base_: Object,
     pub name: String,
@@ -377,8 +360,6 @@ impl SourceName {
         Self { data_: ObjectArc::new(obj) }
     }
 }
-
-unsafe impl tvm_ffi::object::ImmutableObject for SourceNameObj {}
 // tvm-ffi-stubgen(end)
 
 impl SourceName {
diff --git a/rust/src/ir/module.rs b/rust/src/ir/module.rs
index 949d97a2e..ddd850aee 100644
--- a/rust/src/ir/module.rs
+++ b/rust/src/ir/module.rs
@@ -30,7 +30,7 @@
 // Names the hand-written wrappers use: the import section keeps them even 
when no generated block
 // does.
 // tvm-ffi-stubgen(import-object): tvm_ffi::Any
-// tvm-ffi-stubgen(import-object): tvm_ffi::RValueRef
+// tvm-ffi-stubgen(import-object): tvm_ffi::rvalue_ref::RValueRef
 // tvm-ffi-stubgen(import-object): tvm_ffi::Result
 // tvm-ffi-stubgen(import-object): crate::ir::support
 // tvm-ffi-stubgen(import-object): crate::ir::expr::IntoExpr
@@ -51,9 +51,9 @@ use tvm_ffi::IntoObjectRef;
 use tvm_ffi::Map;
 use tvm_ffi::Object;
 use tvm_ffi::ObjectArc;
-use tvm_ffi::RValueRef;
 use tvm_ffi::Result;
 use tvm_ffi::String;
+use tvm_ffi::rvalue_ref::RValueRef;
 // tvm-ffi-stubgen(end)
 
 /// A set of functions, each bound to a [`GlobalVar`] with a unique name, with 
the module
@@ -345,8 +345,7 @@ impl IRModule {
     pub fn get_attr(&self, key: &str) -> Result<Option<Any>> {
         let value = tvm_ffi::cached_global_func!("ir.IRModuleGetAttr")
             .call_tuple((self, String::from(key)))?;
-        let is_none = value.type_index() == tvm_ffi::TypeIndex::kTVMFFINone as 
i32;
-        Ok((!is_none).then_some(value))
+        Ok((!value.is_none()).then_some(value))
     }
 
     /// Return the module with the attribute `key` set to `value` 
(`ir.IRModuleWithAttr`).
diff --git a/rust/src/ir/op.rs b/rust/src/ir/op.rs
index aff9c1f64..ebe38dbe4 100644
--- a/rust/src/ir/op.rs
+++ b/rust/src/ir/op.rs
@@ -199,7 +199,7 @@ impl Op {
     pub(crate) fn attr_column<T>(name: &str) -> Result<Option<OpAttrMap<T>>> {
         let column =
             
tvm_ffi::cached_global_func!("ir.GetOpAttrColumn").call_tuple((String::from(name),))?;
-        if column.type_index() == 
tvm_ffi::tvm_ffi_sys::TVMFFITypeIndex::kTVMFFINone as i32 {
+        if column.is_none() {
             return Ok(None);
         }
         if column.type_index() != 
tvm_ffi::tvm_ffi_sys::TVMFFITypeIndex::kTVMFFIList as i32 {
@@ -257,7 +257,7 @@ impl Op {
     }
 }
 
-tvm_ffi::unsafe_impl_object_ref_upcast!(Op => Expr);
+tvm_ffi::impl_object_ref_upcast!(Op => Expr);
 
 /// The name and documentation of one argument of an [`Op`], as
 /// [`OpObj::args_info`] lists them.
@@ -277,6 +277,7 @@ pub struct ArgumentInfo {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.ArgumentInfo"]
 #[type_final]
+#[type_immutable]
 pub struct ArgumentInfoObj {
     base_: Object,
     pub name: String,
@@ -304,8 +305,6 @@ impl ArgumentInfo {
         Self { data_: ObjectArc::new(obj) }
     }
 }
-
-unsafe impl tvm_ffi::object::ImmutableObject for ArgumentInfoObj {}
 // tvm-ffi-stubgen(end)
 
 /// A live attribute column of the operator registry: the value of one 
attribute for every
@@ -323,8 +322,7 @@ impl<T> OpAttrMap<T> {
     /// The entry of `op` in the column, unless it has none.
     fn entry(&self, op: &OpObj) -> Option<Any> {
         let value = self.column.get(op.index as usize).ok()?;
-        (value.type_index() != 
tvm_ffi::tvm_ffi_sys::TVMFFITypeIndex::kTVMFFINone as i32)
-            .then_some(value)
+        (!value.is_none()).then_some(value)
     }
 
     /// The value `op` registers in this column, of whatever type, or `None`.
diff --git a/rust/src/ir/op_def.rs b/rust/src/ir/op_def.rs
index 544711ce5..f6cdb7922 100644
--- a/rust/src/ir/op_def.rs
+++ b/rust/src/ir/op_def.rs
@@ -25,7 +25,6 @@ use std::any::TypeId;
 use std::marker::PhantomData;
 
 use tvm_ffi::reflection::NativeFunction;
-use tvm_ffi::tvm_ffi_sys::TVMFFITypeIndex;
 use tvm_ffi::{Any, AnyView, Array, Error, ObjectRefCore, Result, String, 
TYPE_ERROR, VALUE_ERROR};
 
 use super::attrs::Attrs;
@@ -110,7 +109,8 @@ impl OpDef {
         let def = self.invoke("_set_signature", &args).then(|op| {
             if !op.validator_is_custom() {
                 let hook: NativeFunction<(&CallObj,)> = 
sig::validate::<S::Checks>.into();
-                op.write_validator(hook.into(), false);
+                // SAFETY: an operator validator is a `void(const CallObj*)` 
hook.
+                op.write_validator(unsafe { hook.into_any() }, false);
             }
             Ok(())
         });
@@ -172,7 +172,8 @@ impl OpDef {
         F: Fn(&CallObj) -> Result<Type> + Copy + 'static,
     {
         let hook: NativeFunction<(&CallObj,), Type> = infer.into();
-        self.attr("FInferType", hook.into(), false)
+        // SAFETY: `FInferType` holds `Type(const CallObj*)` hooks.
+        self.attr("FInferType", unsafe { hook.into_any() }, false)
     }
 
     /// Set a custom validator of the operator, `validate`, a function or a 
closure that captures
@@ -190,7 +191,8 @@ impl OpDef {
                 let message = format!("Validator of {} is already registered", 
op.name.as_str());
                 return Err(Error::new(VALUE_ERROR, &message, ""));
             }
-            op.write_validator(hook.into(), true);
+            // SAFETY: an operator validator is a `void(const CallObj*)` hook.
+            op.write_validator(unsafe { hook.into_any() }, true);
             Ok(())
         })
     }
@@ -206,7 +208,8 @@ impl OpDef {
         F: Fn(&CallObj) -> Result<Array<Var>> + Copy + 'static,
     {
         let hook: NativeFunction<(&CallObj,), Array<Var>> = body_params.into();
-        self.attr("FRegionGetBodyParams", hook.into(), false)
+        // SAFETY: `FRegionGetBodyParams` holds `Array<Var>(const CallObj*)` 
hooks.
+        self.attr("FRegionGetBodyParams", unsafe { hook.into_any() }, false)
     }
 
     /// Set the check of the operator's regions, its `FRegionValidate`, to 
`validate`, a function or
@@ -219,7 +222,8 @@ impl OpDef {
         F: Fn(&RegionStmtObj) -> Result<()> + Copy + 'static,
     {
         let hook: NativeFunction<(&RegionStmtObj,)> = validate.into();
-        self.attr("FRegionValidate", hook.into(), false)
+        // SAFETY: `FRegionValidate` holds `void(const RegionStmtObj*)` hooks.
+        self.attr("FRegionValidate", unsafe { hook.into_any() }, false)
     }
 
     fn attr(self, key: &str, value: Any, replace: bool) -> Self {
@@ -752,7 +756,7 @@ pub mod sig {
         }
         // Borrowed in place: indexing an `Array` bumps no reference count.
         let value = value();
-        if value.type_index() != TVMFFITypeIndex::kTVMFFINone as i32 && 
T::check(value) {
+        if !value.is_none() && T::check(value) {
             return Ok(());
         }
         let (fixed, tail) = match type_arg {
diff --git a/rust/src/ir/prim/expr.rs b/rust/src/ir/prim/expr.rs
index 777ba710e..8d025443a 100644
--- a/rust/src/ir/prim/expr.rs
+++ b/rust/src/ir/prim/expr.rs
@@ -236,6 +236,7 @@ pub struct Cast {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "prim.Cast"]
 #[type_final]
+#[type_immutable]
 pub struct CastObj {
     base_: ExprObj,
     pub value: PrimExpr,
@@ -278,9 +279,8 @@ impl Cast {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for CastObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(Cast => Expr, Cast => PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(Cast => Expr);
+tvm_ffi::impl_object_ref_upcast!(unsafe Cast => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 impl Cast {
@@ -325,6 +325,7 @@ pub struct LShift {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "prim.LShift"]
 #[type_final]
+#[type_immutable]
 pub struct LShiftObj {
     base_: ExprObj,
     pub a: PrimExpr,
@@ -370,9 +371,8 @@ impl LShift {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for LShiftObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(LShift => Expr, LShift => PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(LShift => Expr);
+tvm_ffi::impl_object_ref_upcast!(unsafe LShift => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 binary_new! {
@@ -397,6 +397,7 @@ pub struct RShift {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "prim.RShift"]
 #[type_final]
+#[type_immutable]
 pub struct RShiftObj {
     base_: ExprObj,
     pub a: PrimExpr,
@@ -442,9 +443,8 @@ impl RShift {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for RShiftObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(RShift => Expr, RShift => PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(RShift => Expr);
+tvm_ffi::impl_object_ref_upcast!(unsafe RShift => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 binary_new! {
@@ -469,6 +469,7 @@ pub struct BitwiseAnd {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "prim.BitwiseAnd"]
 #[type_final]
+#[type_immutable]
 pub struct BitwiseAndObj {
     base_: ExprObj,
     pub a: PrimExpr,
@@ -514,9 +515,8 @@ impl BitwiseAnd {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for BitwiseAndObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(BitwiseAnd => Expr, BitwiseAnd => 
PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(BitwiseAnd => Expr);
+tvm_ffi::impl_object_ref_upcast!(unsafe BitwiseAnd => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 binary_new! {
@@ -541,6 +541,7 @@ pub struct BitwiseOr {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "prim.BitwiseOr"]
 #[type_final]
+#[type_immutable]
 pub struct BitwiseOrObj {
     base_: ExprObj,
     pub a: PrimExpr,
@@ -586,9 +587,8 @@ impl BitwiseOr {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for BitwiseOrObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(BitwiseOr => Expr, BitwiseOr => 
PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(BitwiseOr => Expr);
+tvm_ffi::impl_object_ref_upcast!(unsafe BitwiseOr => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 binary_new! {
@@ -613,6 +613,7 @@ pub struct BitwiseXor {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "prim.BitwiseXor"]
 #[type_final]
+#[type_immutable]
 pub struct BitwiseXorObj {
     base_: ExprObj,
     pub a: PrimExpr,
@@ -658,9 +659,8 @@ impl BitwiseXor {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for BitwiseXorObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(BitwiseXor => Expr, BitwiseXor => 
PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(BitwiseXor => Expr);
+tvm_ffi::impl_object_ref_upcast!(unsafe BitwiseXor => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 binary_new! {
@@ -685,6 +685,7 @@ pub struct BitwiseNot {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "prim.BitwiseNot"]
 #[type_final]
+#[type_immutable]
 pub struct BitwiseNotObj {
     base_: ExprObj,
     pub a: PrimExpr,
@@ -727,9 +728,8 @@ impl BitwiseNot {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for BitwiseNotObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(BitwiseNot => Expr, BitwiseNot => 
PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(BitwiseNot => Expr);
+tvm_ffi::impl_object_ref_upcast!(unsafe BitwiseNot => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 impl BitwiseNot {
@@ -790,6 +790,7 @@ pub struct Add {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "prim.Add"]
 #[type_final]
+#[type_immutable]
 pub struct AddObj {
     base_: ExprObj,
     pub a: PrimExpr,
@@ -835,9 +836,8 @@ impl Add {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for AddObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(Add => Expr, Add => PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(Add => Expr);
+tvm_ffi::impl_object_ref_upcast!(unsafe Add => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 binary_new! {
@@ -862,6 +862,7 @@ pub struct Sub {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "prim.Sub"]
 #[type_final]
+#[type_immutable]
 pub struct SubObj {
     base_: ExprObj,
     pub a: PrimExpr,
@@ -907,9 +908,8 @@ impl Sub {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for SubObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(Sub => Expr, Sub => PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(Sub => Expr);
+tvm_ffi::impl_object_ref_upcast!(unsafe Sub => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 binary_new! {
@@ -934,6 +934,7 @@ pub struct Mul {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "prim.Mul"]
 #[type_final]
+#[type_immutable]
 pub struct MulObj {
     base_: ExprObj,
     pub a: PrimExpr,
@@ -979,9 +980,8 @@ impl Mul {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for MulObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(Mul => Expr, Mul => PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(Mul => Expr);
+tvm_ffi::impl_object_ref_upcast!(unsafe Mul => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 binary_new! {
@@ -1006,6 +1006,7 @@ pub struct Div {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "prim.Div"]
 #[type_final]
+#[type_immutable]
 pub struct DivObj {
     base_: ExprObj,
     pub a: PrimExpr,
@@ -1051,9 +1052,8 @@ impl Div {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for DivObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(Div => Expr, Div => PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(Div => Expr);
+tvm_ffi::impl_object_ref_upcast!(unsafe Div => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 binary_new! {
@@ -1078,6 +1078,7 @@ pub struct Mod {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "prim.Mod"]
 #[type_final]
+#[type_immutable]
 pub struct ModObj {
     base_: ExprObj,
     pub a: PrimExpr,
@@ -1123,9 +1124,8 @@ impl Mod {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for ModObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(Mod => Expr, Mod => PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(Mod => Expr);
+tvm_ffi::impl_object_ref_upcast!(unsafe Mod => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 binary_new! {
@@ -1150,6 +1150,7 @@ pub struct FloorDiv {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "prim.FloorDiv"]
 #[type_final]
+#[type_immutable]
 pub struct FloorDivObj {
     base_: ExprObj,
     pub a: PrimExpr,
@@ -1195,9 +1196,8 @@ impl FloorDiv {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for FloorDivObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(FloorDiv => Expr, FloorDiv => 
PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(FloorDiv => Expr);
+tvm_ffi::impl_object_ref_upcast!(unsafe FloorDiv => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 binary_new! {
@@ -1222,6 +1222,7 @@ pub struct FloorMod {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "prim.FloorMod"]
 #[type_final]
+#[type_immutable]
 pub struct FloorModObj {
     base_: ExprObj,
     pub a: PrimExpr,
@@ -1267,9 +1268,8 @@ impl FloorMod {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for FloorModObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(FloorMod => Expr, FloorMod => 
PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(FloorMod => Expr);
+tvm_ffi::impl_object_ref_upcast!(unsafe FloorMod => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 binary_new! {
@@ -1294,6 +1294,7 @@ pub struct Min {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "prim.Min"]
 #[type_final]
+#[type_immutable]
 pub struct MinObj {
     base_: ExprObj,
     pub a: PrimExpr,
@@ -1339,9 +1340,8 @@ impl Min {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for MinObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(Min => Expr, Min => PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(Min => Expr);
+tvm_ffi::impl_object_ref_upcast!(unsafe Min => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 binary_new! {
@@ -1366,6 +1366,7 @@ pub struct Max {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "prim.Max"]
 #[type_final]
+#[type_immutable]
 pub struct MaxObj {
     base_: ExprObj,
     pub a: PrimExpr,
@@ -1411,9 +1412,8 @@ impl Max {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for MaxObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(Max => Expr, Max => PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(Max => Expr);
+tvm_ffi::impl_object_ref_upcast!(unsafe Max => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 binary_new! {
@@ -1441,6 +1441,7 @@ pub struct EQ {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "prim.EQ"]
 #[type_final]
+#[type_immutable]
 pub struct EQObj {
     base_: ExprObj,
     pub a: PrimExpr,
@@ -1486,9 +1487,8 @@ impl EQ {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for EQObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(EQ => Expr, EQ => PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(EQ => Expr);
+tvm_ffi::impl_object_ref_upcast!(unsafe EQ => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 binary_new! {
@@ -1513,6 +1513,7 @@ pub struct NE {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "prim.NE"]
 #[type_final]
+#[type_immutable]
 pub struct NEObj {
     base_: ExprObj,
     pub a: PrimExpr,
@@ -1558,9 +1559,8 @@ impl NE {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for NEObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(NE => Expr, NE => PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(NE => Expr);
+tvm_ffi::impl_object_ref_upcast!(unsafe NE => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 binary_new! {
@@ -1585,6 +1585,7 @@ pub struct LT {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "prim.LT"]
 #[type_final]
+#[type_immutable]
 pub struct LTObj {
     base_: ExprObj,
     pub a: PrimExpr,
@@ -1630,9 +1631,8 @@ impl LT {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for LTObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(LT => Expr, LT => PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(LT => Expr);
+tvm_ffi::impl_object_ref_upcast!(unsafe LT => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 binary_new! {
@@ -1657,6 +1657,7 @@ pub struct LE {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "prim.LE"]
 #[type_final]
+#[type_immutable]
 pub struct LEObj {
     base_: ExprObj,
     pub a: PrimExpr,
@@ -1702,9 +1703,8 @@ impl LE {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for LEObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(LE => Expr, LE => PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(LE => Expr);
+tvm_ffi::impl_object_ref_upcast!(unsafe LE => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 binary_new! {
@@ -1729,6 +1729,7 @@ pub struct GT {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "prim.GT"]
 #[type_final]
+#[type_immutable]
 pub struct GTObj {
     base_: ExprObj,
     pub a: PrimExpr,
@@ -1774,9 +1775,8 @@ impl GT {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for GTObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(GT => Expr, GT => PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(GT => Expr);
+tvm_ffi::impl_object_ref_upcast!(unsafe GT => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 binary_new! {
@@ -1801,6 +1801,7 @@ pub struct GE {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "prim.GE"]
 #[type_final]
+#[type_immutable]
 pub struct GEObj {
     base_: ExprObj,
     pub a: PrimExpr,
@@ -1846,9 +1847,8 @@ impl GE {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for GEObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(GE => Expr, GE => PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(GE => Expr);
+tvm_ffi::impl_object_ref_upcast!(unsafe GE => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 binary_new! {
@@ -1876,6 +1876,7 @@ pub struct And {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "prim.And"]
 #[type_final]
+#[type_immutable]
 pub struct AndObj {
     base_: ExprObj,
     pub a: PrimExpr,
@@ -1921,9 +1922,8 @@ impl And {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for AndObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(And => Expr, And => PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(And => Expr);
+tvm_ffi::impl_object_ref_upcast!(unsafe And => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 impl And {
@@ -1961,6 +1961,7 @@ pub struct Or {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "prim.Or"]
 #[type_final]
+#[type_immutable]
 pub struct OrObj {
     base_: ExprObj,
     pub a: PrimExpr,
@@ -2006,9 +2007,8 @@ impl Or {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for OrObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(Or => Expr, Or => PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(Or => Expr);
+tvm_ffi::impl_object_ref_upcast!(unsafe Or => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 impl Or {
@@ -2046,6 +2046,7 @@ pub struct Not {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "prim.Not"]
 #[type_final]
+#[type_immutable]
 pub struct NotObj {
     base_: ExprObj,
     pub a: PrimExpr,
@@ -2088,9 +2089,8 @@ impl Not {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for NotObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(Not => Expr, Not => PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(Not => Expr);
+tvm_ffi::impl_object_ref_upcast!(unsafe Not => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 impl Not {
@@ -2133,6 +2133,7 @@ pub struct Select {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "prim.Select"]
 #[type_final]
+#[type_immutable]
 pub struct SelectObj {
     base_: ExprObj,
     pub condition: PrimExpr,
@@ -2187,9 +2188,8 @@ impl Select {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for SelectObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(Select => Expr, Select => PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(Select => Expr);
+tvm_ffi::impl_object_ref_upcast!(unsafe Select => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 impl Select {
@@ -2263,6 +2263,7 @@ pub struct Let {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "prim.Let"]
 #[type_final]
+#[type_immutable]
 pub struct LetObj {
     base_: ExprObj,
     pub var: Var,
@@ -2317,9 +2318,8 @@ impl Let {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for LetObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(Let => Expr, Let => PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(Let => Expr);
+tvm_ffi::impl_object_ref_upcast!(unsafe Let => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 impl Let {
diff --git a/rust/src/ir/prim/vector_expr.rs b/rust/src/ir/prim/vector_expr.rs
index 5194eb8d6..29c82565f 100644
--- a/rust/src/ir/prim/vector_expr.rs
+++ b/rust/src/ir/prim/vector_expr.rs
@@ -125,6 +125,7 @@ pub struct Ramp {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "prim.Ramp"]
 #[type_final]
+#[type_immutable]
 pub struct RampObj {
     base_: ExprObj,
     pub base: PrimExpr,
@@ -179,9 +180,8 @@ impl Ramp {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for RampObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(Ramp => Expr, Ramp => PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(Ramp => Expr);
+tvm_ffi::impl_object_ref_upcast!(unsafe Ramp => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 impl Ramp {
@@ -248,6 +248,7 @@ pub struct Broadcast {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "prim.Broadcast"]
 #[type_final]
+#[type_immutable]
 pub struct BroadcastObj {
     base_: ExprObj,
     pub value: PrimExpr,
@@ -293,9 +294,8 @@ impl Broadcast {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for BroadcastObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(Broadcast => Expr, Broadcast => 
PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(Broadcast => Expr);
+tvm_ffi::impl_object_ref_upcast!(unsafe Broadcast => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 impl Broadcast {
@@ -337,6 +337,7 @@ pub struct Shuffle {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "prim.Shuffle"]
 #[type_final]
+#[type_immutable]
 pub struct ShuffleObj {
     base_: ExprObj,
     pub vectors: Array<PrimExpr>,
@@ -387,9 +388,8 @@ impl Shuffle {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for ShuffleObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(Shuffle => Expr, Shuffle => PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(Shuffle => Expr);
+tvm_ffi::impl_object_ref_upcast!(unsafe Shuffle => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 impl Shuffle {
diff --git a/rust/src/ir/stmt.rs b/rust/src/ir/stmt.rs
index dde750ac4..5ad1d69f1 100644
--- a/rust/src/ir/stmt.rs
+++ b/rust/src/ir/stmt.rs
@@ -94,7 +94,6 @@ use crate::ir::r#type::MissingTypeObj;
 use crate::ir::r#type::PrimType;
 use crate::ir::support;
 use std::ops::Deref;
-use std::ptr::addr_of_mut;
 use std::sync::LazyLock;
 use tvm_ffi::Any;
 use tvm_ffi::AnyView;
@@ -111,10 +110,7 @@ use tvm_ffi::Result;
 use tvm_ffi::String;
 use tvm_ffi::VALUE_ERROR;
 use tvm_ffi::reflection::NativeFunction;
-use tvm_ffi::reflection::ShallowCopy;
 use tvm_ffi::reflection::TypeAttrColumn;
-use tvm_ffi::reflection::UnsafeInit;
-use tvm_ffi::reflection::drop_if_defined;
 // tvm-ffi-stubgen(end)
 
 /// The base of every statement: its source location `loc`.
@@ -162,6 +158,8 @@ pub struct Stmt {
 #[repr(C)]
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.Stmt"]
+#[type_immutable]
+#[type_rs_parent]
 pub struct StmtObj {
     base_: Object,
     pub loc: Location,
@@ -188,23 +186,6 @@ impl Stmt {
         Self { data_: ObjectArc::new(obj) }
     }
 }
-
-impl ShallowCopy for StmtObj {
-    fn shallow_copy(&self) -> Self {
-        Self { base_: ShallowCopy::shallow_copy(&self.base_), loc: 
Clone::clone(&self.loc) }
-    }
-}
-
-unsafe impl UnsafeInit for StmtObj {
-    unsafe fn drop_defined_fields(this: *mut Self) {
-        unsafe {
-            UnsafeInit::drop_defined_fields(addr_of_mut!((*this).base_));
-            drop_if_defined(addr_of_mut!((*this).loc));
-        }
-    }
-}
-
-unsafe impl tvm_ffi::object::ImmutableObject for StmtObj {}
 // tvm-ffi-stubgen(end)
 
 /// The binding of the variable `var` to `value`, visible to the statements 
after it in the
@@ -225,6 +206,7 @@ pub struct Bind {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.Bind"]
 #[type_final]
+#[type_immutable]
 pub struct BindObj {
     base_: StmtObj,
     pub var: Var,
@@ -268,9 +250,7 @@ impl Bind {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for BindObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(Bind => Stmt);
+tvm_ffi::impl_object_ref_upcast!(Bind => Stmt);
 // tvm-ffi-stubgen(end)
 
 impl Bind {
@@ -313,6 +293,7 @@ pub struct AssertStmt {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.AssertStmt"]
 #[type_final]
+#[type_immutable]
 pub struct AssertStmtObj {
     base_: StmtObj,
     pub condition: PrimExpr,
@@ -364,9 +345,7 @@ impl AssertStmt {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for AssertStmtObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(AssertStmt => Stmt);
+tvm_ffi::impl_object_ref_upcast!(AssertStmt => Stmt);
 // tvm-ffi-stubgen(end)
 
 impl AssertStmt {
@@ -451,6 +430,7 @@ impl TryFrom<i64> for ForKind {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.For"]
 #[type_final]
+#[type_immutable]
 pub struct ForObj {
     base_: StmtObj,
     pub var: PrimVar,
@@ -518,9 +498,7 @@ impl For {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for ForObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(For => Stmt);
+tvm_ffi::impl_object_ref_upcast!(For => Stmt);
 // tvm-ffi-stubgen(end)
 
 impl Default for ForKind {
@@ -651,6 +629,7 @@ pub struct While {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.While"]
 #[type_final]
+#[type_immutable]
 pub struct WhileObj {
     base_: StmtObj,
     pub condition: PrimExpr,
@@ -694,9 +673,7 @@ impl While {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for WhileObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(While => Stmt);
+tvm_ffi::impl_object_ref_upcast!(While => Stmt);
 // tvm-ffi-stubgen(end)
 
 impl While {
@@ -739,6 +716,7 @@ pub struct TensorStore {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.TensorStore"]
 #[type_final]
+#[type_immutable]
 pub struct TensorStoreObj {
     base_: StmtObj,
     pub dest: Expr,
@@ -790,9 +768,7 @@ impl TensorStore {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for TensorStoreObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(TensorStore => Stmt);
+tvm_ffi::impl_object_ref_upcast!(TensorStore => Stmt);
 // tvm-ffi-stubgen(end)
 
 impl TensorStore {
@@ -811,7 +787,7 @@ impl TensorStore {
     ) -> Result<Self> {
         // The native library registers the column when it loads, so one 
lookup suffices.
         static VALIDATE: LazyLock<Option<TypeAttrColumn>> =
-            LazyLock::new(|| 
TypeAttrColumn::new("__tvm_tensor_store_validate__"));
+            LazyLock::new(|| 
TypeAttrColumn::lookup("__tvm_tensor_store_validate__"));
         let (dest, value) = (dest.into_owned_expr()?, 
value.into_owned_expr()?);
         let indices = indices
             .into_iter()
@@ -859,6 +835,7 @@ pub struct RegionStmt {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.RegionStmt"]
 #[type_final]
+#[type_immutable]
 pub struct RegionStmtObj {
     base_: StmtObj,
     pub op: Op,
@@ -922,9 +899,7 @@ impl RegionStmt {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for RegionStmtObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(RegionStmt => Stmt);
+tvm_ffi::impl_object_ref_upcast!(RegionStmt => Stmt);
 // tvm-ffi-stubgen(end)
 
 impl RegionStmt {
@@ -1029,6 +1004,7 @@ pub struct SeqStmt {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.SeqStmt"]
 #[type_final]
+#[type_immutable]
 pub struct SeqStmtObj {
     base_: StmtObj,
     pub seq: Array<Stmt>,
@@ -1063,9 +1039,7 @@ impl SeqStmt {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for SeqStmtObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(SeqStmt => Stmt);
+tvm_ffi::impl_object_ref_upcast!(SeqStmt => Stmt);
 // tvm-ffi-stubgen(end)
 
 impl SeqStmt {
@@ -1099,6 +1073,7 @@ pub struct If {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.If"]
 #[type_final]
+#[type_immutable]
 pub struct IfObj {
     base_: StmtObj,
     pub condition: PrimExpr,
@@ -1150,9 +1125,7 @@ impl If {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for IfObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(If => Stmt);
+tvm_ffi::impl_object_ref_upcast!(If => Stmt);
 // tvm-ffi-stubgen(end)
 
 impl If {
@@ -1188,6 +1161,7 @@ pub struct Evaluate {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.Evaluate"]
 #[type_final]
+#[type_immutable]
 pub struct EvaluateObj {
     base_: StmtObj,
     pub value: Expr,
@@ -1222,9 +1196,7 @@ impl Evaluate {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for EvaluateObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(Evaluate => Stmt);
+tvm_ffi::impl_object_ref_upcast!(Evaluate => Stmt);
 // tvm-ffi-stubgen(end)
 
 impl Evaluate {
@@ -1251,6 +1223,7 @@ pub struct Break {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.Break"]
 #[type_final]
+#[type_immutable]
 pub struct BreakObj {
     base_: StmtObj,
 }
@@ -1284,9 +1257,7 @@ impl Break {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for BreakObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(Break => Stmt);
+tvm_ffi::impl_object_ref_upcast!(Break => Stmt);
 // tvm-ffi-stubgen(end)
 
 // `ir.Break` stores its location as given: the generated `Break::new` is the 
same.
@@ -1308,6 +1279,7 @@ pub struct Return {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.Return"]
 #[type_final]
+#[type_immutable]
 pub struct ReturnObj {
     base_: StmtObj,
     pub value: Expr,
@@ -1342,9 +1314,7 @@ impl Return {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for ReturnObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(Return => Stmt);
+tvm_ffi::impl_object_ref_upcast!(Return => Stmt);
 // tvm-ffi-stubgen(end)
 
 impl Return {
@@ -1371,6 +1341,7 @@ pub struct Continue {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.Continue"]
 #[type_final]
+#[type_immutable]
 pub struct ContinueObj {
     base_: StmtObj,
 }
@@ -1404,9 +1375,7 @@ impl Continue {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for ContinueObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(Continue => Stmt);
+tvm_ffi::impl_object_ref_upcast!(Continue => Stmt);
 // tvm-ffi-stubgen(end)
 
 // `ir.Continue` stores its location as given: the generated `Continue::new` 
is the same.
diff --git a/rust/src/ir/transform/core.rs b/rust/src/ir/transform/core.rs
index f69b2238e..4d7b63788 100644
--- a/rust/src/ir/transform/core.rs
+++ b/rust/src/ir/transform/core.rs
@@ -25,6 +25,8 @@
 // `custom-new` reserves `new` for the checked `transform.PassContext`; the 
generated `from_fields`
 // allocates the fields as given.
 // tvm-ffi-stubgen(custom-new): transform.PassContext
+// tvm-ffi-stubgen(field): transform.PassInfo.opt_level -> i32
+// tvm-ffi-stubgen(field): transform.PassContext.opt_level -> i32
 // Names the hand-written wrappers use: the import section keeps them even 
when no generated block
 // does.
 // tvm-ffi-stubgen(import-object): crate::ir::module::IRModule
@@ -106,7 +108,7 @@ impl Pass {
     ///
     /// An instrument's `should_run` hook may skip the pass, unless the 
context requires it.
     pub fn run(&self, module: impl IntoObjectRef<IRModule>) -> 
Result<IRModule> {
-        let module = tvm_ffi::RValueRef::new(module.into_owned_object_ref());
+        let module = 
tvm_ffi::rvalue_ref::RValueRef::new(module.into_owned_object_ref());
         tvm_ffi::cached_global_func!("transform.RunPass")
             .call_packed(&[tvm_ffi::AnyView::from(self), 
tvm_ffi::AnyView::from(&module)])?
             .try_into()
@@ -131,6 +133,7 @@ pub struct PassInfo {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "transform.PassInfo"]
 #[type_final]
+#[type_immutable]
 pub struct PassInfoObj {
     base_: Object,
     pub opt_level: i32,
@@ -158,8 +161,6 @@ impl PassInfo {
         Self { data_: ObjectArc::new(obj) }
     }
 }
-
-unsafe impl tvm_ffi::object::ImmutableObject for PassInfoObj {}
 // tvm-ffi-stubgen(end)
 
 // `PassInfo::new` above has the semantics of the native `transform.PassInfo`, 
which only stores
@@ -182,6 +183,7 @@ pub struct ModulePass {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "transform.ModulePass"]
 #[type_final]
+#[type_immutable]
 pub struct ModulePassObj {
     base_: PassObj,
 }
@@ -200,9 +202,7 @@ impl ModulePassObj {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for ModulePassObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(ModulePass => Pass);
+tvm_ffi::impl_object_ref_upcast!(ModulePass => Pass);
 // tvm-ffi-stubgen(end)
 
 impl ModulePass {
@@ -215,7 +215,7 @@ impl ModulePass {
         pass_info: impl IntoObjectRef<PassInfo>,
     ) -> Self {
         let pass_func = tvm_ffi::Function::from_typed(
-            move |module: tvm_ffi::RValueRef<IRModule>, ctx: PassContext| {
+            move |module: tvm_ffi::rvalue_ref::RValueRef<IRModule>, ctx: 
PassContext| {
                 pass_func(module.into_inner(), ctx)
             },
         );
@@ -245,6 +245,7 @@ pub struct Sequential {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "transform.Sequential"]
 #[type_final]
+#[type_immutable]
 pub struct SequentialObj {
     base_: PassObj,
 }
@@ -267,9 +268,7 @@ impl SequentialObj {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for SequentialObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(Sequential => Pass);
+tvm_ffi::impl_object_ref_upcast!(Sequential => Pass);
 // tvm-ffi-stubgen(end)
 
 impl Sequential {
@@ -495,6 +494,7 @@ pub struct PassInstrument {
 #[repr(C)]
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "transform.PassInstrument"]
+#[type_immutable]
 pub struct PassInstrumentObj {
     base_: Object,
 }
@@ -505,8 +505,6 @@ impl PassInstrumentObj {
         FieldGetter::new(Self::type_index(), "name")?.get(self)
     }
 }
-
-unsafe impl tvm_ffi::object::ImmutableObject for PassInstrumentObj {}
 // tvm-ffi-stubgen(end)
 
 /// Create a pass that logs `header` and the module, which it returns unchanged
diff --git a/rust/src/ir/transform/instrument.rs 
b/rust/src/ir/transform/instrument.rs
index 3ab4ad5c3..27c21c41e 100644
--- a/rust/src/ir/transform/instrument.rs
+++ b/rust/src/ir/transform/instrument.rs
@@ -99,7 +99,7 @@ impl Deref for BasePassInstrumentObj {
     }
 }
 
-tvm_ffi::unsafe_impl_object_ref_upcast!(BasePassInstrument => PassInstrument);
+tvm_ffi::impl_object_ref_upcast!(BasePassInstrument => PassInstrument);
 // tvm-ffi-stubgen(end)
 
 /// A hook of a [`BasePassInstrument`], called when a pass context is entered 
or exited.
diff --git a/rust/src/ir/type.rs b/rust/src/ir/type.rs
index 1b4f1f124..08901c3ff 100644
--- a/rust/src/ir/type.rs
+++ b/rust/src/ir/type.rs
@@ -53,7 +53,6 @@ use crate::ir::location::UNKNOWN_LOC;
 use crate::ir::support;
 use crate::runtime::MemoryScope;
 use std::ops::Deref;
-use std::ptr::addr_of_mut;
 use tvm_ffi::Array;
 use tvm_ffi::DLDataType;
 use tvm_ffi::DLDataTypeCode;
@@ -64,9 +63,6 @@ use tvm_ffi::ObjectArc;
 use tvm_ffi::ObjectRefCore;
 use tvm_ffi::Result;
 use tvm_ffi::VALUE_ERROR;
-use tvm_ffi::reflection::ShallowCopy;
-use tvm_ffi::reflection::UnsafeInit;
-use tvm_ffi::reflection::drop_if_defined;
 // tvm-ffi-stubgen(end)
 
 /// The base of every type; `loc` is the source location the type was written 
at.
@@ -85,6 +81,8 @@ pub struct Type {
 #[repr(C)]
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.Type"]
+#[type_immutable]
+#[type_rs_parent]
 pub struct TypeObj {
     base_: Object,
     pub loc: Location,
@@ -111,23 +109,6 @@ impl Type {
         Self { data_: ObjectArc::new(obj) }
     }
 }
-
-impl ShallowCopy for TypeObj {
-    fn shallow_copy(&self) -> Self {
-        Self { base_: ShallowCopy::shallow_copy(&self.base_), loc: 
Clone::clone(&self.loc) }
-    }
-}
-
-unsafe impl UnsafeInit for TypeObj {
-    unsafe fn drop_defined_fields(this: *mut Self) {
-        unsafe {
-            UnsafeInit::drop_defined_fields(addr_of_mut!((*this).base_));
-            drop_if_defined(addr_of_mut!((*this).loc));
-        }
-    }
-}
-
-unsafe impl tvm_ffi::object::ImmutableObject for TypeObj {}
 // tvm-ffi-stubgen(end)
 
 /// The type of a node whose type has not been given or inferred yet.
@@ -150,6 +131,7 @@ pub struct MissingType {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.MissingType"]
 #[type_final]
+#[type_immutable]
 pub struct MissingTypeObj {
     base_: TypeObj,
 }
@@ -183,9 +165,7 @@ impl MissingType {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for MissingTypeObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(MissingType => Type);
+tvm_ffi::impl_object_ref_upcast!(MissingType => Type);
 // tvm-ffi-stubgen(end)
 
 impl MissingType {
@@ -220,6 +200,7 @@ pub struct NoneType {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.NoneType"]
 #[type_final]
+#[type_immutable]
 pub struct NoneTypeObj {
     base_: TypeObj,
 }
@@ -253,9 +234,7 @@ impl NoneType {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for NoneTypeObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(NoneType => Type);
+tvm_ffi::impl_object_ref_upcast!(NoneType => Type);
 // tvm-ffi-stubgen(end)
 
 impl NoneType {
@@ -290,6 +269,7 @@ pub struct AnyType {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.AnyType"]
 #[type_final]
+#[type_immutable]
 pub struct AnyTypeObj {
     base_: TypeObj,
 }
@@ -323,9 +303,7 @@ impl AnyType {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for AnyTypeObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(AnyType => Type);
+tvm_ffi::impl_object_ref_upcast!(AnyType => Type);
 // tvm-ffi-stubgen(end)
 
 // `ir.AnyType` stores its location as given: the generated `AnyType::new` is 
the same.
@@ -348,6 +326,7 @@ pub struct TensorRegionType {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.TensorRegionType"]
 #[type_final]
+#[type_immutable]
 pub struct TensorRegionTypeObj {
     base_: TypeObj,
 }
@@ -381,9 +360,7 @@ impl TensorRegionType {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for TensorRegionTypeObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(TensorRegionType => Type);
+tvm_ffi::impl_object_ref_upcast!(TensorRegionType => Type);
 // tvm-ffi-stubgen(end)
 
 impl TensorRegionType {
@@ -419,6 +396,7 @@ pub struct OpaqueType {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.OpaqueType"]
 #[type_final]
+#[type_immutable]
 pub struct OpaqueTypeObj {
     base_: TypeObj,
 }
@@ -452,9 +430,7 @@ impl OpaqueType {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for OpaqueTypeObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(OpaqueType => Type);
+tvm_ffi::impl_object_ref_upcast!(OpaqueType => Type);
 // tvm-ffi-stubgen(end)
 
 impl OpaqueType {
@@ -499,6 +475,7 @@ pub struct PrimType {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.PrimType"]
 #[type_final]
+#[type_immutable]
 pub struct PrimTypeObj {
     base_: TypeObj,
     pub dtype: DLDataType,
@@ -533,9 +510,7 @@ impl PrimType {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for PrimTypeObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(PrimType => Type);
+tvm_ffi::impl_object_ref_upcast!(PrimType => Type);
 // tvm-ffi-stubgen(end)
 
 /// The primitive type of a dtype, checked to be a valid primitive type 
(`ir.PrimType`).
@@ -735,6 +710,7 @@ pub struct StringType {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.StringType"]
 #[type_final]
+#[type_immutable]
 pub struct StringTypeObj {
     base_: TypeObj,
 }
@@ -768,9 +744,7 @@ impl StringType {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for StringTypeObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(StringType => Type);
+tvm_ffi::impl_object_ref_upcast!(StringType => Type);
 // tvm-ffi-stubgen(end)
 
 impl StringType {
@@ -800,6 +774,7 @@ pub struct PtrType {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.PtrType"]
 #[type_final]
+#[type_immutable]
 pub struct PtrTypeObj {
     base_: TypeObj,
     pub element_type: Type,
@@ -843,9 +818,7 @@ impl PtrType {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for PtrTypeObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(PtrType => Type);
+tvm_ffi::impl_object_ref_upcast!(PtrType => Type);
 // tvm-ffi-stubgen(end)
 
 impl PtrType {
@@ -886,6 +859,7 @@ pub struct TupleType {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.TupleType"]
 #[type_final]
+#[type_immutable]
 pub struct TupleTypeObj {
     base_: TypeObj,
     pub fields: Array<Type>,
@@ -920,9 +894,7 @@ impl TupleType {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for TupleTypeObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(TupleType => Type);
+tvm_ffi::impl_object_ref_upcast!(TupleType => Type);
 // tvm-ffi-stubgen(end)
 
 impl TupleType {
@@ -954,6 +926,7 @@ pub struct FuncType {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ir.FuncType"]
 #[type_final]
+#[type_immutable]
 pub struct FuncTypeObj {
     base_: TypeObj,
     pub arg_types: Array<Type>,
@@ -997,9 +970,7 @@ impl FuncType {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for FuncTypeObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(FuncType => Type);
+tvm_ffi::impl_object_ref_upcast!(FuncType => Type);
 // tvm-ffi-stubgen(end)
 
 impl FuncType {
diff --git a/rust/src/macro/def_expr_op_overloads.rs 
b/rust/src/macro/def_expr_op_overloads.rs
index d3ea12b9c..27e705048 100644
--- a/rust/src/macro/def_expr_op_overloads.rs
+++ b/rust/src/macro/def_expr_op_overloads.rs
@@ -54,7 +54,7 @@
 /// }
 ///
 /// // Every thread index is an `int32`, as `new` makes it.
-/// tvm_ffi::unsafe_impl_object_ref_upcast!(ThreadIndex => PrimExpr);
+/// tvm_ffi::impl_object_ref_upcast!(unsafe ThreadIndex => PrimExpr);
 /// tvm_tirx::def_expr_op_over_loads!(ThreadIndex);
 ///
 /// # fn main() -> tvm_tirx::tvm_ffi::Result<()> {
diff --git a/rust/src/runtime.rs b/rust/src/runtime.rs
index 56898ff42..760394438 100644
--- a/rust/src/runtime.rs
+++ b/rust/src/runtime.rs
@@ -39,13 +39,13 @@ use std::borrow::Borrow;
 use tvm_ffi::Any;
 use tvm_ffi::DLDataType;
 use tvm_ffi::DLDevice;
-use tvm_ffi::EnumObj;
 use tvm_ffi::IntoObjectRef;
 use tvm_ffi::ObjectArc;
 use tvm_ffi::Result;
 use tvm_ffi::Shape;
 use tvm_ffi::String;
 use tvm_ffi::Tensor;
+use tvm_ffi::enum_ref::EnumObj;
 // tvm-ffi-stubgen(end)
 
 // `ir.PtrType.storage_scope` refers to this type.
@@ -70,6 +70,7 @@ pub struct MemoryScope {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "runtime.MemoryScope"]
 #[type_final]
+#[type_immutable]
 pub struct MemoryScopeObj {
     base_: EnumObj,
 }
@@ -79,8 +80,6 @@ const _: () = {
     assert!(::core::mem::align_of::<MemoryScopeObj>() == 8);
 };
 
-unsafe impl tvm_ffi::object::ImmutableObject for MemoryScopeObj {}
-
 impl tvm_ffi::EnumRef for MemoryScope {}
 // tvm-ffi-stubgen(end)
 
diff --git a/rust/src/sym/analyzer.rs b/rust/src/sym/analyzer.rs
index dc6d5c8cb..c7aa9b3a3 100644
--- a/rust/src/sym/analyzer.rs
+++ b/rust/src/sym/analyzer.rs
@@ -424,6 +424,7 @@ pub struct ConstIntBound {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "sym.ConstIntBound"]
 #[type_final]
+#[type_immutable]
 pub struct ConstIntBoundObj {
     base_: Object,
     pub min_value: i64,
@@ -451,8 +452,6 @@ impl ConstIntBound {
         Self { data_: ObjectArc::new(obj) }
     }
 }
-
-unsafe impl tvm_ffi::object::ImmutableObject for ConstIntBoundObj {}
 // tvm-ffi-stubgen(end)
 
 /// The congruence class `{coeff * x + base}` over the integers `x` that an 
integer expression lies
@@ -473,6 +472,7 @@ pub struct ModularSet {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "sym.ModularSet"]
 #[type_final]
+#[type_immutable]
 pub struct ModularSetObj {
     base_: Object,
     pub coeff: i64,
@@ -500,8 +500,6 @@ impl ModularSet {
         Self { data_: ObjectArc::new(obj) }
     }
 }
-
-unsafe impl tvm_ffi::object::ImmutableObject for ModularSetObj {}
 // tvm-ffi-stubgen(end)
 
 /// The counters of an analyzer's rewrite simplifier: the nodes it visited, 
the constraints it
@@ -523,6 +521,7 @@ pub struct RewriteSimplifierStats {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "sym.RewriteSimplifierStats"]
 #[type_final]
+#[type_immutable]
 pub struct RewriteSimplifierStatsObj {
     base_: Object,
     pub nodes_visited: i64,
@@ -583,8 +582,6 @@ impl RewriteSimplifierStats {
         Self { data_: ObjectArc::new(obj) }
     }
 }
-
-unsafe impl tvm_ffi::object::ImmutableObject for RewriteSimplifierStatsObj {}
 // tvm-ffi-stubgen(end)
 
 // 
---------------------------------------------------------------------------------------------
@@ -618,7 +615,7 @@ impl Deref for CanonicalExprObj {
     }
 }
 
-tvm_ffi::unsafe_impl_object_ref_upcast!(CanonicalExpr => Expr);
+tvm_ffi::impl_object_ref_upcast!(CanonicalExpr => Expr);
 // tvm-ffi-stubgen(end)
 
 /// The canonical form of a split index: `((index % upper_factor) / 
lower_factor) * scale`.
@@ -648,7 +645,8 @@ impl Deref for SplitExprObj {
     }
 }
 
-tvm_ffi::unsafe_impl_object_ref_upcast!(SplitExpr => Expr, SplitExpr => 
CanonicalExpr, SplitExpr => PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(SplitExpr => Expr, SplitExpr => 
CanonicalExpr);
+tvm_ffi::impl_object_ref_upcast!(unsafe SplitExpr => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 /// The canonical form of a sum: a sum of [`SplitExpr`]s plus a constant base.
@@ -678,5 +676,6 @@ impl Deref for SumExprObj {
     }
 }
 
-tvm_ffi::unsafe_impl_object_ref_upcast!(SumExpr => Expr, SumExpr => 
CanonicalExpr, SumExpr => PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(SumExpr => Expr, SumExpr => CanonicalExpr);
+tvm_ffi::impl_object_ref_upcast!(unsafe SumExpr => PrimExpr);
 // tvm-ffi-stubgen(end)
diff --git a/rust/src/sym/constraint.rs b/rust/src/sym/constraint.rs
index 12e8ac1e0..dadcc2b02 100644
--- a/rust/src/sym/constraint.rs
+++ b/rust/src/sym/constraint.rs
@@ -41,12 +41,9 @@ use crate::ir::expr::Range;
 use crate::ir::expr::Var;
 use std::borrow::Borrow;
 use std::ops::Deref;
-use std::ptr::addr_of_mut;
 use tvm_ffi::IntoObjectRef;
 use tvm_ffi::Object;
 use tvm_ffi::ObjectArc;
-use tvm_ffi::reflection::ShallowCopy;
-use tvm_ffi::reflection::UnsafeInit;
 // tvm-ffi-stubgen(end)
 
 /// The base of every fact a [`ConstraintContext`] holds; it owns no analyzer 
state.
@@ -65,6 +62,8 @@ pub struct Constraint {
 #[repr(C)]
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "sym.Constraint"]
+#[type_immutable]
+#[type_rs_parent]
 pub struct ConstraintObj {
     base_: Object,
 }
@@ -90,22 +89,6 @@ impl Constraint {
         Self { data_: ObjectArc::new(obj) }
     }
 }
-
-impl ShallowCopy for ConstraintObj {
-    fn shallow_copy(&self) -> Self {
-        Self { base_: ShallowCopy::shallow_copy(&self.base_) }
-    }
-}
-
-unsafe impl UnsafeInit for ConstraintObj {
-    unsafe fn drop_defined_fields(this: *mut Self) {
-        unsafe {
-            UnsafeInit::drop_defined_fields(addr_of_mut!((*this).base_));
-        }
-    }
-}
-
-unsafe impl tvm_ffi::object::ImmutableObject for ConstraintObj {}
 // tvm-ffi-stubgen(end)
 
 /// The binding of `var` to `value`. The value is the bound expression as 
written, memory reads
@@ -126,6 +109,7 @@ pub struct BindingConstraint {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "sym.BindingConstraint"]
 #[type_final]
+#[type_immutable]
 pub struct BindingConstraintObj {
     base_: ConstraintObj,
     pub var: Var,
@@ -162,9 +146,7 @@ impl BindingConstraint {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for BindingConstraintObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(BindingConstraint => Constraint);
+tvm_ffi::impl_object_ref_upcast!(BindingConstraint => Constraint);
 // tvm-ffi-stubgen(end)
 
 /// The fact that the boolean `condition` holds. With `assume_always`, the 
caller guarantees it
@@ -185,6 +167,7 @@ pub struct PredicateConstraint {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "sym.PredicateConstraint"]
 #[type_final]
+#[type_immutable]
 pub struct PredicateConstraintObj {
     base_: ConstraintObj,
     pub condition: PrimExpr,
@@ -220,9 +203,7 @@ impl PredicateConstraint {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for PredicateConstraintObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(PredicateConstraint => Constraint);
+tvm_ffi::impl_object_ref_upcast!(PredicateConstraint => Constraint);
 // tvm-ffi-stubgen(end)
 
 /// The fact that `var` lies in the half-open `range`, which may be wider than 
the values `var`
@@ -243,6 +224,7 @@ pub struct RangeConstraint {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "sym.RangeConstraint"]
 #[type_final]
+#[type_immutable]
 pub struct RangeConstraintObj {
     base_: ConstraintObj,
     pub var: Var,
@@ -279,9 +261,7 @@ impl RangeConstraint {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for RangeConstraintObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(RangeConstraint => Constraint);
+tvm_ffi::impl_object_ref_upcast!(RangeConstraint => Constraint);
 // tvm-ffi-stubgen(end)
 
 /// The object of a [`ConstraintContext`], with the layout of the native one 
and a stable identity.
diff --git a/rust/src/sym/int_set.rs b/rust/src/sym/int_set.rs
index a2ab80050..c9cc3ac8a 100644
--- a/rust/src/sym/int_set.rs
+++ b/rust/src/sym/int_set.rs
@@ -143,6 +143,7 @@ pub struct IntervalSet {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "sym.IntervalSet"]
 #[type_final]
+#[type_immutable]
 pub struct IntervalSetObj {
     base_: IntSetObj,
 }
@@ -165,9 +166,7 @@ impl IntervalSetObj {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for IntervalSetObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(IntervalSet => IntSet);
+tvm_ffi::impl_object_ref_upcast!(IntervalSet => IntSet);
 // tvm-ffi-stubgen(end)
 
 impl IntervalSet {
diff --git a/rust/src/sym/iter_affine_map.rs b/rust/src/sym/iter_affine_map.rs
index f799134b4..496c7e15e 100644
--- a/rust/src/sym/iter_affine_map.rs
+++ b/rust/src/sym/iter_affine_map.rs
@@ -100,7 +100,8 @@ impl Deref for IterMapExprObj {
     }
 }
 
-tvm_ffi::unsafe_impl_object_ref_upcast!(IterMapExpr => Expr, IterMapExpr => 
PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(IterMapExpr => Expr);
+tvm_ffi::impl_object_ref_upcast!(unsafe IterMapExpr => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 /// The result of [`detect_iter_map`]: the detected `indices`, or empty 
`indices` and the `errors`
@@ -204,6 +205,7 @@ pub struct IterMark {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "sym.IterMark"]
 #[type_final]
+#[type_immutable]
 pub struct IterMarkObj {
     base_: Object,
     pub source: PrimExpr,
@@ -231,8 +233,6 @@ impl IterMark {
         Self { data_: ObjectArc::new(obj) }
     }
 }
-
-unsafe impl tvm_ffi::object::ImmutableObject for IterMarkObj {}
 // tvm-ffi-stubgen(end)
 
 /// The split `(source // lower_factor) % extent * scale` of the marked 
iterator `source`.
@@ -252,6 +252,7 @@ pub struct IterSplitExpr {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "sym.IterSplitExpr"]
 #[type_final]
+#[type_immutable]
 pub struct IterSplitExprObj {
     base_: IterMapExprObj,
 }
@@ -282,9 +283,8 @@ impl IterSplitExprObj {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for IterSplitExprObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(IterSplitExpr => Expr, IterSplitExpr 
=> IterMapExpr, IterSplitExpr => PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(IterSplitExpr => Expr, IterSplitExpr => 
IterMapExpr);
+tvm_ffi::impl_object_ref_upcast!(unsafe IterSplitExpr => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 /// The sum of the splits `args` plus `base`.
@@ -304,6 +304,7 @@ pub struct IterSumExpr {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "sym.IterSumExpr"]
 #[type_final]
+#[type_immutable]
 pub struct IterSumExprObj {
     base_: IterMapExprObj,
 }
@@ -326,9 +327,8 @@ impl IterSumExprObj {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for IterSumExprObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(IterSumExpr => Expr, IterSumExpr => 
IterMapExpr, IterSumExpr => PrimExpr);
+tvm_ffi::impl_object_ref_upcast!(IterSumExpr => Expr, IterSumExpr => 
IterMapExpr);
+tvm_ffi::impl_object_ref_upcast!(unsafe IterSumExpr => PrimExpr);
 // tvm-ffi-stubgen(end)
 
 // `IterMark::new` is generated: the native `sym.IterMark` constructor stores 
the fields as given.
diff --git a/rust/src/tirx/function.rs b/rust/src/tirx/function.rs
index 2110c00be..cd5b6efaf 100644
--- a/rust/src/tirx/function.rs
+++ b/rust/src/tirx/function.rs
@@ -96,6 +96,7 @@ pub struct Function {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "tirx.Function"]
 #[type_final]
+#[type_immutable]
 pub struct FunctionObj {
     base_: BaseFuncObj,
     pub params: Array<Var>,
@@ -153,9 +154,7 @@ impl Function {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for FunctionObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(Function => Expr, Function => 
BaseFunc);
+tvm_ffi::impl_object_ref_upcast!(Function => Expr, Function => BaseFunc);
 // tvm-ffi-stubgen(end)
 
 impl Function {
diff --git a/rust/src/tirx/index_map.rs b/rust/src/tirx/index_map.rs
index 1ea2ae6be..504c5773e 100644
--- a/rust/src/tirx/index_map.rs
+++ b/rust/src/tirx/index_map.rs
@@ -97,6 +97,7 @@ pub struct IndexMap {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "tirx.IndexMap"]
 #[type_final]
+#[type_immutable]
 pub struct IndexMapObj {
     base_: Object,
     pub initial_indices: Array<PrimVar>,
@@ -137,8 +138,6 @@ impl IndexMap {
         Self { data_: ObjectArc::new(obj) }
     }
 }
-
-unsafe impl tvm_ffi::object::ImmutableObject for IndexMapObj {}
 // tvm-ffi-stubgen(end)
 
 impl IndexMap {
diff --git a/rust/src/tirx/layout.rs b/rust/src/tirx/layout.rs
index 20613641c..4b9f61a64 100644
--- a/rust/src/tirx/layout.rs
+++ b/rust/src/tirx/layout.rs
@@ -49,7 +49,6 @@ use std::borrow::Borrow;
 use std::ops::Deref;
 use tvm_ffi::Any;
 use tvm_ffi::Array;
-use tvm_ffi::EnumObj;
 use tvm_ffi::IntoObjectRef;
 use tvm_ffi::Map;
 use tvm_ffi::Object;
@@ -57,6 +56,7 @@ use tvm_ffi::ObjectArc;
 use tvm_ffi::ObjectCore;
 use tvm_ffi::Result;
 use tvm_ffi::String;
+use tvm_ffi::enum_ref::EnumObj;
 use tvm_ffi::reflection::FieldGetter;
 // tvm-ffi-stubgen(end)
 
@@ -238,6 +238,7 @@ pub struct TileLayout {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "tirx.TileLayout"]
 #[type_final]
+#[type_immutable]
 pub struct TileLayoutObj {
     base_: LayoutObj,
 }
@@ -264,9 +265,7 @@ impl TileLayoutObj {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for TileLayoutObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(TileLayout => Layout);
+tvm_ffi::impl_object_ref_upcast!(TileLayout => Layout);
 // tvm-ffi-stubgen(end)
 
 impl TileLayout {
@@ -354,6 +353,7 @@ pub struct Iter {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "tirx.Iter"]
 #[type_final]
+#[type_immutable]
 pub struct IterObj {
     base_: Object,
     pub extent: PrimExpr,
@@ -390,8 +390,6 @@ impl Iter {
         Self { data_: ObjectArc::new(obj) }
     }
 }
-
-unsafe impl tvm_ffi::object::ImmutableObject for IterObj {}
 // tvm-ffi-stubgen(end)
 
 // The native `tirx.Iter` stores its fields as given, as the generated 
`Iter::new` does.
@@ -415,6 +413,7 @@ pub struct Axis {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "tirx.Axis"]
 #[type_final]
+#[type_immutable]
 pub struct AxisObj {
     base_: EnumObj,
 }
@@ -424,8 +423,6 @@ const _: () = {
     assert!(::core::mem::align_of::<AxisObj>() == 8);
 };
 
-unsafe impl tvm_ffi::object::ImmutableObject for AxisObj {}
-
 impl tvm_ffi::EnumRef for Axis {}
 // tvm-ffi-stubgen(end)
 
@@ -486,6 +483,7 @@ pub struct ComposeLayout {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "tirx.ComposeLayout"]
 #[type_final]
+#[type_immutable]
 pub struct ComposeLayoutObj {
     base_: LayoutObj,
 }
@@ -528,9 +526,7 @@ impl ComposeLayoutObj {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for ComposeLayoutObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(ComposeLayout => Layout);
+tvm_ffi::impl_object_ref_upcast!(ComposeLayout => Layout);
 // tvm-ffi-stubgen(end)
 
 impl ComposeLayout {
diff --git a/rust/src/tirx/op.rs b/rust/src/tirx/op.rs
index 7fc4dc334..f5924411f 100644
--- a/rust/src/tirx/op.rs
+++ b/rust/src/tirx/op.rs
@@ -108,6 +108,7 @@ pub struct CallFFIKernelAttr {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "tirx.CallFFIKernelAttr"]
 #[type_final]
+#[type_immutable]
 pub struct CallFFIKernelAttrObj {
     base_: AttrsObj,
     pub launch_params: Array<String>,
@@ -160,9 +161,7 @@ impl CallFFIKernelAttr {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for CallFFIKernelAttrObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(CallFFIKernelAttr => Attrs);
+tvm_ffi::impl_object_ref_upcast!(CallFFIKernelAttr => Attrs);
 // tvm-ffi-stubgen(end)
 
 // The native `tirx.CallFFIKernelAttr` stores its fields as given, as the 
generated
diff --git a/rust/src/tirx/structural.rs b/rust/src/tirx/structural.rs
index b452bc262..4aeeb8a57 100644
--- a/rust/src/tirx/structural.rs
+++ b/rust/src/tirx/structural.rs
@@ -32,20 +32,22 @@ use tvm_ffi::Result;
 use tvm_ffi::reflection::FieldGetter;
 
 /// Header-only stand-in for the builtin `ffi.StructuralMutator`; it only 
carries the ancestor depth.
+#[doc(hidden)]
 #[allow(dead_code)]
 #[repr(C)]
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ffi.StructuralMutator"]
-struct FfiStructuralMutatorObj {
+pub struct FfiStructuralMutatorObj {
     base_: Object,
 }
 
 /// Header-only stand-in for the builtin `ffi.StructuralVisitor`; it only 
carries the ancestor depth.
+#[doc(hidden)]
 #[allow(dead_code)]
 #[repr(C)]
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "ffi.StructuralVisitor"]
-struct FfiStructuralVisitorObj {
+pub struct FfiStructuralVisitorObj {
     base_: Object,
 }
 // tvm-ffi-stubgen(end)
diff --git a/rust/src/tirx/structural_map_with_constraint_context.rs 
b/rust/src/tirx/structural_map_with_constraint_context.rs
index 26b6872b6..478692085 100644
--- a/rust/src/tirx/structural_map_with_constraint_context.rs
+++ b/rust/src/tirx/structural_map_with_constraint_context.rs
@@ -23,9 +23,9 @@
 
 use std::borrow::BorrowMut;
 
+use tvm_ffi::extra::structural_visit::DefRegionKind;
 use tvm_ffi::{
-    Any, AnyView, DefRegionKind, Dispatcher, IntoDispatcher, MutationView, 
Object, Result,
-    UnchangedOr, WalkOrder,
+    Any, AnyView, Dispatcher, IntoDispatcher, MutationView, Object, Result, 
UnchangedOr, WalkOrder,
 };
 
 use super::structural_mutate_with_constraint_context::{
diff --git a/rust/src/tirx/structural_mutate_with_constraint_context.rs 
b/rust/src/tirx/structural_mutate_with_constraint_context.rs
index b0447359b..531e3be33 100644
--- a/rust/src/tirx/structural_mutate_with_constraint_context.rs
+++ b/rust/src/tirx/structural_mutate_with_constraint_context.rs
@@ -25,13 +25,16 @@ use std::borrow::BorrowMut;
 use std::cell::UnsafeCell;
 
 use tvm_ffi::extra::cold_init_lazy_lock::ColdInitLazyLock;
-use tvm_ffi::extra::structural_mutate::{StructuralMutateEngine, 
StructuralMutatorBase};
+use tvm_ffi::extra::structural_mutate::{
+    InplaceMode, StructuralMutateEngine, StructuralMutatorBase, 
StructuralMutatorObj,
+    StructuralMutatorView,
+};
 use tvm_ffi::reflection::TypeAttrColumn;
 use tvm_ffi::object::ObjectRef;
 use tvm_ffi::tvm_ffi_sys::{TVMFFIAny, TVMFFITypeIndex};
 use tvm_ffi::{
-    Any, Dispatcher, InplaceMode, IntoDispatcher, MutationView, ObjectArc, 
ObjectCore,
-    ObjectRefCore, Result, StructuralMutatorObj, StructuralMutatorView, 
UnchangedOr,
+    Any, Dispatcher, IntoDispatcher, MutationView, ObjectArc, ObjectCore, 
ObjectRefCore, Result,
+    UnchangedOr,
 };
 
 use super::StructuralMutatorWithConstraintContextObj;
@@ -84,10 +87,11 @@ unsafe impl StructuralMutatorBase for 
StructuralMutatorWithConstraintContextBase
 
     #[inline(always)]
     unsafe fn default_mutate_raw(runtime: *mut Self, value: MutationView<'_>) 
-> TVMFFIAny {
-        static COPYING: ColdInitLazyLock<Option<TypeAttrColumn>> =
-            ColdInitLazyLock::new(|| 
TypeAttrColumn::new("__s_mutate_with_constraint_context__"));
+        static COPYING: ColdInitLazyLock<Option<TypeAttrColumn>> = 
ColdInitLazyLock::new(|| {
+            TypeAttrColumn::lookup("__s_mutate_with_constraint_context__")
+        });
         static INPLACE: ColdInitLazyLock<Option<TypeAttrColumn>> = 
ColdInitLazyLock::new(|| {
-            
TypeAttrColumn::new("__s_maybe_inplace_mutate_with_constraint_context__")
+            
TypeAttrColumn::lookup("__s_maybe_inplace_mutate_with_constraint_context__")
         });
         let borrowed = value.as_any_view();
         let lookup = |column: Option<TypeAttrColumn>| {
@@ -101,7 +105,7 @@ unsafe impl StructuralMutatorBase for 
StructuralMutatorWithConstraintContextBase
                 // alias. Borrow in place so the count is the slot's own 
reference.
                 borrowed
                     .downcast_ref::<ObjectRef>()
-                    .is_some_and(|owner| 
ObjectArc::strong_count(ObjectRef::data(owner)) == 1)
+                    .is_some_and(|owner| 
ObjectArc::is_unique(ObjectRef::data(owner)))
             })
         } else {
             None
diff --git a/rust/src/tirx/structural_visit_with_constraint_context.rs 
b/rust/src/tirx/structural_visit_with_constraint_context.rs
index 634247087..80a8daca6 100644
--- a/rust/src/tirx/structural_visit_with_constraint_context.rs
+++ b/rust/src/tirx/structural_visit_with_constraint_context.rs
@@ -25,13 +25,12 @@ use std::borrow::BorrowMut;
 use std::cell::UnsafeCell;
 
 use tvm_ffi::extra::cold_init_lazy_lock::ColdInitLazyLock;
-use tvm_ffi::extra::structural_visit::StructuralVisitorBase;
+use tvm_ffi::extra::structural_visit::{
+    StructuralVisitEngine, StructuralVisitorBase, StructuralVisitorObj, 
StructuralVisitorView,
+};
 use tvm_ffi::reflection::TypeAttrColumn;
 use tvm_ffi::tvm_ffi_sys::{TVMFFIAny, TVMFFITypeIndex};
-use tvm_ffi::{
-    AnyView, Dispatcher, IntoDispatcher, ObjectCore, Result, 
StructuralVisitEngine,
-    StructuralVisitorObj, StructuralVisitorView, VisitInterrupt,
-};
+use tvm_ffi::{AnyView, Dispatcher, IntoDispatcher, ObjectCore, Result, 
VisitInterrupt};
 
 use super::StructuralVisitorWithConstraintContextObj;
 use crate::sym::ConstraintContext;
@@ -87,7 +86,7 @@ unsafe impl StructuralVisitorBase for 
StructuralVisitorWithConstraintContextBase
     #[inline(always)]
     unsafe fn default_visit_raw(runtime: *mut Self, value: AnyView<'_>) -> 
TVMFFIAny {
         static HOOKS: ColdInitLazyLock<Option<TypeAttrColumn>> =
-            ColdInitLazyLock::new(|| 
TypeAttrColumn::new("__s_visit_with_constraint_context__"));
+            ColdInitLazyLock::new(|| 
TypeAttrColumn::lookup("__s_visit_with_constraint_context__"));
         // Copy the borrowed token before reentry. Packed dispatch acquires an
         // owning Function before invoking a callback that could register 
hooks.
         let hook = HOOKS
diff --git a/rust/src/tirx/structural_walk_with_constraint_context.rs 
b/rust/src/tirx/structural_walk_with_constraint_context.rs
index f69d07d07..c998cbb9c 100644
--- a/rust/src/tirx/structural_walk_with_constraint_context.rs
+++ b/rust/src/tirx/structural_walk_with_constraint_context.rs
@@ -21,10 +21,8 @@
 //! on every value with the facts in scope there, to observe the value, not to 
rewrite it.
 
 use std::borrow::BorrowMut;
-use tvm_ffi::{
-    Any, AnyView, DefRegionKind, Dispatcher, IntoDispatcher, Result, 
VisitInterrupt, WalkOrder,
-    WalkResult,
-};
+use tvm_ffi::extra::structural_visit::DefRegionKind;
+use tvm_ffi::{Any, AnyView, Dispatcher, IntoDispatcher, Result, 
VisitInterrupt, WalkOrder, WalkResult};
 
 use crate::sym::ConstraintContext;
 use super::structural_visit_with_constraint_context::{
diff --git a/rust/src/tirx/tensor.rs b/rust/src/tirx/tensor.rs
index 608391249..fedcaa210 100644
--- a/rust/src/tirx/tensor.rs
+++ b/rust/src/tirx/tensor.rs
@@ -37,6 +37,7 @@
 // tvm-ffi-stubgen(import-object): crate::ir::expr::PrimExpr
 // A tensor's shape is a list of `PrimExpr`s, as C++ `TensorTypeObj` holds it.
 // tvm-ffi-stubgen(field): tirx.TensorType.shape -> Array<PrimExpr>
+// tvm-ffi-stubgen(field): tirx.TensorType.data_alignment -> i32
 // tvm-ffi-stubgen(import-object): tvm_ffi::Array
 // tvm-ffi-stubgen(import-object): tvm_ffi::IntoObjectRef
 // tvm-ffi-stubgen(begin): import-section
@@ -102,6 +103,7 @@ pub struct TensorType {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "tirx.TensorType"]
 #[type_final]
+#[type_immutable]
 pub struct TensorTypeObj {
     base_: TypeObj,
     pub dtype: PrimType,
@@ -161,9 +163,7 @@ impl TensorType {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for TensorTypeObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(TensorType => Type);
+tvm_ffi::impl_object_ref_upcast!(TensorType => Type);
 // tvm-ffi-stubgen(end)
 
 impl TensorType {
diff --git a/rust/src/tirx/thread_scope.rs b/rust/src/tirx/thread_scope.rs
index ea69dd257..d4f56fab5 100644
--- a/rust/src/tirx/thread_scope.rs
+++ b/rust/src/tirx/thread_scope.rs
@@ -30,11 +30,11 @@
 
 // tvm-ffi-stubgen(begin): import-section
 use std::borrow::Borrow;
-use tvm_ffi::EnumObj;
 use tvm_ffi::IntoObjectRef;
 use tvm_ffi::ObjectArc;
 use tvm_ffi::Result;
 use tvm_ffi::String;
+use tvm_ffi::enum_ref::EnumObj;
 // tvm-ffi-stubgen(end)
 
 /// A level of a backend's thread hierarchy, named `backend.level`, such as 
`cuda.warp`.
@@ -73,6 +73,7 @@ pub struct ThreadScope {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "tirx.ThreadScope"]
 #[type_final]
+#[type_immutable]
 pub struct ThreadScopeObj {
     base_: EnumObj,
 }
@@ -82,8 +83,6 @@ const _: () = {
     assert!(::core::mem::align_of::<ThreadScopeObj>() == 8);
 };
 
-unsafe impl tvm_ffi::object::ImmutableObject for ThreadScopeObj {}
-
 impl tvm_ffi::EnumRef for ThreadScope {}
 // tvm-ffi-stubgen(end)
 
diff --git a/rust/src/tirx/tile/dispatch.rs b/rust/src/tirx/tile/dispatch.rs
index 1b333d9d2..af079ff18 100644
--- a/rust/src/tirx/tile/dispatch.rs
+++ b/rust/src/tirx/tile/dispatch.rs
@@ -110,6 +110,7 @@ pub struct DispatchContext {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "tirx.DispatchContext"]
 #[type_final]
+#[type_immutable]
 pub struct DispatchContextObj {
     base_: Object,
 }
@@ -156,8 +157,6 @@ impl DispatchContextObj {
         FieldGetter::new(Self::type_index(), "scope_kind")?.get(self)
     }
 }
-
-unsafe impl tvm_ffi::object::ImmutableObject for DispatchContextObj {}
 // tvm-ffi-stubgen(end)
 
 impl DispatchContext {
diff --git a/rust/src/tirx/tile/instruction.rs 
b/rust/src/tirx/tile/instruction.rs
index 9fa4fccab..a217de9d7 100644
--- a/rust/src/tirx/tile/instruction.rs
+++ b/rust/src/tirx/tile/instruction.rs
@@ -72,6 +72,7 @@ pub struct ScopeAttrs {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "tirx.tensor.ScopeAttrs"]
 #[type_final]
+#[type_immutable]
 pub struct ScopeAttrsObj {
     base_: AttrsObj,
     pub scope: ThreadScope,
@@ -106,9 +107,7 @@ impl ScopeAttrs {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for ScopeAttrsObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(ScopeAttrs => Attrs);
+tvm_ffi::impl_object_ref_upcast!(ScopeAttrs => Attrs);
 // tvm-ffi-stubgen(end)
 
 /// The attributes of a Trainium (`trn`) tile instruction call: the thread 
`scope` it runs at, an
@@ -130,6 +129,7 @@ pub struct TrnAttrs {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "tirx.tensor.TrnAttrs"]
 #[type_final]
+#[type_immutable]
 pub struct TrnAttrsObj {
     base_: AttrsObj,
     pub scope: ThreadScope,
@@ -202,9 +202,7 @@ impl TrnAttrs {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for TrnAttrsObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(TrnAttrs => Attrs);
+tvm_ffi::impl_object_ref_upcast!(TrnAttrs => Attrs);
 // tvm-ffi-stubgen(end)
 
 /// Return the tensor region `value` covers: a `TensorLoad` converted, a 
[`TensorRegion`] as is
diff --git a/rust/src/tirx/transform/function_pass.rs 
b/rust/src/tirx/transform/function_pass.rs
index 0946ebbdf..bef18cdec 100644
--- a/rust/src/tirx/transform/function_pass.rs
+++ b/rust/src/tirx/transform/function_pass.rs
@@ -26,7 +26,7 @@
 // tvm-ffi-stubgen(import-object): crate::ir::module::IRModule
 // tvm-ffi-stubgen(import-object): crate::ir::transform::core::PassContext
 // tvm-ffi-stubgen(import-object): crate::tirx::function::Function
-// tvm-ffi-stubgen(import-object): tvm_ffi::RValueRef
+// tvm-ffi-stubgen(import-object): tvm_ffi::rvalue_ref::RValueRef
 // tvm-ffi-stubgen(import-object): crate::ir::support
 // tvm-ffi-stubgen(import-object): tvm_ffi::IntoObjectRef
 // tvm-ffi-stubgen(import-object): std::borrow::Borrow
@@ -44,9 +44,9 @@ use std::ops::Deref;
 use tvm_ffi::IntoObjectRef;
 use tvm_ffi::ObjectArc;
 use tvm_ffi::ObjectCore;
-use tvm_ffi::RValueRef;
 use tvm_ffi::Result;
 use tvm_ffi::reflection::FieldGetter;
+use tvm_ffi::rvalue_ref::RValueRef;
 // tvm-ffi-stubgen(end)
 
 /// A pass that replaces each TIRx function of a module by what a function 
returns for it, or
@@ -92,6 +92,7 @@ pub struct FunctionPass {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "tirx.FunctionPass"]
 #[type_final]
+#[type_immutable]
 pub struct FunctionPassObj {
     base_: PassObj,
 }
@@ -110,9 +111,7 @@ impl FunctionPassObj {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for FunctionPassObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(FunctionPass => Pass);
+tvm_ffi::impl_object_ref_upcast!(FunctionPass => Pass);
 // tvm-ffi-stubgen(end)
 
 impl FunctionPass {
diff --git a/rust/src/tirx/transform/transform.rs 
b/rust/src/tirx/transform/transform.rs
index 02ffc92ce..d2835a978 100644
--- a/rust/src/tirx/transform/transform.rs
+++ b/rust/src/tirx/transform/transform.rs
@@ -22,6 +22,11 @@
 //! [`remove_no_op`] and [`unroll_loop`].
 
 // tvm-ffi-stubgen(missing-docs): allow
+// tvm-ffi-stubgen(field): tirx.transform.UnrollLoopConfig.auto_max_step -> i32
+// tvm-ffi-stubgen(field): tirx.transform.UnrollLoopConfig.auto_max_depth -> 
i32
+// tvm-ffi-stubgen(field): tirx.transform.UnrollLoopConfig.auto_max_extent -> 
i32
+// tvm-ffi-stubgen(field): tirx.transform.UnrollLoopConfig.explicit_unroll -> 
i32
+// tvm-ffi-stubgen(field): tirx.transform.UnrollLoopConfig.unroll_local_access 
-> i32
 // Names the hand-written wrappers use: the import section keeps them even 
when no generated block
 // does.
 // tvm-ffi-stubgen(import-object): crate::ir::transform::core::Pass
@@ -64,6 +69,7 @@ pub struct RemoveNoOpConfig {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "tirx.transform.RemoveNoOpConfig"]
 #[type_final]
+#[type_immutable]
 pub struct RemoveNoOpConfigObj {
     base_: Object,
     pub max_simplification_steps: i64,
@@ -91,8 +97,6 @@ impl RemoveNoOpConfig {
         Self { data_: ObjectArc::new(obj) }
     }
 }
-
-unsafe impl tvm_ffi::object::ImmutableObject for RemoveNoOpConfigObj {}
 // tvm-ffi-stubgen(end)
 
 /// The configuration of [`unroll_loop`], the pass context option 
`tirx.UnrollLoop`.
@@ -117,6 +121,7 @@ pub struct UnrollLoopConfig {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "tirx.transform.UnrollLoopConfig"]
 #[type_final]
+#[type_immutable]
 pub struct UnrollLoopConfigObj {
     base_: Object,
     pub auto_max_step: i32,
@@ -172,8 +177,6 @@ impl UnrollLoopConfig {
         Self { data_: ObjectArc::new(obj) }
     }
 }
-
-unsafe impl tvm_ffi::object::ImmutableObject for UnrollLoopConfigObj {}
 // tvm-ffi-stubgen(end)
 
 /// Infer and mark an unambiguous TIRx entry function 
(`tirx.transform.AnnotateEntryFunc`).
diff --git a/rust/src/tirx/type.rs b/rust/src/tirx/type.rs
index 60e3c8f33..3a1cac4e5 100644
--- a/rust/src/tirx/type.rs
+++ b/rust/src/tirx/type.rs
@@ -51,6 +51,7 @@ pub struct TensorMapType {
 #[derive(tvm_ffi::derive::Object)]
 #[type_key = "tirx.TensorMapType"]
 #[type_final]
+#[type_immutable]
 pub struct TensorMapTypeObj {
     base_: TypeObj,
 }
@@ -84,9 +85,7 @@ impl TensorMapType {
     }
 }
 
-unsafe impl tvm_ffi::object::ImmutableObject for TensorMapTypeObj {}
-
-tvm_ffi::unsafe_impl_object_ref_upcast!(TensorMapType => Type);
+tvm_ffi::impl_object_ref_upcast!(TensorMapType => Type);
 // tvm-ffi-stubgen(end)
 
 // The native `tirx.TensorMapType` stores the location as given, as the 
generated
diff --git a/rust/tests/ir/op_def.rs b/rust/tests/ir/op_def.rs
index 0a65e206a..307f239ab 100644
--- a/rust/tests/ir/op_def.rs
+++ b/rust/tests/ir/op_def.rs
@@ -610,7 +610,7 @@ fn static_init_blocks_define_operators_and_types() {
 
     // The type `register_type` registers with them: tvm-ffi constructs it 
from its fields,
     // positionally or by keyword, and reads it back from a JSON graph.
-    let init: Function = 
tvm_ffi::reflection::TypeAttrColumn::new("__ffi_init__")
+    let init: Function = 
tvm_ffi::reflection::TypeAttrColumn::lookup("__ffi_init__")
         .and_then(|column| column.get(BlockTypeObj::type_index()))
         .unwrap()
         .try_into()
diff --git a/rust/tests/runtime/main.rs b/rust/tests/runtime/main.rs
index 57acb6f3c..ad67a029b 100644
--- a/rust/tests/runtime/main.rs
+++ b/rust/tests/runtime/main.rs
@@ -26,7 +26,7 @@ mod common;
 use tvm_tirx::runtime::{self, MemoryScope};
 use tvm_tirx::ir::dtype;
 use tvm_tirx::tvm_ffi::reflection::EnumDef;
-use tvm_tirx::tvm_ffi::{DLDevice, DLDeviceType, TypeIndex};
+use tvm_tirx::tvm_ffi::{DLDevice, DLDeviceType};
 
 #[test]
 fn tensors() {
@@ -68,7 +68,7 @@ fn device_attrs() {
     let exist = runtime::get_device_attr(cpu, "exist").unwrap();
     assert!(bool::try_from(exist).unwrap());
     let missing = runtime::get_device_attr(cpu, "no_such_attr").unwrap();
-    assert_eq!(missing.type_index(), TypeIndex::kTVMFFINone as i32);
+    assert!(missing.is_none());
 }
 
 #[test]
diff --git a/rust/tests/sym/iter_affine_map.rs 
b/rust/tests/sym/iter_affine_map.rs
index 681ce1e80..a89781950 100644
--- a/rust/tests/sym/iter_affine_map.rs
+++ b/rust/tests/sym/iter_affine_map.rs
@@ -21,7 +21,8 @@
 
 use tvm_tirx::ir::{ExprObj, IntImm, PrimExpr, PrimVar, Range, Var};
 use tvm_tirx::sym::*;
-use tvm_tirx::tvm_ffi::{Array, Map, Object, ObjectIdentity, ObjectRefCore};
+use tvm_tirx::tvm_ffi::object::ObjectIdentity;
+use tvm_tirx::tvm_ffi::{Array, Map, Object, ObjectRefCore};
 
 use super::{fixture, int32, op, range, var, Fixture};
 use crate::common::{assert_type_contracts, global};
diff --git a/rust/tests/target/main.rs b/rust/tests/target/main.rs
index 28d9dccfe..54ef018d6 100644
--- a/rust/tests/target/main.rs
+++ b/rust/tests/target/main.rs
@@ -24,7 +24,7 @@
 mod common;
 
 use tvm_tirx::target::{self, Target, TargetKind, TargetKindObj, TargetObj};
-use tvm_tirx::tvm_ffi::{Any, Map, Object, ObjectRefCore, String as FfiString, 
TypeIndex};
+use tvm_tirx::tvm_ffi::{Any, Map, Object, ObjectRefCore, String as FfiString};
 
 use common::{assert_type_contracts, empty_module, global, load_llvm_backend};
 
@@ -82,10 +82,7 @@ fn construction_and_queries() {
     assert_eq!(llvm.get_target_device_type().unwrap(), 1);
     
assert!(llvm.export().unwrap().contains_key(&FfiString::from("kind")).unwrap());
     assert!(llvm.as_json().unwrap().as_str().contains("llvm"));
-    assert_eq!(
-        llvm.feature("no_such_feature").unwrap().type_index(),
-        TypeIndex::kTVMFFINone as i32
-    );
+    assert!(llvm.feature("no_such_feature").unwrap().is_none());
 
     let c = Target::from_config(config("c")).unwrap();
     assert_eq!(c.kind().unwrap().name().unwrap().as_str(), "c");
diff --git a/rust/tests/tirx/structural_map_with_constraint_context.rs 
b/rust/tests/tirx/structural_map_with_constraint_context.rs
index c50b48835..bb5fb867a 100644
--- a/rust/tests/tirx/structural_map_with_constraint_context.rs
+++ b/rust/tests/tirx/structural_map_with_constraint_context.rs
@@ -24,8 +24,9 @@ use tvm_tirx::sym::{
     BindingConstraintObj, ConstraintContext, PredicateConstraintObj, 
RangeConstraintObj,
 };
 use 
tvm_tirx::tirx::structural_map_with_constraint_context::structural_map_with_constraint_context
 as map;
+use tvm_tirx::tvm_ffi::extra::structural_visit::DefRegionKind;
 use tvm_tirx::tvm_ffi::{
-    Any, AnyView, DefRegionKind, Error, ObjectRefCore, Result, Unchanged, 
WalkOrder, RUNTIME_ERROR,
+    Any, AnyView, Error, ObjectRefCore, Result, Unchanged, WalkOrder, 
RUNTIME_ERROR,
 };
 
 use super::{int32, op};
diff --git a/rust/tests/tirx/structural_mutate_with_constraint_context.rs 
b/rust/tests/tirx/structural_mutate_with_constraint_context.rs
index 86ded3449..be99365bf 100644
--- a/rust/tests/tirx/structural_mutate_with_constraint_context.rs
+++ b/rust/tests/tirx/structural_mutate_with_constraint_context.rs
@@ -25,9 +25,8 @@ use 
tvm_tirx::tirx::structural_mutate_with_constraint_context::{
     structural_mutate_with_constraint_context as mutate,
     StructuralMutatorWithConstraintContextView as View,
 };
-use tvm_tirx::tvm_ffi::{
-    dispatch, Any, DefRegionKind, MutationView, ObjectRefCore, Result, 
Unchanged, UnchangedOr,
-};
+use tvm_tirx::tvm_ffi::extra::structural_visit::DefRegionKind;
+use tvm_tirx::tvm_ffi::{dispatch, Any, MutationView, ObjectRefCore, Result, 
Unchanged, UnchangedOr};
 
 use super::{int32, op};
 use crate::common::serial;
diff --git a/rust/tests/tirx/structural_visit_with_constraint_context.rs 
b/rust/tests/tirx/structural_visit_with_constraint_context.rs
index 1d7276712..92fc9a9c1 100644
--- a/rust/tests/tirx/structural_visit_with_constraint_context.rs
+++ b/rust/tests/tirx/structural_visit_with_constraint_context.rs
@@ -24,9 +24,9 @@ use tvm_tirx::sym::{Constraint, ConstraintContext, 
PredicateConstraintObj, Range
 use tvm_tirx::tirx::structural_visit_with_constraint_context::{
     structural_visit_with_constraint_context as visit, 
StructuralVisitorWithConstraintContextView,
 };
+use tvm_tirx::tvm_ffi::extra::structural_visit::DefRegionKind;
 use tvm_tirx::tvm_ffi::{
-    dispatch, AnyView, Array, DefRegionKind, Error, Map, ObjectRefCore, 
Result, VisitInterrupt,
-    RUNTIME_ERROR,
+    dispatch, AnyView, Array, Error, Map, ObjectRefCore, Result, 
VisitInterrupt, RUNTIME_ERROR,
 };
 
 use super::int32;
diff --git a/rust/tests/tirx/structural_walk_with_constraint_context.rs 
b/rust/tests/tirx/structural_walk_with_constraint_context.rs
index 2cb1c34f8..7be710df6 100644
--- a/rust/tests/tirx/structural_walk_with_constraint_context.rs
+++ b/rust/tests/tirx/structural_walk_with_constraint_context.rs
@@ -24,9 +24,8 @@ use tvm_tirx::sym::{
     Analyzer, BindingConstraintObj, ConstraintContext, PredicateConstraintObj, 
RangeConstraintObj,
 };
 use 
tvm_tirx::tirx::structural_walk_with_constraint_context::structural_walk_with_constraint_context
 as walk;
-use tvm_tirx::tvm_ffi::{
-    AnyView, DefRegionKind, Error, ObjectRefCore, Result, WalkOrder, 
WalkResult, RUNTIME_ERROR,
-};
+use tvm_tirx::tvm_ffi::extra::structural_visit::DefRegionKind;
+use tvm_tirx::tvm_ffi::{AnyView, Error, ObjectRefCore, Result, WalkOrder, 
WalkResult, RUNTIME_ERROR};
 use super::int32;
 use crate::common::serial;
 

Reply via email to